Skip to content

Commit

Permalink
Merge pull request #9 from codetiger/main
Browse files Browse the repository at this point in the history
Performance Improvements
  • Loading branch information
codetiger authored Dec 9, 2024
2 parents a3ad125 + f51374b commit 54c71ae
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 56 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"


[dev-dependencies]
criterion = "0.5"
lazy_static = "1.4"
Expand All @@ -26,3 +25,6 @@ reqwest = { version = "0.11", features = ["blocking"] }
[[bench]]
name = "jsonlogic_bench"
harness = false

[profile.release]
debug = true
42 changes: 42 additions & 0 deletions examples/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use datalogic_rs::JsonLogic;
use reqwest::blocking::get;
use serde_json::Value;

lazy_static::lazy_static! {
static ref TEST_CASES: Vec<(Value, Value, Value)> = {
let response = get("http://jsonlogic.com/tests.json")
.expect("Failed to fetch test cases")
.text()
.expect("Failed to read response");

let json_data: Vec<Value> = serde_json::from_str(&response)
.expect("Failed to parse test cases");

json_data.into_iter()
.filter_map(|entry| {
if let Value::Array(test_case) = entry {
if test_case.len() == 3 {
return Some((
test_case[0].clone(),
test_case[1].clone(),
test_case[2].clone()
));
}
}
None
})
.collect()
};
}

fn main() {
let logic = JsonLogic::new();

for _ in 0..100000 {
for (rule, data, expected) in TEST_CASES.iter() {
if let Ok(result) = logic.apply(rule, data) {
assert_eq!(result, *expected);
}
}
}
}
133 changes: 87 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use operators::{
missing::*,
};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;

pub type JsonLogicResult = Result<Value, Error>;
Expand All @@ -39,7 +38,38 @@ pub struct JsonLogic {
filter_op: Arc<FilterOperator>,
reduce_op: Arc<ReduceOperator>,

operators: HashMap<String, Arc<dyn Operator>>,
// Common arithmetic operators
not_eq_op: Arc<NotEqualsOperator>,
strict_not_eq_op: Arc<StrictNotEqualsOperator>,
gt_eq_op: Arc<GreaterThanEqualOperator>,
lt_eq_op: Arc<LessThanEqualOperator>,

// Logic operators
ternary_op: Arc<TernaryOperator>,
double_bang_op: Arc<DoubleBangOperator>,
if_op: Arc<IfOperator>,
merge_op: Arc<MergeOperator>,
missing_op: Arc<MissingOperator>,
missing_some_op: Arc<MissingSomeOperator>,
all_op: Arc<AllOperator>,
none_op: Arc<NoneOperator>,
some_op: Arc<SomeOperator>,

preserve_op: Arc<PreserveOperator>,

// String operators
in_op: Arc<InOperator>,
cat_op: Arc<CatOperator>,
substr_op: Arc<SubstrOperator>,

// Arithmetic operators
add_op: Arc<AddOperator>,
multiply_op: Arc<MultiplyOperator>,
subtract_op: Arc<SubtractOperator>,
divide_op: Arc<DivideOperator>,
modulo_op: Arc<ModuloOperator>,
max_op: Arc<MaxOperator>,
min_op: Arc<MinOperator>,
}

impl Default for JsonLogic {
Expand All @@ -50,7 +80,7 @@ impl Default for JsonLogic {

impl JsonLogic {
pub fn new() -> Self {
let mut logic = Self {
let logic = Self {
var_op: Arc::new(VarOperator),
eq_op: Arc::new(EqualsOperator),
strict_eq_op: Arc::new(StrictEqualsOperator),
Expand All @@ -62,50 +92,39 @@ impl JsonLogic {
map_op: Arc::new(MapOperator),
filter_op: Arc::new(FilterOperator),
reduce_op: Arc::new(ReduceOperator),
operators: HashMap::new(),
not_eq_op: Arc::new(NotEqualsOperator),
strict_not_eq_op: Arc::new(StrictNotEqualsOperator),
gt_eq_op: Arc::new(GreaterThanEqualOperator),
lt_eq_op: Arc::new(LessThanEqualOperator),
ternary_op: Arc::new(TernaryOperator),
double_bang_op: Arc::new(DoubleBangOperator),
if_op: Arc::new(IfOperator),
merge_op: Arc::new(MergeOperator),
missing_op: Arc::new(MissingOperator),
missing_some_op: Arc::new(MissingSomeOperator),
all_op: Arc::new(AllOperator),
none_op: Arc::new(NoneOperator),
some_op: Arc::new(SomeOperator),
preserve_op: Arc::new(PreserveOperator),
in_op: Arc::new(InOperator),
cat_op: Arc::new(CatOperator),
substr_op: Arc::new(SubstrOperator),
add_op: Arc::new(AddOperator),
multiply_op: Arc::new(MultiplyOperator),
subtract_op: Arc::new(SubtractOperator),
divide_op: Arc::new(DivideOperator),
modulo_op: Arc::new(ModuloOperator),
max_op: Arc::new(MaxOperator),
min_op: Arc::new(MinOperator),
};
logic.register_defaults();
logic
}

fn register_defaults(&mut self) {
self.operators.insert("!=".into(), Arc::new(NotEqualsOperator));
self.operators.insert("!==".into(), Arc::new(StrictNotEqualsOperator));
self.operators.insert(">=".into(), Arc::new(GreaterThanEqualOperator));
self.operators.insert("<=".into(), Arc::new(LessThanEqualOperator));

self.operators.insert("?:".into(), Arc::new(TernaryOperator));
self.operators.insert("!!".into(), Arc::new(DoubleBangOperator));

self.operators.insert("in".into(), Arc::new(InOperator));
self.operators.insert("cat".into(), Arc::new(CatOperator));
self.operators.insert("substr".into(), Arc::new(SubstrOperator));

self.operators.insert("+".into(), Arc::new(AddOperator));
self.operators.insert("*".into(), Arc::new(MultiplyOperator));
self.operators.insert("-".into(), Arc::new(SubtractOperator));
self.operators.insert("/".into(), Arc::new(DivideOperator));
self.operators.insert("%".into(), Arc::new(ModuloOperator));
self.operators.insert("max".into(), Arc::new(MaxOperator));
self.operators.insert("min".into(), Arc::new(MinOperator));

self.operators.insert("merge".into(), Arc::new(MergeOperator));

self.operators.insert("if".into(), Arc::new(IfOperator));

self.operators.insert("missing".into(), Arc::new(MissingOperator));
self.operators.insert("missing_some".into(), Arc::new(MissingSomeOperator));

self.operators.insert("all".into(), Arc::new(AllOperator));
self.operators.insert("none".into(), Arc::new(NoneOperator));
self.operators.insert("some".into(), Arc::new(SomeOperator));

self.operators.insert("preserve".into(), Arc::new(PreserveOperator));

}

pub fn apply(&self, logic: &Value, data: &Value) -> JsonLogicResult {
match logic {
Value::String(_) | Value::Number(_) | Value::Bool(_) | Value::Null => {
Ok(logic.clone())
}
Value::Object(map) if map.len() == 1 => {
let (op, args) = map.iter().next().unwrap();
let operator: &dyn Operator = match op.as_str() {
Expand All @@ -120,8 +139,33 @@ impl JsonLogic {
"map" => &*self.map_op,
"filter" => &*self.filter_op,
"reduce" => &*self.reduce_op,
_ => &**self.operators.get(op)
.ok_or(Error::UnknownOperator(op.clone()))?
"!=" => &*self.not_eq_op,
"!==" => &*self.strict_not_eq_op,
">=" => &*self.gt_eq_op,
"<=" => &*self.lt_eq_op,
"?:" => &*self.ternary_op,
"!!" => &*self.double_bang_op,
"if" => &*self.if_op,
"merge" => &*self.merge_op,
"missing" => &*self.missing_op,
"missing_some" => &*self.missing_some_op,
"all" => &*self.all_op,
"none" => &*self.none_op,
"some" => &*self.some_op,
"preserve" => &*self.preserve_op,
"in" => &*self.in_op,
"cat" => &*self.cat_op,
"substr" => &*self.substr_op,
"+" => &*self.add_op,
"*" => &*self.multiply_op,
"-" => &*self.subtract_op,
"/" => &*self.divide_op,
"%" => &*self.modulo_op,
"max" => &*self.max_op,
"min" => &*self.min_op,
_ => {
return Err(Error::UnknownOperator(op.clone()));
}
};

if operator.auto_traverse() {
Expand All @@ -146,9 +190,6 @@ impl JsonLogic {
}
Ok(Value::Array(results))
}
Value::String(_) | Value::Number(_) | Value::Bool(_) | Value::Null => {
Ok(logic.clone())
}
_ => Err(Error::InvalidRule("Invalid Rule".to_string())),
}
}
Expand Down
31 changes: 22 additions & 9 deletions src/operators/array.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::operators::operator::Operator;
use crate::{JsonLogic, JsonLogicResult};
use serde_json::{json, Value};
use serde_json::Value;

pub struct FilterOperator;

Expand Down Expand Up @@ -31,7 +31,7 @@ impl Operator for FilterOperator {
_ => return Ok(Value::Array(Vec::new())),
};

let mut result = Vec::with_capacity(array.len() / 2);
let mut result = Vec::with_capacity(array.len() / 4);

for item in array {
let condition_result = logic.apply(condition, &item)?;
Expand Down Expand Up @@ -114,13 +114,26 @@ impl Operator for ReduceOperator {
_ => return Ok(initial_value),
};

array.into_iter().try_fold(initial_value, |acc, current| {
let context = json!({
"current": current,
"accumulator": acc
});
logic.apply(reducer, &context)
})
static CURRENT: &str = "current";
static ACCUMULATOR: &str = "accumulator";

let mut context = serde_json::Map::with_capacity(2);
let mut acc = initial_value;

context.insert(CURRENT.to_owned(), Value::Null);
context.insert(ACCUMULATOR.to_owned(), Value::Null);
let mut context_value = Value::Object(context);

for current in array {
if let Value::Object(ref mut map) = context_value {
map.get_mut(CURRENT).map(|v| *v = current);
map.get_mut(ACCUMULATOR).map(|v| *v = acc.clone());
}

acc = logic.apply(reducer, &context_value)?;
}

Ok(acc)
}
}

Expand Down

0 comments on commit 54c71ae

Please sign in to comment.