Skip to content

Commit ad5ef1e

Browse files
committed
fix(cypher): frontend should always emit exclusive kind matchers
1 parent a3be153 commit ad5ef1e

4 files changed

Lines changed: 34 additions & 20 deletions

File tree

cypher/frontend/expression.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ type NonArithmeticOperatorExpressionVisitor struct {
378378
func (s *NonArithmeticOperatorExpressionVisitor) EnterOC_NodeLabels(ctx *parser.OC_NodeLabelsContext) {
379379
s.Expression = &cypher.KindMatcher{
380380
Reference: s.Expression,
381+
// Cypher-generated `KindMatcher`s should _always_ be exclusive to jive with the spec
382+
IsExclusive: true,
381383
}
382384
}
383385

cypher/models/cypher/model.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ func (s AssignmentOperator) String() string {
2525
return string(s)
2626
}
2727

28-
type SyntaxNode any
29-
type Expression SyntaxNode
28+
type (
29+
SyntaxNode any
30+
Expression SyntaxNode
31+
)
3032

3133
type ExpressionList interface {
3234
Add(expression Expression)
@@ -744,12 +746,15 @@ func NewRangeQuantifier(value string) *RangeQuantifier {
744746
type KindMatcher struct {
745747
Reference Expression
746748
Kinds graph.Kinds
749+
// IsExclusive changes the kind matching operator from overlap (PG &&) to a stricter "left contains right" (PG @>)
750+
IsExclusive bool
747751
}
748752

749-
func NewKindMatcher(reference Expression, kinds graph.Kinds) *KindMatcher {
753+
func NewKindMatcher(reference Expression, kinds graph.Kinds, isExclusive bool) *KindMatcher {
750754
return &KindMatcher{
751-
Reference: reference,
752-
Kinds: kinds,
755+
Reference: reference,
756+
Kinds: kinds,
757+
IsExclusive: isExclusive,
753758
}
754759
}
755760

@@ -759,8 +764,9 @@ func (s *KindMatcher) copy() *KindMatcher {
759764
}
760765

761766
return &KindMatcher{
762-
Reference: Copy(s.Reference),
763-
Kinds: Copy(s.Kinds),
767+
Reference: Copy(s.Reference),
768+
Kinds: Copy(s.Kinds),
769+
IsExclusive: s.IsExclusive,
764770
}
765771
}
766772

cypher/models/pgsql/translate/kind.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@ import (
99
"github.com/specterops/dawgs/cypher/models/pgsql/pgd"
1010
)
1111

12-
func newPGKindIDMatcher(scope *Scope, treeTranslator *ExpressionTreeTranslator, binding *BoundIdentifier, kindIDs []int16) error {
12+
func newPGKindIDMatcher(scope *Scope, treeTranslator *ExpressionTreeTranslator, binding *BoundIdentifier, kindIDs []int16, isExclusive bool) error {
1313
kindIDsLiteral := pgsql.NewLiteral(kindIDs, pgsql.Int2Array)
1414

1515
switch binding.DataType {
1616
case pgsql.NodeComposite, pgsql.ExpansionRootNode, pgsql.ExpansionTerminalNode:
1717
treeTranslator.PushOperand(pgd.Column(binding.Identifier, pgsql.ColumnKindIDs))
1818
treeTranslator.PushOperand(kindIDsLiteral)
1919

20-
return treeTranslator.CompleteBinaryExpression(scope, pgsql.OperatorPGArrayLHSContainsRHS)
20+
if isExclusive {
21+
return treeTranslator.CompleteBinaryExpression(scope, pgsql.OperatorPGArrayLHSContainsRHS)
22+
} else {
23+
return treeTranslator.CompleteBinaryExpression(scope, pgsql.OperatorPGArrayOverlap)
24+
}
2125

2226
case pgsql.EdgeComposite, pgsql.ExpansionEdge:
27+
// An edge will only have a single kind_id, so the IsExclusive does not apply here.
2328
treeTranslator.PushOperand(pgsql.CompoundIdentifier{binding.Identifier, pgsql.ColumnKindID})
2429
treeTranslator.PushOperand(pgsql.NewAnyExpressionHinted(kindIDsLiteral))
2530

@@ -39,6 +44,6 @@ func (s *Translator) translateKindMatcher(kindMatcher *cypher.KindMatcher) error
3944
} else if kindIDs, err := s.kindMapper.MapKinds(kindMatcher.Kinds); err != nil {
4045
return fmt.Errorf("failed to translate kinds: %w", err)
4146
} else {
42-
return newPGKindIDMatcher(s.scope, s.treeTranslator, binding, kindIDs)
47+
return newPGKindIDMatcher(s.scope, s.treeTranslator, binding, kindIDs, kindMatcher.IsExclusive)
4348
}
4449
}

query/model.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import (
1010
)
1111

1212
func convertCriteria[T any](criteria ...graph.Criteria) []T {
13-
var (
14-
converted = make([]T, len(criteria))
15-
)
13+
converted := make([]T, len(criteria))
1614

1715
for idx, nextCriteria := range criteria {
1816
converted[idx] = nextCriteria.(T)
@@ -67,8 +65,9 @@ func DeleteKind(reference graph.Criteria, kind graph.Kind) *cypherModel.Updating
6765
return cypherModel.NewUpdatingClause(&cypherModel.Remove{
6866
Items: []*cypherModel.RemoveItem{{
6967
KindMatcher: &cypherModel.KindMatcher{
70-
Reference: reference,
71-
Kinds: graph.Kinds{kind},
68+
Reference: reference,
69+
Kinds: graph.Kinds{kind},
70+
IsExclusive: false,
7271
},
7372
}},
7473
})
@@ -78,8 +77,9 @@ func DeleteKinds(reference graph.Criteria, kinds graph.Kinds) *cypherModel.Updat
7877
return cypherModel.NewUpdatingClause(&cypherModel.Remove{
7978
Items: []*cypherModel.RemoveItem{{
8079
KindMatcher: &cypherModel.KindMatcher{
81-
Reference: reference,
82-
Kinds: kinds,
80+
Reference: reference,
81+
Kinds: kinds,
82+
IsExclusive: false,
8383
},
8484
}},
8585
})
@@ -131,13 +131,14 @@ func DeleteProperties(reference graph.Criteria, propertyNames ...string) *cypher
131131

132132
func Kind(reference graph.Criteria, kinds ...graph.Kind) *cypherModel.KindMatcher {
133133
return &cypherModel.KindMatcher{
134-
Reference: reference,
135-
Kinds: kinds,
134+
Reference: reference,
135+
Kinds: kinds,
136+
IsExclusive: false,
136137
}
137138
}
138139

139140
func KindIn(reference graph.Criteria, kinds ...graph.Kind) *cypherModel.KindMatcher {
140-
return cypherModel.NewKindMatcher(reference, kinds)
141+
return cypherModel.NewKindMatcher(reference, kinds, false)
141142
}
142143

143144
func NodeProperty(name string) *cypherModel.PropertyLookup {

0 commit comments

Comments
 (0)