Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
diff.go7.55 kB
// Package diff provides APOC diff operations. // // This package implements all apoc.diff.* functions for comparing // and finding differences between nodes, relationships, and data structures. package diff import ( "fmt" "reflect" ) // Node represents a graph node. type Node struct { ID int64 Labels []string Properties map[string]interface{} } // Relationship represents a graph relationship. type Relationship struct { ID int64 Type string StartNode int64 EndNode int64 Properties map[string]interface{} } // DiffResult represents the result of a diff operation. type DiffResult struct { Added map[string]interface{} Removed map[string]interface{} Changed map[string]interface{} Unchanged map[string]interface{} } // Nodes compares two nodes and returns differences. // // Example: // // apoc.diff.nodes(node1, node2) => {added: {...}, removed: {...}, changed: {...}} func Nodes(node1, node2 *Node) *DiffResult { result := &DiffResult{ Added: make(map[string]interface{}), Removed: make(map[string]interface{}), Changed: make(map[string]interface{}), Unchanged: make(map[string]interface{}), } // Check for added/changed properties for k, v2 := range node2.Properties { if v1, ok := node1.Properties[k]; ok { if !reflect.DeepEqual(v1, v2) { result.Changed[k] = map[string]interface{}{ "old": v1, "new": v2, } } else { result.Unchanged[k] = v1 } } else { result.Added[k] = v2 } } // Check for removed properties for k, v := range node1.Properties { if _, ok := node2.Properties[k]; !ok { result.Removed[k] = v } } return result } // Relationships compares two relationships and returns differences. // // Example: // // apoc.diff.relationships(rel1, rel2) => {added: {...}, removed: {...}, changed: {...}} func Relationships(rel1, rel2 *Relationship) *DiffResult { result := &DiffResult{ Added: make(map[string]interface{}), Removed: make(map[string]interface{}), Changed: make(map[string]interface{}), Unchanged: make(map[string]interface{}), } // Check for added/changed properties for k, v2 := range rel2.Properties { if v1, ok := rel1.Properties[k]; ok { if !reflect.DeepEqual(v1, v2) { result.Changed[k] = map[string]interface{}{ "old": v1, "new": v2, } } else { result.Unchanged[k] = v1 } } else { result.Added[k] = v2 } } // Check for removed properties for k, v := range rel1.Properties { if _, ok := rel2.Properties[k]; !ok { result.Removed[k] = v } } return result } // Maps compares two maps and returns differences. // // Example: // // apoc.diff.maps({a: 1}, {a: 2, b: 3}) => {added: {b: 3}, changed: {a: {old: 1, new: 2}}} func Maps(map1, map2 map[string]interface{}) *DiffResult { result := &DiffResult{ Added: make(map[string]interface{}), Removed: make(map[string]interface{}), Changed: make(map[string]interface{}), Unchanged: make(map[string]interface{}), } // Check for added/changed keys for k, v2 := range map2 { if v1, ok := map1[k]; ok { if !reflect.DeepEqual(v1, v2) { result.Changed[k] = map[string]interface{}{ "old": v1, "new": v2, } } else { result.Unchanged[k] = v1 } } else { result.Added[k] = v2 } } // Check for removed keys for k, v := range map1 { if _, ok := map2[k]; !ok { result.Removed[k] = v } } return result } // Lists compares two lists and returns differences. // // Example: // // apoc.diff.lists([1, 2, 3], [2, 3, 4]) => {added: [4], removed: [1], common: [2, 3]} func Lists(list1, list2 []interface{}) map[string]interface{} { added := make([]interface{}, 0) removed := make([]interface{}, 0) common := make([]interface{}, 0) // Build sets for comparison set1 := make(map[string]bool) set2 := make(map[string]bool) for _, item := range list1 { set1[fmt.Sprintf("%v", item)] = true } for _, item := range list2 { key := fmt.Sprintf("%v", item) set2[key] = true if set1[key] { common = append(common, item) } else { added = append(added, item) } } for _, item := range list1 { key := fmt.Sprintf("%v", item) if !set2[key] { removed = append(removed, item) } } return map[string]interface{}{ "added": added, "removed": removed, "common": common, } } // Strings compares two strings and returns character-level differences. // // Example: // // apoc.diff.strings('hello', 'hallo') => differences func Strings(str1, str2 string) []map[string]interface{} { differences := make([]map[string]interface{}, 0) // Simple character-by-character comparison maxLen := len(str1) if len(str2) > maxLen { maxLen = len(str2) } for i := 0; i < maxLen; i++ { var char1, char2 string if i < len(str1) { char1 = string(str1[i]) } if i < len(str2) { char2 = string(str2[i]) } if char1 != char2 { differences = append(differences, map[string]interface{}{ "position": i, "old": char1, "new": char2, }) } } return differences } // Deep performs a deep comparison of two values. // // Example: // // apoc.diff.deep(value1, value2) => true/false func Deep(value1, value2 interface{}) bool { return reflect.DeepEqual(value1, value2) } // Patch applies a diff patch to a map. // // Example: // // apoc.diff.patch(original, diff) => patched map func Patch(original map[string]interface{}, diff *DiffResult) map[string]interface{} { result := make(map[string]interface{}) // Copy unchanged and existing values for k, v := range original { if _, removed := diff.Removed[k]; !removed { result[k] = v } } // Apply changes for k, v := range diff.Changed { if changeMap, ok := v.(map[string]interface{}); ok { if newVal, ok := changeMap["new"]; ok { result[k] = newVal } } } // Add new values for k, v := range diff.Added { result[k] = v } return result } // Merge merges two maps with conflict resolution. // // Example: // // apoc.diff.merge(map1, map2, 'prefer_new') => merged map func Merge(map1, map2 map[string]interface{}, strategy string) map[string]interface{} { result := make(map[string]interface{}) // Copy first map for k, v := range map1 { result[k] = v } // Merge second map based on strategy for k, v2 := range map2 { if v1, exists := result[k]; exists { switch strategy { case "prefer_new": result[k] = v2 case "prefer_old": // Keep existing value case "combine": // Try to combine values result[k] = combineValues(v1, v2) default: result[k] = v2 } } else { result[k] = v2 } } return result } // combineValues attempts to combine two values intelligently. func combineValues(v1, v2 interface{}) interface{} { // If both are lists, concatenate if list1, ok1 := v1.([]interface{}); ok1 { if list2, ok2 := v2.([]interface{}); ok2 { return append(list1, list2...) } } // If both are numbers, add if num1, ok1 := v1.(float64); ok1 { if num2, ok2 := v2.(float64); ok2 { return num1 + num2 } } // If both are strings, concatenate if str1, ok1 := v1.(string); ok1 { if str2, ok2 := v2.(string); ok2 { return str1 + str2 } } // Default: prefer new value return v2 } // Summary provides a summary of differences. // // Example: // // apoc.diff.summary(diff) => {added: 2, removed: 1, changed: 3} func Summary(diff *DiffResult) map[string]int { return map[string]int{ "added": len(diff.Added), "removed": len(diff.Removed), "changed": len(diff.Changed), "unchanged": len(diff.Unchanged), } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server