Skip to content

Commit fb4d828

Browse files
authored
Merge pull request #10 from microsoft/virtual_graph_improvements
Virtual graph updates
2 parents e8b3ee1 + 9026d2f commit fb4d828

File tree

6 files changed

+43
-13
lines changed

6 files changed

+43
-13
lines changed

flowquery-py/src/graph/pattern_expression.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ def __init__(self):
2020
self._evaluation: bool = False
2121

2222
def add_element(self, element) -> None:
23-
"""Add an element to the pattern, ensuring it starts with a NodeReference."""
24-
if len(self._chain) == 0 and not isinstance(element, NodeReference):
25-
raise ValueError("PatternExpression must start with a NodeReference")
2623
super().add_element(element)
2724

25+
def verify(self) -> None:
26+
if(len(self._chain) == 0):
27+
raise ValueError("PatternExpression cannot be empty")
28+
if not(any(isinstance(el, NodeReference) for el in self._chain if isinstance(el, ASTNode))):
29+
raise ValueError("PatternExpression must contain at least one NodeReference")
30+
2831
@property
2932
def identifier(self):
3033
return None

flowquery-py/src/parsing/parser.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,6 @@ def _parse_pattern_expression(self) -> Optional[PatternExpression]:
426426
node = self._parse_node()
427427
if node is None:
428428
raise ValueError("Expected node definition")
429-
if not isinstance(node, NodeReference):
430-
raise ValueError("PatternExpression must start with a NodeReference")
431429
pattern.add_element(node)
432430
while True:
433431
relationship = self._parse_relationship()
@@ -440,6 +438,7 @@ def _parse_pattern_expression(self) -> Optional[PatternExpression]:
440438
if node is None:
441439
raise ValueError("Expected target node definition")
442440
pattern.add_element(node)
441+
pattern.verify()
443442
return pattern
444443

445444
def _parse_node(self) -> Optional[Node]:
@@ -606,7 +605,7 @@ def _parse_expression(self) -> Optional[Expression]:
606605
if func is not None:
607606
lookup = self._parse_lookup(func)
608607
expression.add_node(lookup)
609-
elif self.token.is_left_parenthesis() and self.peek() is not None and self.peek().is_identifier():
608+
elif self.token.is_left_parenthesis() and self.peek() is not None and (self.peek().is_identifier() or self.peek().is_colon() or self.peek().is_right_parenthesis()):
610609
# Possible graph pattern expression
611610
pattern = self._parse_pattern_expression()
612611
if pattern is not None:

flowquery-py/tests/parsing/test_parser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,9 @@ def test_parse_statement_with_graph_pattern_in_where_clause(self):
672672
"--- Reference (a)"
673673
)
674674
assert ast.print() == expected
675+
676+
def test_check_pattern_expression_without_noderef(self):
677+
"""Test check pattern expression without NodeReference."""
678+
parser = Parser()
679+
with pytest.raises(Exception, match="PatternExpression must contain at least one NodeReference"):
680+
parser.parse("MATCH (a:Person) WHERE (:Person)-[:KNOWS]->(:Person) RETURN a")

src/graph/pattern_expression.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@ class PatternExpression extends Pattern {
1010
throw new Error("Cannot set identifier on PatternExpression");
1111
}
1212
public addElement(element: Relationship | Node): void {
13-
if (this._chain.length == 0 && !(element instanceof NodeReference)) {
14-
throw new Error("PatternExpression must start with a NodeReference");
15-
}
1613
super.addElement(element);
1714
}
15+
public verify(): void {
16+
if (this._chain.length === 0) {
17+
throw new Error("PatternExpression must contain at least one element");
18+
}
19+
const referenced = this._chain.some((element) => {
20+
if (element instanceof NodeReference) {
21+
return true;
22+
}
23+
return false;
24+
});
25+
if (!referenced) {
26+
throw new Error("PatternExpression must contain at least one NodeReference");
27+
}
28+
}
1829
public async evaluate(): Promise<void> {
1930
this._evaluation = false;
2031
this.endNode.todoNext = async () => {

src/parsing/parser.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,6 @@ class Parser extends BaseParser {
516516
if (node === null) {
517517
throw new Error("Expected node definition");
518518
}
519-
if (!(node instanceof NodeReference)) {
520-
throw new Error("PatternExpression must start with a NodeReference");
521-
}
522519
pattern.addElement(node);
523520
let relationship: Relationship | null = null;
524521
while (true) {
@@ -536,6 +533,7 @@ class Parser extends BaseParser {
536533
}
537534
pattern.addElement(node);
538535
}
536+
pattern.verify();
539537
return pattern;
540538
}
541539

@@ -709,7 +707,12 @@ class Parser extends BaseParser {
709707
const lookup = this.parseLookup(func);
710708
expression.addNode(lookup);
711709
}
712-
} else if (this.token.isLeftParenthesis() && this.peek()?.isIdentifier()) {
710+
} else if (
711+
this.token.isLeftParenthesis() &&
712+
(this.peek()?.isIdentifier() ||
713+
this.peek()?.isColon() ||
714+
this.peek()?.isRightParenthesis())
715+
) {
713716
// Possible graph pattern expression
714717
const pattern = this.parsePatternExpression();
715718
if (pattern !== null) {

tests/parsing/parser.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,11 @@ test("Parse statement with graph pattern in where clause", () => {
752752
expect(relationship.type).toBe("KNOWS");
753753
expect(target.label).toBe("Person");
754754
});
755+
756+
test("Test check pattern expression without NodeReference", () => {
757+
const parser = new Parser();
758+
// Should throw an error because pattern does not start with NodeReference
759+
expect(() => {
760+
parser.parse("MATCH (a:Person) WHERE (:Person)-[:KNOWS]->(:Person) RETURN a");
761+
}).toThrow("PatternExpression must contain at least one NodeReference");
762+
});

0 commit comments

Comments
 (0)