47 个版本 (26 个重大更新)

0.26.0 2024年8月8日
0.24.3 2024年8月1日
0.24.2 2024年7月17日
0.19.0 2024年3月25日
0.0.0 2023年3月13日

#409 in 编码

Download history 100/week @ 2024-04-22 510/week @ 2024-04-29 73/week @ 2024-05-06 503/week @ 2024-05-13 520/week @ 2024-05-20 232/week @ 2024-05-27 140/week @ 2024-06-03 315/week @ 2024-06-10 321/week @ 2024-06-17 212/week @ 2024-06-24 187/week @ 2024-07-01 313/week @ 2024-07-08 546/week @ 2024-07-15 189/week @ 2024-07-22 499/week @ 2024-07-29 346/week @ 2024-08-05

每月1,584次下载

MIT 许可证

460KB
11K SLoC

Rust 8K SLoC // 0.0% comments JavaScript 3K SLoC // 0.1% comments

ZEN 引擎

ZEN 引擎是一个友好的开源业务规则引擎 (BRE),用于根据 GoRules JSON 决策模型 (JDM) 标准(JSON Decision Model, JDM)执行决策模型。它用 Rust 编写,并为 NodeJS 和 Python 提供了原生绑定。ZEN 引擎允许从 JSON 文件加载和执行 JSON 决策模型 (JDM)。

资源

文档

在线规则引擎编辑器

安装

将以下内容添加到您的 Cargo.toml 文件中

[dependencies]
zen-engine = "0"

用法

要使用 Noop(默认)加载器执行简单的决策,您可以使用以下代码。

use serde_json::json;
use zen_engine::DecisionEngine;
use zen_engine::model::DecisionContent;

async fn evaluate() {
    let decision_content: DecisionContent = serde_json::from_str(include_str!("jdm_graph.json")).unwrap();
    let engine = DecisionEngine::default();
    let decision = engine.create_decision(decision_content.into());

    let result = decision.evaluate(&json!({ "input": 12 })).await;
}

或者,您也可以不构建引擎,通过使用 Decision::from 函数间接创建决策。

加载器

对于更复杂的使用案例,您想要加载多个决策并使用图,您可以使用以下预制的加载器之一

  • FilesystemLoader - 以给定路径作为根,尝试根据相对路径加载决策
  • MemoryLoader - 作为 HashMap(键值存储)工作
  • ClosureLoader - 允许定义简单的异步回调函数,该函数接受键作为参数,并返回一个 Arc<DecisionContent> 实例
  • NoopLoader - (默认)无法加载决策,允许使用 create_decision(主要用于简化跨语言的 API)

文件系统加载器

假设您有一个包含决策模型(.json 文件)的文件夹,该文件夹位于 /app/decisions 下,您可以使用以下方式使用 FilesystemLoader

use serde_json::json;
use zen_engine::DecisionEngine;
use zen_engine::loader::{FilesystemLoader, FilesystemLoaderOptions};

async fn evaluate() {
    let engine = DecisionEngine::new(FilesystemLoader::new(FilesystemLoaderOptions {
        keep_in_memory: true, // optionally, keep in memory for increase performance
        root: "/app/decisions"
    }));

    let context = json!({ "customer": { "joinedAt": "2022-01-01" } });
    // If you plan on using it multiple times, you may cache JDM for minor performance gains
    // In case of bindings (in other languages, this increase is much greater)
    {
        let promotion_decision = engine.get_decision("commercial/promotion.json").await.unwrap();
        let result = promotion_decision.evaluate(&context).await.unwrap();
    }

    // Or on demand
    {
        let result = engine.evaluate("commercial/promotion.json", &context).await.unwrap();
    }
}

自定义加载器

您可以通过实现 DecisionLoader 特性来为 zen 引擎创建自定义加载器。以下是一个 MemoryLoader 的实现示例。

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use zen_engine::loader::{DecisionLoader, LoaderError, LoaderResponse};
use zen_engine::model::DecisionContent;

#[derive(Debug, Default)]
pub struct MemoryLoader {
    memory_refs: RwLock<HashMap<String, Arc<DecisionContent>>>,
}

impl MemoryLoader {
    pub fn add<K, D>(&self, key: K, content: D)
    where
        K: Into<String>,
        D: Into<DecisionContent>,
    {
        let mut mref = self.memory_refs.write().unwrap();
        mref.insert(key.into(), Arc::new(content.into()));
    }
    pub fn get<K>(&self, key: K) -> Option<Arc<DecisionContent>>
    where
        K: AsRef<str>,
    {
        let mref = self.memory_refs.read().unwrap();
        mref.get(key.as_ref()).map(|r| r.clone())
    }
    pub fn remove<K>(&self, key: K) -> bool
    where
        K: AsRef<str>,
    {
        let mut mref = self.memory_refs.write().unwrap();
        mref.remove(key.as_ref()).is_some()
    }
}

impl DecisionLoader for MemoryLoader {
    fn load<'a>(&'a self, key: &'a str) -> impl Future<Output=LoaderResponse> + 'a {
        async move {
            self.get(&key)
                .ok_or_else(|| LoaderError::NotFound(key.to_string()).into())
        }
    }
}

依赖关系

~17–29MB
~500K SLoC