Skip to content

Commit

Permalink
fix merge
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-gorules committed Oct 22, 2024
1 parent 16bec40 commit dc4e87c
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 17 deletions.
4 changes: 2 additions & 2 deletions core/engine/src/util/transform_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl TransformAttributes {
}

if self.pass_through {
response.output = input.clone().merge(&response.output);
response.output = input.clone().merge_clone(&response.output);
}

response.output.dot_remove("$nodes");
Expand All @@ -87,7 +87,7 @@ impl TransformAttributes {

if self.pass_through {
let mut node_input = node_input;
output = node_input.merge(&output)
output = node_input.merge_clone(&output)
}

Ok(NodeResponse { output, trace_data })
Expand Down
98 changes: 83 additions & 15 deletions core/expression/src/variable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,18 @@ impl Variable {
}

pub fn merge(&mut self, patch: &Variable) -> Variable {
merge_variables(self, patch, true);
let _ = merge_variables(self, patch, true, MergeStrategy::InPlace);

self.shallow_clone()
}

pub fn merge_clone(&mut self, patch: &Variable) -> Variable {
let mut new_self = self.shallow_clone();

let _ = merge_variables(&mut new_self, patch, true, MergeStrategy::CloneOnWrite);
new_self
}

pub fn shallow_clone(&self) -> Self {
match self {
Variable::Null => Variable::Null,
Expand Down Expand Up @@ -228,35 +235,96 @@ impl Clone for Variable {
}
}

fn merge_variables(doc: &mut Variable, patch: &Variable, top_level: bool) {
#[derive(Copy, Clone)]
enum MergeStrategy {
InPlace,
CloneOnWrite,
}

fn merge_variables(
doc: &mut Variable,
patch: &Variable,
top_level: bool,
strategy: MergeStrategy,
) -> bool {
if patch.is_array() && top_level {
*doc = patch.shallow_clone();
return;
return true;
}

if !patch.is_object() && top_level {
return;
return false;
}

if doc.is_object() && patch.is_object() {
let map_ref = doc.as_object().unwrap();
let doc_ref = doc.as_object().unwrap();
let patch_ref = patch.as_object().unwrap();
if Rc::ptr_eq(&map_ref, &patch_ref) {
return;
if Rc::ptr_eq(&doc_ref, &patch_ref) {
return false;
}

let mut map = map_ref.borrow_mut();
let patch = patch_ref.borrow();
for (key, value) in patch.deref() {
if value == &Variable::Null {
map.remove(key.as_str());
} else {
let entry = map.entry(key.to_string()).or_insert(Variable::Null);
merge_variables(entry, value, false)
match strategy {
MergeStrategy::InPlace => {
let mut map = doc_ref.borrow_mut();
for (key, value) in patch.deref() {
if value == &Variable::Null {
map.remove(key.as_str());
} else {
let entry = map.entry(key.to_string()).or_insert(Variable::Null);
merge_variables(entry, value, false, strategy);
}
}

return true;
}
MergeStrategy::CloneOnWrite => {
let mut changed = false;
let mut new_map = None;

for (key, value) in patch.deref() {
// Get or create the new map if we haven't yet
let map = if let Some(ref mut m) = new_map {
m
} else {
let m = doc_ref.borrow().clone();
new_map = Some(m);
new_map.as_mut().unwrap()
};

if value == &Variable::Null {
// Remove null values
if map.remove(key.as_str()).is_some() {
changed = true;
}
} else {
// Handle nested merging
let entry = map.entry(key.to_string()).or_insert(Variable::Null);
if merge_variables(entry, value, false, strategy) {
changed = true;
}
}
}

// Only update doc if changes were made
if changed {
if let Some(new_map) = new_map {
*doc = Variable::Object(Rc::new(RefCell::new(new_map)));
}
return true;
}

return false;
}
}
} else {
*doc = patch.shallow_clone();
let new_value = patch.shallow_clone();
if *doc != new_value {
*doc = new_value;
return true;
}

return false;
}
}

Expand Down

0 comments on commit dc4e87c

Please sign in to comment.