Skip to main content
Glama
orneryd

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

by orneryd
index_hints_test.go7.74 kB
package cypher import ( "testing" ) func TestParseIndexHints(t *testing.T) { tests := []struct { name string query string expectedHints int expectedClean string checkHint func([]IndexHint) bool }{ { name: "single index hint", query: "MATCH (n:Person) USING INDEX n:Person(name) WHERE n.name = 'Alice' RETURN n", expectedHints: 1, expectedClean: "MATCH (n:Person) WHERE n.name = 'Alice' RETURN n", checkHint: func(hints []IndexHint) bool { return hints[0].Type == HintIndex && hints[0].Variable == "n" && hints[0].Label == "Person" && hints[0].Property == "name" }, }, { name: "multiple index hints", query: "MATCH (a:Person)-[:KNOWS]->(b:Person) USING INDEX a:Person(name) USING INDEX b:Person(email) WHERE a.name = 'Alice' RETURN a, b", expectedHints: 2, expectedClean: "MATCH (a:Person)-[:KNOWS]->(b:Person) WHERE a.name = 'Alice' RETURN a, b", checkHint: func(hints []IndexHint) bool { return hints[0].Variable == "a" && hints[0].Property == "name" && hints[1].Variable == "b" && hints[1].Property == "email" }, }, { name: "scan hint", query: "MATCH (n:Person) USING SCAN n:Person WHERE n.age > 30 RETURN n", expectedHints: 1, expectedClean: "MATCH (n:Person) WHERE n.age > 30 RETURN n", checkHint: func(hints []IndexHint) bool { return hints[0].Type == HintScan && hints[0].Variable == "n" && hints[0].Label == "Person" }, }, { name: "join hint", query: "MATCH (a:Person)-[:KNOWS]->(b:Person) USING JOIN ON a WHERE a.name = 'Alice' RETURN a, b", expectedHints: 1, expectedClean: "MATCH (a:Person)-[:KNOWS]->(b:Person) WHERE a.name = 'Alice' RETURN a, b", checkHint: func(hints []IndexHint) bool { return hints[0].Type == HintJoin && hints[0].Variable == "a" }, }, { name: "composite index hint", query: "MATCH (n:Person) USING INDEX n:Person(firstName, lastName) WHERE n.firstName = 'John' RETURN n", expectedHints: 1, expectedClean: "MATCH (n:Person) WHERE n.firstName = 'John' RETURN n", checkHint: func(hints []IndexHint) bool { return hints[0].Type == HintIndex && hints[0].Property == "firstName" && len(hints[0].Properties) == 2 && hints[0].Properties[1] == "lastName" }, }, { name: "no hints", query: "MATCH (n:Person) WHERE n.name = 'Alice' RETURN n", expectedHints: 0, expectedClean: "MATCH (n:Person) WHERE n.name = 'Alice' RETURN n", checkHint: nil, }, { name: "case insensitive", query: "MATCH (n:Person) using index n:Person(name) WHERE n.name = 'Alice' RETURN n", expectedHints: 1, expectedClean: "MATCH (n:Person) WHERE n.name = 'Alice' RETURN n", checkHint: func(hints []IndexHint) bool { return hints[0].Type == HintIndex && hints[0].Variable == "n" && hints[0].Label == "Person" }, }, { name: "mixed hints", query: "MATCH (a:Person)-[:KNOWS]->(b:Person) USING INDEX a:Person(name) USING SCAN b:Person WHERE a.name = 'Alice' RETURN a, b", expectedHints: 2, expectedClean: "MATCH (a:Person)-[:KNOWS]->(b:Person) WHERE a.name = 'Alice' RETURN a, b", checkHint: func(hints []IndexHint) bool { hasIndex := false hasScan := false for _, h := range hints { if h.Type == HintIndex && h.Variable == "a" { hasIndex = true } if h.Type == HintScan && h.Variable == "b" { hasScan = true } } return hasIndex && hasScan }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hints, cleanQuery := ParseIndexHints(tt.query) if len(hints) != tt.expectedHints { t.Errorf("expected %d hints, got %d", tt.expectedHints, len(hints)) } if cleanQuery != tt.expectedClean { t.Errorf("expected clean query:\n%s\ngot:\n%s", tt.expectedClean, cleanQuery) } if tt.checkHint != nil && !tt.checkHint(hints) { t.Errorf("hint check failed for hints: %+v", hints) } }) } } func TestIndexHintContext(t *testing.T) { hints := []IndexHint{ {Type: HintIndex, Variable: "n", Label: "Person", Property: "name"}, {Type: HintIndex, Variable: "n", Label: "Person", Property: "email"}, {Type: HintScan, Variable: "m", Label: "Movie"}, } ctx := NewIndexHintContext(hints) t.Run("GetHintsForVariable", func(t *testing.T) { nHints := ctx.GetHintsForVariable("n") if len(nHints) != 2 { t.Errorf("expected 2 hints for 'n', got %d", len(nHints)) } mHints := ctx.GetHintsForVariable("m") if len(mHints) != 1 { t.Errorf("expected 1 hint for 'm', got %d", len(mHints)) } xHints := ctx.GetHintsForVariable("x") if len(xHints) != 0 { t.Errorf("expected 0 hints for 'x', got %d", len(xHints)) } }) t.Run("HasIndexHint", func(t *testing.T) { if !ctx.HasIndexHint("n", "Person", "name") { t.Error("expected index hint for n:Person(name)") } if !ctx.HasIndexHint("n", "Person", "email") { t.Error("expected index hint for n:Person(email)") } if ctx.HasIndexHint("n", "Person", "age") { t.Error("unexpected index hint for n:Person(age)") } if ctx.HasIndexHint("m", "Movie", "title") { t.Error("unexpected index hint for m:Movie(title)") } }) t.Run("ShouldForceScan", func(t *testing.T) { if !ctx.ShouldForceScan("m", "Movie") { t.Error("expected scan hint for m:Movie") } if ctx.ShouldForceScan("n", "Person") { t.Error("unexpected scan hint for n:Person") } }) } func TestIndexHintString(t *testing.T) { tests := []struct { hint IndexHint expected string }{ { hint: IndexHint{Type: HintIndex, Variable: "n", Label: "Person", Property: "name"}, expected: "USING INDEX n:Person(name)", }, { hint: IndexHint{Type: HintIndex, Variable: "n", Label: "Person", Property: "firstName", Properties: []string{"firstName", "lastName"}}, expected: "USING INDEX n:Person(firstName, lastName)", }, { hint: IndexHint{Type: HintScan, Variable: "n", Label: "Person"}, expected: "USING SCAN n:Person", }, { hint: IndexHint{Type: HintJoin, Variable: "n"}, expected: "USING JOIN ON n", }, } for _, tt := range tests { t.Run(tt.expected, func(t *testing.T) { result := tt.hint.String() if result != tt.expected { t.Errorf("expected %q, got %q", tt.expected, result) } }) } } func TestCompareValues(t *testing.T) { tests := []struct { name string a, b interface{} expected bool }{ {"nil == nil", nil, nil, true}, {"nil != value", nil, "test", false}, {"same string", "test", "test", true}, // Note: compareValues in case_expression.go uses sprintf which is case-sensitive {"case sensitive", "Test", "test", false}, {"int == int", 42, 42, true}, {"int == float64", 42, 42.0, true}, {"int64 == float64", int64(42), 42.0, true}, {"different numbers", 42, 43, false}, // Note: compareValues in case_expression.go converts numeric strings to float64 {"string_42 == number_42", "42", 42, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := compareValues(tt.a, tt.b) if result != tt.expected { t.Errorf("compareValues(%v, %v) = %v, expected %v", tt.a, tt.b, result, tt.expected) } }) } } func TestNilIndexHintContext(t *testing.T) { var ctx *IndexHintContext = nil // Should not panic hints := ctx.GetHintsForVariable("n") if len(hints) != 0 { t.Error("expected empty hints for nil context") } if ctx.HasIndexHint("n", "Person", "name") { t.Error("expected false for nil context") } if ctx.ShouldForceScan("n", "Person") { t.Error("expected false for nil context") } }

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