Skip to main content
Glama
orneryd

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

by orneryd
comparison.go8.73 kB
// Comparison operators for NornicDB Cypher. // // This file contains functions for comparing values in WHERE clauses and // property matching. These functions implement Cypher's comparison semantics // with appropriate type coercion. // // # Comparison Operators // // Equality and inequality: // - compareEqual: n.value = 42, n.name = 'Alice' // - nodeMatchesProps: Match node against property filter // // Numeric comparison: // - compareGreater: n.age > 30 // - compareLess: n.age < 30 // // Pattern matching: // - compareRegex: n.name =~ '.*Smith' // // String operations: // - evaluateStringOp: CONTAINS, STARTS WITH, ENDS WITH // // List operations: // - evaluateInOp: n.status IN ['active', 'pending'] // // NULL checks: // - evaluateIsNull: IS NULL / IS NOT NULL // // # Type Coercion // // Comparisons follow Neo4j's type coercion rules: // - Numeric types are compared as numbers (int, float) // - Strings are compared lexicographically // - NULL propagates (NULL = anything → NULL) // // # ELI12 // // Comparison is like asking "is this the same as that?" // // n.age > 30 → "Is the person older than 30?" // n.name = 'Alice' → "Is their name Alice?" // n.email CONTAINS '@' → "Does the email have an @ sign?" // // These functions do the actual checking and tell us yes or no! // // # Neo4j Compatibility // // All comparison operators match Neo4j semantics exactly. package cypher import ( "fmt" "strings" "github.com/orneryd/nornicdb/pkg/storage" ) // nodeMatchesProps checks if a node's properties match the expected values. // // # Parameters // // - node: The node to check // - props: Map of expected property values // // # Returns // // - true if all expected properties match (or props is nil/empty) // // # Example // // nodeMatchesProps(node, map[string]interface{}{"name": "Alice", "age": 30}) // // Returns true only if node has both name="Alice" AND age=30 func (e *StorageExecutor) nodeMatchesProps(node *storage.Node, props map[string]interface{}) bool { if props == nil { return true } for key, expected := range props { actual, exists := node.Properties[key] if !exists { return false } if !e.compareEqual(actual, expected) { return false } } return true } // compareEqual handles equality comparison with type coercion. // // # Parameters // // - actual: The actual value from the node // - expected: The expected value from the query // // # Returns // // - true if values are equal (with type coercion) // // # Example // // compareEqual(int64(42), float64(42.0)) // true // compareEqual("hello", "hello") // true // compareEqual(nil, nil) // true // compareEqual(42, "42") // true (string coercion) func (e *StorageExecutor) compareEqual(actual, expected interface{}) bool { // Handle nil if actual == nil && expected == nil { return true } if actual == nil || expected == nil { return false } // Try numeric comparison actualNum, actualOk := toFloat64(actual) expectedNum, expectedOk := toFloat64(expected) if actualOk && expectedOk { return actualNum == expectedNum } // String comparison return fmt.Sprintf("%v", actual) == fmt.Sprintf("%v", expected) } // compareGreater handles > comparison. // // # Parameters // // - actual: The actual value // - expected: The threshold value // // # Returns // // - true if actual > expected func (e *StorageExecutor) compareGreater(actual, expected interface{}) bool { actualNum, actualOk := toFloat64(actual) expectedNum, expectedOk := toFloat64(expected) if actualOk && expectedOk { return actualNum > expectedNum } // String comparison as fallback return fmt.Sprintf("%v", actual) > fmt.Sprintf("%v", expected) } // compareLess handles < comparison. // // # Parameters // // - actual: The actual value // - expected: The threshold value // // # Returns // // - true if actual < expected func (e *StorageExecutor) compareLess(actual, expected interface{}) bool { actualNum, actualOk := toFloat64(actual) expectedNum, expectedOk := toFloat64(expected) if actualOk && expectedOk { return actualNum < expectedNum } // String comparison as fallback return fmt.Sprintf("%v", actual) < fmt.Sprintf("%v", expected) } // compareRegex handles =~ regex comparison. // Uses cached compiled regex for performance (avoids recompiling same pattern). // // # Parameters // // - actual: The string value to test // - expected: The regex pattern // // # Returns // // - true if actual matches the pattern func (e *StorageExecutor) compareRegex(actual, expected interface{}) bool { pattern, ok := expected.(string) if !ok { return false } actualStr := fmt.Sprintf("%v", actual) // Use cached regex compilation re, err := GetCachedRegex(pattern) if err != nil { return false } return re.MatchString(actualStr) } // evaluateStringOp handles CONTAINS, STARTS WITH, ENDS WITH. // // # Parameters // // - node: The node containing the property // - variable: The variable name in the query // - whereClause: The WHERE clause string // - op: The operator ("CONTAINS", "STARTS WITH", "ENDS WITH") // // # Returns // // - true if the string operation evaluates to true // // # Example // // evaluateStringOp(node, "n", "n.name CONTAINS 'Smith'", "CONTAINS") // // Returns true if node.Properties["name"] contains "Smith" func (e *StorageExecutor) evaluateStringOp(node *storage.Node, variable, whereClause, op string) bool { upperClause := strings.ToUpper(whereClause) opIdx := strings.Index(upperClause, " "+op+" ") if opIdx < 0 { return true } left := strings.TrimSpace(whereClause[:opIdx]) right := strings.TrimSpace(whereClause[opIdx+len(op)+2:]) // Extract property if !strings.HasPrefix(left, variable+".") { return true } propName := left[len(variable)+1:] actualVal, exists := node.Properties[propName] if !exists { return false } actualStr := fmt.Sprintf("%v", actualVal) expectedStr := fmt.Sprintf("%v", e.parseValue(right)) switch op { case "CONTAINS": return strings.Contains(actualStr, expectedStr) case "STARTS WITH": return strings.HasPrefix(actualStr, expectedStr) case "ENDS WITH": return strings.HasSuffix(actualStr, expectedStr) } return true } // evaluateInOp handles IN [list] operator. // // # Parameters // // - node: The node containing the property // - variable: The variable name in the query // - whereClause: The WHERE clause string // // # Returns // // - true if the property value is in the list // // # Example // // evaluateInOp(node, "n", "n.status IN ['active', 'pending']") // // Returns true if node.Properties["status"] is "active" or "pending" func (e *StorageExecutor) evaluateInOp(node *storage.Node, variable, whereClause string) bool { upperClause := strings.ToUpper(whereClause) inIdx := strings.Index(upperClause, " IN ") if inIdx < 0 { return true } left := strings.TrimSpace(whereClause[:inIdx]) right := strings.TrimSpace(whereClause[inIdx+4:]) // Extract property if !strings.HasPrefix(left, variable+".") { return true } propName := left[len(variable)+1:] actualVal, exists := node.Properties[propName] if !exists { return false } // Parse list: [val1, val2, ...] if strings.HasPrefix(right, "[") && strings.HasSuffix(right, "]") { listContent := right[1 : len(right)-1] items := strings.Split(listContent, ",") for _, item := range items { itemVal := e.parseValue(strings.TrimSpace(item)) if e.compareEqual(actualVal, itemVal) { return true } } } return false } // evaluateIsNull handles IS NULL / IS NOT NULL. // // # Parameters // // - node: The node containing the property // - variable: The variable name in the query // - whereClause: The WHERE clause string // - expectNotNull: true for IS NOT NULL, false for IS NULL // // # Returns // // - true if the NULL check evaluates to true // // # Example // // evaluateIsNull(node, "n", "n.email IS NOT NULL", true) // // Returns true if node.Properties["email"] exists and is not nil func (e *StorageExecutor) evaluateIsNull(node *storage.Node, variable, whereClause string, expectNotNull bool) bool { upperClause := strings.ToUpper(whereClause) var propExpr string if expectNotNull { idx := strings.Index(upperClause, " IS NOT NULL") propExpr = strings.TrimSpace(whereClause[:idx]) } else { idx := strings.Index(upperClause, " IS NULL") propExpr = strings.TrimSpace(whereClause[:idx]) } // Extract property if !strings.HasPrefix(propExpr, variable+".") { return true } propName := propExpr[len(variable)+1:] val, exists := node.Properties[propName] if expectNotNull { return exists && val != nil } return !exists || val == nil }

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