flowquery 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/python-publish.yml +97 -0
- package/dist/compute/runner.d.ts +3 -2
- package/dist/compute/runner.d.ts.map +1 -1
- package/dist/compute/runner.js +7 -7
- package/dist/compute/runner.js.map +1 -1
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/data.d.ts +31 -0
- package/dist/graph/data.d.ts.map +1 -0
- package/dist/graph/data.js +110 -0
- package/dist/graph/data.js.map +1 -0
- package/dist/graph/database.d.ts +20 -0
- package/dist/graph/database.d.ts.map +1 -0
- package/dist/graph/database.js +77 -0
- package/dist/graph/database.js.map +1 -0
- package/dist/graph/hops.d.ts +11 -0
- package/dist/graph/hops.d.ts.map +1 -0
- package/dist/graph/hops.js +25 -0
- package/dist/graph/hops.js.map +1 -0
- package/dist/graph/node.d.ts +35 -0
- package/dist/graph/node.d.ts.map +1 -0
- package/dist/graph/node.js +113 -0
- package/dist/graph/node.js.map +1 -0
- package/dist/graph/node_data.d.ts +11 -0
- package/dist/graph/node_data.d.ts.map +1 -0
- package/dist/graph/node_data.js +20 -0
- package/dist/graph/node_data.js.map +1 -0
- package/dist/graph/node_reference.d.ts +10 -0
- package/dist/graph/node_reference.d.ts.map +1 -0
- package/dist/graph/node_reference.js +52 -0
- package/dist/graph/node_reference.js.map +1 -0
- package/dist/graph/pattern.d.ts +18 -0
- package/dist/graph/pattern.d.ts.map +1 -0
- package/dist/graph/pattern.js +114 -0
- package/dist/graph/pattern.js.map +1 -0
- package/dist/graph/pattern_expression.d.ts +14 -0
- package/dist/graph/pattern_expression.d.ts.map +1 -0
- package/dist/graph/pattern_expression.js +58 -0
- package/dist/graph/pattern_expression.js.map +1 -0
- package/dist/graph/patterns.d.ts +11 -0
- package/dist/graph/patterns.d.ts.map +1 -0
- package/dist/graph/patterns.js +49 -0
- package/dist/graph/patterns.js.map +1 -0
- package/dist/graph/physical_node.d.ts +10 -0
- package/dist/graph/physical_node.d.ts.map +1 -0
- package/dist/graph/physical_node.js +40 -0
- package/dist/graph/physical_node.js.map +1 -0
- package/dist/graph/physical_relationship.d.ts +10 -0
- package/dist/graph/physical_relationship.d.ts.map +1 -0
- package/dist/graph/physical_relationship.js +40 -0
- package/dist/graph/physical_relationship.js.map +1 -0
- package/dist/graph/relationship.d.ts +40 -0
- package/dist/graph/relationship.d.ts.map +1 -0
- package/dist/graph/relationship.js +124 -0
- package/dist/graph/relationship.js.map +1 -0
- package/dist/graph/relationship_data.d.ts +12 -0
- package/dist/graph/relationship_data.d.ts.map +1 -0
- package/dist/graph/relationship_data.js +40 -0
- package/dist/graph/relationship_data.js.map +1 -0
- package/dist/graph/relationship_match_collector.d.ts +19 -0
- package/dist/graph/relationship_match_collector.d.ts.map +1 -0
- package/dist/graph/relationship_match_collector.js +55 -0
- package/dist/graph/relationship_match_collector.js.map +1 -0
- package/dist/graph/relationship_reference.d.ts +8 -0
- package/dist/graph/relationship_reference.d.ts.map +1 -0
- package/dist/graph/relationship_reference.js +37 -0
- package/dist/graph/relationship_reference.js.map +1 -0
- package/dist/parsing/base_parser.d.ts +1 -0
- package/dist/parsing/base_parser.d.ts.map +1 -1
- package/dist/parsing/base_parser.js +4 -1
- package/dist/parsing/base_parser.js.map +1 -1
- package/dist/parsing/context.d.ts +2 -2
- package/dist/parsing/context.js +5 -5
- package/dist/parsing/expressions/boolean.d.ts +8 -0
- package/dist/parsing/expressions/boolean.d.ts.map +1 -0
- package/dist/parsing/expressions/boolean.js +26 -0
- package/dist/parsing/expressions/boolean.js.map +1 -0
- package/dist/parsing/expressions/expression.d.ts +4 -1
- package/dist/parsing/expressions/expression.d.ts.map +1 -1
- package/dist/parsing/expressions/expression.js +15 -8
- package/dist/parsing/expressions/expression.js.map +1 -1
- package/dist/parsing/expressions/expression_map.d.ts +1 -0
- package/dist/parsing/expressions/expression_map.d.ts.map +1 -1
- package/dist/parsing/expressions/expression_map.js +3 -0
- package/dist/parsing/expressions/expression_map.js.map +1 -1
- package/dist/parsing/expressions/operator.d.ts +1 -1
- package/dist/parsing/expressions/operator.d.ts.map +1 -1
- package/dist/parsing/expressions/operator.js.map +1 -1
- package/dist/parsing/functions/function_factory.d.ts +13 -13
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +20 -18
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/operations/call.d.ts.map +1 -1
- package/dist/parsing/operations/call.js +3 -1
- package/dist/parsing/operations/call.js.map +1 -1
- package/dist/parsing/operations/create_node.d.ts +14 -0
- package/dist/parsing/operations/create_node.d.ts.map +1 -0
- package/dist/parsing/operations/create_node.js +51 -0
- package/dist/parsing/operations/create_node.js.map +1 -0
- package/dist/parsing/operations/create_relationship.d.ts +14 -0
- package/dist/parsing/operations/create_relationship.d.ts.map +1 -0
- package/dist/parsing/operations/create_relationship.js +51 -0
- package/dist/parsing/operations/create_relationship.js.map +1 -0
- package/dist/parsing/operations/match.d.ts +15 -0
- package/dist/parsing/operations/match.d.ts.map +1 -0
- package/dist/parsing/operations/match.js +45 -0
- package/dist/parsing/operations/match.js.map +1 -0
- package/dist/parsing/operations/operation.d.ts +1 -0
- package/dist/parsing/operations/operation.d.ts.map +1 -1
- package/dist/parsing/operations/operation.js +6 -0
- package/dist/parsing/operations/operation.js.map +1 -1
- package/dist/parsing/operations/return.d.ts +1 -0
- package/dist/parsing/operations/return.d.ts.map +1 -1
- package/dist/parsing/operations/return.js +7 -1
- package/dist/parsing/operations/return.js.map +1 -1
- package/dist/parsing/operations/where.d.ts +1 -1
- package/dist/parsing/operations/where.d.ts.map +1 -1
- package/dist/parsing/operations/where.js +4 -0
- package/dist/parsing/operations/where.js.map +1 -1
- package/dist/parsing/parser.d.ts +10 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +344 -5
- package/dist/parsing/parser.js.map +1 -1
- package/dist/parsing/token_to_node.d.ts.map +1 -1
- package/dist/parsing/token_to_node.js +7 -0
- package/dist/parsing/token_to_node.js.map +1 -1
- package/dist/tokenization/keyword.d.ts +1 -0
- package/dist/tokenization/keyword.d.ts.map +1 -1
- package/dist/tokenization/keyword.js +1 -0
- package/dist/tokenization/keyword.js.map +1 -1
- package/dist/tokenization/token.d.ts +4 -0
- package/dist/tokenization/token.d.ts.map +1 -1
- package/dist/tokenization/token.js +14 -1
- package/dist/tokenization/token.js.map +1 -1
- package/dist/tokenization/token_type.d.ts +1 -0
- package/dist/tokenization/token_type.d.ts.map +1 -1
- package/dist/tokenization/token_type.js +1 -0
- package/dist/tokenization/token_type.js.map +1 -1
- package/dist/tokenization/tokenizer.d.ts +2 -1
- package/dist/tokenization/tokenizer.d.ts.map +1 -1
- package/dist/tokenization/tokenizer.js +25 -12
- package/dist/tokenization/tokenizer.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/README.md +166 -0
- package/flowquery-py/pyproject.toml +75 -0
- package/flowquery-py/setup_env.ps1 +92 -0
- package/flowquery-py/setup_env.sh +87 -0
- package/flowquery-py/src/__init__.py +34 -0
- package/flowquery-py/src/__main__.py +10 -0
- package/flowquery-py/src/compute/__init__.py +5 -0
- package/flowquery-py/src/compute/runner.py +60 -0
- package/flowquery-py/src/extensibility.py +52 -0
- package/flowquery-py/src/graph/__init__.py +31 -0
- package/flowquery-py/src/graph/data.py +118 -0
- package/flowquery-py/src/graph/database.py +82 -0
- package/flowquery-py/src/graph/hops.py +43 -0
- package/flowquery-py/src/graph/node.py +112 -0
- package/flowquery-py/src/graph/node_data.py +26 -0
- package/flowquery-py/src/graph/node_reference.py +49 -0
- package/flowquery-py/src/graph/pattern.py +125 -0
- package/flowquery-py/src/graph/pattern_expression.py +62 -0
- package/flowquery-py/src/graph/patterns.py +42 -0
- package/flowquery-py/src/graph/physical_node.py +40 -0
- package/flowquery-py/src/graph/physical_relationship.py +36 -0
- package/flowquery-py/src/graph/relationship.py +135 -0
- package/flowquery-py/src/graph/relationship_data.py +33 -0
- package/flowquery-py/src/graph/relationship_match_collector.py +77 -0
- package/flowquery-py/src/graph/relationship_reference.py +21 -0
- package/flowquery-py/src/io/__init__.py +5 -0
- package/flowquery-py/src/io/command_line.py +67 -0
- package/flowquery-py/src/parsing/__init__.py +17 -0
- package/flowquery-py/src/parsing/alias.py +20 -0
- package/flowquery-py/src/parsing/alias_option.py +11 -0
- package/flowquery-py/src/parsing/ast_node.py +146 -0
- package/flowquery-py/src/parsing/base_parser.py +84 -0
- package/flowquery-py/src/parsing/components/__init__.py +19 -0
- package/flowquery-py/src/parsing/components/csv.py +8 -0
- package/flowquery-py/src/parsing/components/from_.py +10 -0
- package/flowquery-py/src/parsing/components/headers.py +12 -0
- package/flowquery-py/src/parsing/components/json.py +8 -0
- package/flowquery-py/src/parsing/components/null.py +10 -0
- package/flowquery-py/src/parsing/components/post.py +8 -0
- package/flowquery-py/src/parsing/components/text.py +8 -0
- package/flowquery-py/src/parsing/context.py +50 -0
- package/flowquery-py/src/parsing/data_structures/__init__.py +15 -0
- package/flowquery-py/src/parsing/data_structures/associative_array.py +41 -0
- package/flowquery-py/src/parsing/data_structures/json_array.py +30 -0
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +38 -0
- package/flowquery-py/src/parsing/data_structures/lookup.py +49 -0
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +42 -0
- package/flowquery-py/src/parsing/expressions/__init__.py +57 -0
- package/flowquery-py/src/parsing/expressions/boolean.py +20 -0
- package/flowquery-py/src/parsing/expressions/expression.py +138 -0
- package/flowquery-py/src/parsing/expressions/expression_map.py +26 -0
- package/flowquery-py/src/parsing/expressions/f_string.py +27 -0
- package/flowquery-py/src/parsing/expressions/identifier.py +20 -0
- package/flowquery-py/src/parsing/expressions/number.py +32 -0
- package/flowquery-py/src/parsing/expressions/operator.py +169 -0
- package/flowquery-py/src/parsing/expressions/reference.py +47 -0
- package/flowquery-py/src/parsing/expressions/string.py +27 -0
- package/flowquery-py/src/parsing/functions/__init__.py +75 -0
- package/flowquery-py/src/parsing/functions/aggregate_function.py +60 -0
- package/flowquery-py/src/parsing/functions/async_function.py +62 -0
- package/flowquery-py/src/parsing/functions/avg.py +55 -0
- package/flowquery-py/src/parsing/functions/collect.py +75 -0
- package/flowquery-py/src/parsing/functions/function.py +68 -0
- package/flowquery-py/src/parsing/functions/function_factory.py +173 -0
- package/flowquery-py/src/parsing/functions/function_metadata.py +149 -0
- package/flowquery-py/src/parsing/functions/functions.py +59 -0
- package/flowquery-py/src/parsing/functions/join.py +47 -0
- package/flowquery-py/src/parsing/functions/keys.py +34 -0
- package/flowquery-py/src/parsing/functions/predicate_function.py +46 -0
- package/flowquery-py/src/parsing/functions/predicate_sum.py +47 -0
- package/flowquery-py/src/parsing/functions/rand.py +28 -0
- package/flowquery-py/src/parsing/functions/range_.py +34 -0
- package/flowquery-py/src/parsing/functions/reducer_element.py +15 -0
- package/flowquery-py/src/parsing/functions/replace.py +37 -0
- package/flowquery-py/src/parsing/functions/round_.py +32 -0
- package/flowquery-py/src/parsing/functions/size.py +32 -0
- package/flowquery-py/src/parsing/functions/split.py +47 -0
- package/flowquery-py/src/parsing/functions/stringify.py +47 -0
- package/flowquery-py/src/parsing/functions/sum.py +51 -0
- package/flowquery-py/src/parsing/functions/to_json.py +33 -0
- package/flowquery-py/src/parsing/functions/type_.py +47 -0
- package/flowquery-py/src/parsing/functions/value_holder.py +24 -0
- package/flowquery-py/src/parsing/logic/__init__.py +15 -0
- package/flowquery-py/src/parsing/logic/case.py +29 -0
- package/flowquery-py/src/parsing/logic/else_.py +12 -0
- package/flowquery-py/src/parsing/logic/end.py +8 -0
- package/flowquery-py/src/parsing/logic/then.py +12 -0
- package/flowquery-py/src/parsing/logic/when.py +10 -0
- package/flowquery-py/src/parsing/operations/__init__.py +35 -0
- package/flowquery-py/src/parsing/operations/aggregated_return.py +24 -0
- package/flowquery-py/src/parsing/operations/aggregated_with.py +22 -0
- package/flowquery-py/src/parsing/operations/call.py +74 -0
- package/flowquery-py/src/parsing/operations/create_node.py +34 -0
- package/flowquery-py/src/parsing/operations/create_relationship.py +34 -0
- package/flowquery-py/src/parsing/operations/group_by.py +130 -0
- package/flowquery-py/src/parsing/operations/limit.py +22 -0
- package/flowquery-py/src/parsing/operations/load.py +140 -0
- package/flowquery-py/src/parsing/operations/match.py +29 -0
- package/flowquery-py/src/parsing/operations/operation.py +69 -0
- package/flowquery-py/src/parsing/operations/projection.py +21 -0
- package/flowquery-py/src/parsing/operations/return_op.py +50 -0
- package/flowquery-py/src/parsing/operations/unwind.py +37 -0
- package/flowquery-py/src/parsing/operations/where.py +41 -0
- package/flowquery-py/src/parsing/operations/with_op.py +18 -0
- package/flowquery-py/src/parsing/parser.py +1011 -0
- package/flowquery-py/src/parsing/token_to_node.py +109 -0
- package/flowquery-py/src/tokenization/__init__.py +23 -0
- package/flowquery-py/src/tokenization/keyword.py +48 -0
- package/flowquery-py/src/tokenization/operator.py +29 -0
- package/flowquery-py/src/tokenization/string_walker.py +158 -0
- package/flowquery-py/src/tokenization/symbol.py +19 -0
- package/flowquery-py/src/tokenization/token.py +659 -0
- package/flowquery-py/src/tokenization/token_mapper.py +52 -0
- package/flowquery-py/src/tokenization/token_type.py +21 -0
- package/flowquery-py/src/tokenization/tokenizer.py +214 -0
- package/flowquery-py/src/tokenization/trie.py +124 -0
- package/flowquery-py/src/utils/__init__.py +6 -0
- package/flowquery-py/src/utils/object_utils.py +20 -0
- package/flowquery-py/src/utils/string_utils.py +113 -0
- package/flowquery-py/tests/__init__.py +1 -0
- package/flowquery-py/tests/compute/__init__.py +1 -0
- package/flowquery-py/tests/compute/test_runner.py +1335 -0
- package/flowquery-py/tests/graph/__init__.py +1 -0
- package/flowquery-py/tests/graph/test_create.py +56 -0
- package/flowquery-py/tests/graph/test_data.py +73 -0
- package/flowquery-py/tests/graph/test_match.py +40 -0
- package/flowquery-py/tests/parsing/__init__.py +1 -0
- package/flowquery-py/tests/parsing/test_context.py +34 -0
- package/flowquery-py/tests/parsing/test_expression.py +49 -0
- package/flowquery-py/tests/parsing/test_parser.py +674 -0
- package/flowquery-py/tests/test_extensibility.py +611 -0
- package/flowquery-py/tests/tokenization/__init__.py +1 -0
- package/flowquery-py/tests/tokenization/test_token_mapper.py +60 -0
- package/flowquery-py/tests/tokenization/test_tokenizer.py +164 -0
- package/flowquery-py/tests/tokenization/test_trie.py +30 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/package.json +1 -1
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +76 -8
- package/misc/apps/RAG/src/components/index.ts +19 -10
- package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +21 -26
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +24 -25
- package/misc/apps/RAG/src/plugins/loaders/Form.ts +163 -147
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +103 -90
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +80 -130
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +104 -101
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +47 -36
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +89 -78
- package/package.json +1 -1
- package/src/compute/runner.ts +24 -19
- package/src/graph/data.ts +112 -0
- package/src/graph/database.ts +63 -0
- package/src/graph/hops.ts +22 -0
- package/src/graph/node.ts +99 -0
- package/src/graph/node_data.ts +18 -0
- package/src/graph/node_reference.ts +33 -0
- package/src/graph/pattern.ts +101 -0
- package/src/graph/pattern_expression.ts +37 -0
- package/src/graph/patterns.ts +36 -0
- package/src/graph/physical_node.ts +23 -0
- package/src/graph/physical_relationship.ts +23 -0
- package/src/graph/relationship.ts +116 -0
- package/src/graph/relationship_data.ts +27 -0
- package/src/graph/relationship_match_collector.ts +58 -0
- package/src/graph/relationship_reference.ts +24 -0
- package/src/parsing/base_parser.ts +20 -14
- package/src/parsing/context.ts +14 -14
- package/src/parsing/expressions/boolean.ts +21 -0
- package/src/parsing/expressions/expression.ts +34 -26
- package/src/parsing/expressions/expression_map.ts +3 -0
- package/src/parsing/expressions/operator.ts +19 -1
- package/src/parsing/functions/function_factory.ts +45 -45
- package/src/parsing/operations/call.ts +3 -1
- package/src/parsing/operations/create_node.ts +39 -0
- package/src/parsing/operations/create_relationship.ts +38 -0
- package/src/parsing/operations/match.ts +31 -0
- package/src/parsing/operations/operation.ts +3 -0
- package/src/parsing/operations/return.ts +11 -7
- package/src/parsing/operations/where.ts +10 -6
- package/src/parsing/parser.ts +346 -8
- package/src/parsing/token_to_node.ts +6 -0
- package/src/tokenization/keyword.ts +41 -40
- package/src/tokenization/token.ts +21 -1
- package/src/tokenization/token_type.ts +2 -1
- package/src/tokenization/tokenizer.ts +52 -31
- package/tests/compute/runner.test.ts +660 -6
- package/tests/extensibility.test.ts +97 -93
- package/tests/graph/create.test.ts +36 -0
- package/tests/graph/data.test.ts +58 -0
- package/tests/graph/match.test.ts +29 -0
- package/tests/parsing/parser.test.ts +276 -8
- package/tests/tokenization/tokenizer.test.ts +107 -17
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
"""Tests for the FlowQuery parser."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from typing import AsyncIterator
|
|
5
|
+
from flowquery.parsing.parser import Parser
|
|
6
|
+
from flowquery.parsing.functions.async_function import AsyncFunction
|
|
7
|
+
from flowquery.parsing.functions.function_metadata import FunctionDef
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Test async function for CALL operation parsing test
|
|
11
|
+
# Named with underscore prefix to prevent pytest from trying to collect it as a test class
|
|
12
|
+
@FunctionDef({
|
|
13
|
+
"description": "Asynchronous function for testing CALL operation",
|
|
14
|
+
"category": "async",
|
|
15
|
+
"parameters": [],
|
|
16
|
+
"output": {"description": "Yields test values", "type": "any"},
|
|
17
|
+
})
|
|
18
|
+
class _Test(AsyncFunction):
|
|
19
|
+
"""Async function for CALL operation testing, registered as 'test'."""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
super().__init__("test") # Register as 'test'
|
|
23
|
+
self._expected_parameter_count = 0
|
|
24
|
+
|
|
25
|
+
async def generate(self) -> AsyncIterator:
|
|
26
|
+
yield 1
|
|
27
|
+
yield 2
|
|
28
|
+
yield 3
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestParser:
|
|
32
|
+
"""Test cases for the Parser class."""
|
|
33
|
+
|
|
34
|
+
def test_parser_basic(self):
|
|
35
|
+
"""Test basic parser functionality."""
|
|
36
|
+
parser = Parser()
|
|
37
|
+
ast = parser.parse("RETURN 1, 2, 3")
|
|
38
|
+
expected = (
|
|
39
|
+
"ASTNode\n"
|
|
40
|
+
"- Return\n"
|
|
41
|
+
"-- Expression\n"
|
|
42
|
+
"--- Number (1)\n"
|
|
43
|
+
"-- Expression\n"
|
|
44
|
+
"--- Number (2)\n"
|
|
45
|
+
"-- Expression\n"
|
|
46
|
+
"--- Number (3)"
|
|
47
|
+
)
|
|
48
|
+
assert ast.print() == expected
|
|
49
|
+
|
|
50
|
+
def test_parser_with_function(self):
|
|
51
|
+
"""Test parser with function."""
|
|
52
|
+
parser = Parser()
|
|
53
|
+
ast = parser.parse("RETURN rand()")
|
|
54
|
+
expected = (
|
|
55
|
+
"ASTNode\n"
|
|
56
|
+
"- Return\n"
|
|
57
|
+
"-- Expression\n"
|
|
58
|
+
"--- Function (rand)"
|
|
59
|
+
)
|
|
60
|
+
assert ast.print() == expected
|
|
61
|
+
|
|
62
|
+
def test_parser_with_associative_array(self):
|
|
63
|
+
"""Test parser with associative array."""
|
|
64
|
+
parser = Parser()
|
|
65
|
+
ast = parser.parse("RETURN {a: 1, b: 2}")
|
|
66
|
+
expected = (
|
|
67
|
+
"ASTNode\n"
|
|
68
|
+
"- Return\n"
|
|
69
|
+
"-- Expression\n"
|
|
70
|
+
"--- AssociativeArray\n"
|
|
71
|
+
"---- KeyValuePair\n"
|
|
72
|
+
"----- String (a)\n"
|
|
73
|
+
"----- Expression\n"
|
|
74
|
+
"------ Number (1)\n"
|
|
75
|
+
"---- KeyValuePair\n"
|
|
76
|
+
"----- String (b)\n"
|
|
77
|
+
"----- Expression\n"
|
|
78
|
+
"------ Number (2)"
|
|
79
|
+
)
|
|
80
|
+
assert ast.print() == expected
|
|
81
|
+
|
|
82
|
+
def test_parser_with_json_array(self):
|
|
83
|
+
"""Test parser with JSON array."""
|
|
84
|
+
parser = Parser()
|
|
85
|
+
ast = parser.parse("RETURN [1, 2]")
|
|
86
|
+
expected = (
|
|
87
|
+
"ASTNode\n"
|
|
88
|
+
"- Return\n"
|
|
89
|
+
"-- Expression\n"
|
|
90
|
+
"--- JSONArray\n"
|
|
91
|
+
"---- Expression\n"
|
|
92
|
+
"----- Number (1)\n"
|
|
93
|
+
"---- Expression\n"
|
|
94
|
+
"----- Number (2)"
|
|
95
|
+
)
|
|
96
|
+
assert ast.print() == expected
|
|
97
|
+
|
|
98
|
+
def test_parser_with_nested_associative_array(self):
|
|
99
|
+
"""Test parser with nested associative array."""
|
|
100
|
+
parser = Parser()
|
|
101
|
+
ast = parser.parse("RETURN {a:{}}")
|
|
102
|
+
expected = (
|
|
103
|
+
"ASTNode\n"
|
|
104
|
+
"- Return\n"
|
|
105
|
+
"-- Expression\n"
|
|
106
|
+
"--- AssociativeArray\n"
|
|
107
|
+
"---- KeyValuePair\n"
|
|
108
|
+
"----- String (a)\n"
|
|
109
|
+
"----- Expression\n"
|
|
110
|
+
"------ AssociativeArray"
|
|
111
|
+
)
|
|
112
|
+
assert ast.print() == expected
|
|
113
|
+
|
|
114
|
+
def test_parser_with_multiple_operations(self):
|
|
115
|
+
"""Test parser with multiple operations."""
|
|
116
|
+
parser = Parser()
|
|
117
|
+
ast = parser.parse("WITH 1 AS n RETURN n")
|
|
118
|
+
expected = (
|
|
119
|
+
"ASTNode\n"
|
|
120
|
+
"- With\n"
|
|
121
|
+
"-- Expression (n)\n"
|
|
122
|
+
"--- Number (1)\n"
|
|
123
|
+
"- Return\n"
|
|
124
|
+
"-- Expression (n)\n"
|
|
125
|
+
"--- Reference (n)"
|
|
126
|
+
)
|
|
127
|
+
assert ast.print() == expected
|
|
128
|
+
|
|
129
|
+
def test_parser_with_comments(self):
|
|
130
|
+
"""Test parser with comments."""
|
|
131
|
+
parser = Parser()
|
|
132
|
+
ast = parser.parse("WITH 1 AS n /* comment */ RETURN n")
|
|
133
|
+
expected = (
|
|
134
|
+
"ASTNode\n"
|
|
135
|
+
"- With\n"
|
|
136
|
+
"-- Expression (n)\n"
|
|
137
|
+
"--- Number (1)\n"
|
|
138
|
+
"- Return\n"
|
|
139
|
+
"-- Expression (n)\n"
|
|
140
|
+
"--- Reference (n)"
|
|
141
|
+
)
|
|
142
|
+
assert ast.print() == expected
|
|
143
|
+
|
|
144
|
+
def test_parser_with_unwind(self):
|
|
145
|
+
"""Test parser with UNWIND."""
|
|
146
|
+
parser = Parser()
|
|
147
|
+
ast = parser.parse("UNWIND [1, 2, 3] AS n RETURN n")
|
|
148
|
+
expected = (
|
|
149
|
+
"ASTNode\n"
|
|
150
|
+
"- Unwind\n"
|
|
151
|
+
"-- Expression (n)\n"
|
|
152
|
+
"--- JSONArray\n"
|
|
153
|
+
"---- Expression\n"
|
|
154
|
+
"----- Number (1)\n"
|
|
155
|
+
"---- Expression\n"
|
|
156
|
+
"----- Number (2)\n"
|
|
157
|
+
"---- Expression\n"
|
|
158
|
+
"----- Number (3)\n"
|
|
159
|
+
"- Return\n"
|
|
160
|
+
"-- Expression (n)\n"
|
|
161
|
+
"--- Reference (n)"
|
|
162
|
+
)
|
|
163
|
+
assert ast.print() == expected
|
|
164
|
+
|
|
165
|
+
def test_unwind_with_invalid_expression(self):
|
|
166
|
+
"""Test Unwind with invalid expression."""
|
|
167
|
+
parser = Parser()
|
|
168
|
+
with pytest.raises(Exception, match="Expected array, function, reference, or lookup"):
|
|
169
|
+
parser.parse("UNWIND 1 AS n RETURN n")
|
|
170
|
+
|
|
171
|
+
def test_unwind_with_invalid_alias(self):
|
|
172
|
+
"""Test Unwind with invalid alias."""
|
|
173
|
+
parser = Parser()
|
|
174
|
+
with pytest.raises(Exception, match="Expected identifier"):
|
|
175
|
+
parser.parse("UNWIND [1, 2, 3] AS 1 RETURN n")
|
|
176
|
+
|
|
177
|
+
def test_unwind_with_missing_alias(self):
|
|
178
|
+
"""Test Unwind with missing alias."""
|
|
179
|
+
parser = Parser()
|
|
180
|
+
with pytest.raises(Exception, match="Expected alias"):
|
|
181
|
+
parser.parse("UNWIND [1, 2, 3] RETURN n")
|
|
182
|
+
|
|
183
|
+
def test_statement_with_where_clause(self):
|
|
184
|
+
"""Test statement with where clause."""
|
|
185
|
+
parser = Parser()
|
|
186
|
+
ast = parser.parse("with 1 as n where n > 0 return n")
|
|
187
|
+
expected = (
|
|
188
|
+
"ASTNode\n"
|
|
189
|
+
"- With\n"
|
|
190
|
+
"-- Expression (n)\n"
|
|
191
|
+
"--- Number (1)\n"
|
|
192
|
+
"- Where\n"
|
|
193
|
+
"-- Expression\n"
|
|
194
|
+
"--- GreaterThan\n"
|
|
195
|
+
"---- Reference (n)\n"
|
|
196
|
+
"---- Number (0)\n"
|
|
197
|
+
"- Return\n"
|
|
198
|
+
"-- Expression (n)\n"
|
|
199
|
+
"--- Reference (n)"
|
|
200
|
+
)
|
|
201
|
+
assert ast.print() == expected
|
|
202
|
+
|
|
203
|
+
def test_lookup(self):
|
|
204
|
+
"""Test lookup expression."""
|
|
205
|
+
parser = Parser()
|
|
206
|
+
ast = parser.parse("return {a: 1}.a")
|
|
207
|
+
expected = (
|
|
208
|
+
"ASTNode\n"
|
|
209
|
+
"- Return\n"
|
|
210
|
+
"-- Expression\n"
|
|
211
|
+
"--- Lookup\n"
|
|
212
|
+
"---- Identifier (a)\n"
|
|
213
|
+
"---- AssociativeArray\n"
|
|
214
|
+
"----- KeyValuePair\n"
|
|
215
|
+
"------ String (a)\n"
|
|
216
|
+
"------ Expression\n"
|
|
217
|
+
"------- Number (1)"
|
|
218
|
+
)
|
|
219
|
+
assert ast.print() == expected
|
|
220
|
+
|
|
221
|
+
def test_lookup_as_part_of_expression(self):
|
|
222
|
+
"""Test lookup as part of expression."""
|
|
223
|
+
parser = Parser()
|
|
224
|
+
ast = parser.parse("return {a: 1}.a + 1")
|
|
225
|
+
expected = (
|
|
226
|
+
"ASTNode\n"
|
|
227
|
+
"- Return\n"
|
|
228
|
+
"-- Expression\n"
|
|
229
|
+
"--- Add\n"
|
|
230
|
+
"---- Lookup\n"
|
|
231
|
+
"----- Identifier (a)\n"
|
|
232
|
+
"----- AssociativeArray\n"
|
|
233
|
+
"------ KeyValuePair\n"
|
|
234
|
+
"------- String (a)\n"
|
|
235
|
+
"------- Expression\n"
|
|
236
|
+
"-------- Number (1)\n"
|
|
237
|
+
"---- Number (1)"
|
|
238
|
+
)
|
|
239
|
+
assert ast.print() == expected
|
|
240
|
+
|
|
241
|
+
def test_lookup_with_nested_associative_array(self):
|
|
242
|
+
"""Test lookup with nested associative array."""
|
|
243
|
+
parser = Parser()
|
|
244
|
+
ast = parser.parse("return {a: {b: 1}}.a.b")
|
|
245
|
+
expected = (
|
|
246
|
+
"ASTNode\n"
|
|
247
|
+
"- Return\n"
|
|
248
|
+
"-- Expression\n"
|
|
249
|
+
"--- Lookup\n"
|
|
250
|
+
"---- Identifier (b)\n"
|
|
251
|
+
"---- Lookup\n"
|
|
252
|
+
"----- Identifier (a)\n"
|
|
253
|
+
"----- AssociativeArray\n"
|
|
254
|
+
"------ KeyValuePair\n"
|
|
255
|
+
"------- String (a)\n"
|
|
256
|
+
"------- Expression\n"
|
|
257
|
+
"-------- AssociativeArray\n"
|
|
258
|
+
"--------- KeyValuePair\n"
|
|
259
|
+
"---------- String (b)\n"
|
|
260
|
+
"---------- Expression\n"
|
|
261
|
+
"----------- Number (1)"
|
|
262
|
+
)
|
|
263
|
+
assert ast.print() == expected
|
|
264
|
+
_return = ast.first_child()
|
|
265
|
+
assert _return.first_child().value() == 1
|
|
266
|
+
|
|
267
|
+
def test_lookup_with_json_array(self):
|
|
268
|
+
"""Test lookup with JSON array."""
|
|
269
|
+
parser = Parser()
|
|
270
|
+
ast = parser.parse("return [1, 2][1]")
|
|
271
|
+
expected = (
|
|
272
|
+
"ASTNode\n"
|
|
273
|
+
"- Return\n"
|
|
274
|
+
"-- Expression\n"
|
|
275
|
+
"--- Lookup\n"
|
|
276
|
+
"---- Expression\n"
|
|
277
|
+
"----- Number (1)\n"
|
|
278
|
+
"---- JSONArray\n"
|
|
279
|
+
"----- Expression\n"
|
|
280
|
+
"------ Number (1)\n"
|
|
281
|
+
"----- Expression\n"
|
|
282
|
+
"------ Number (2)"
|
|
283
|
+
)
|
|
284
|
+
assert ast.print() == expected
|
|
285
|
+
_return = ast.first_child()
|
|
286
|
+
assert _return.first_child().value() == 2
|
|
287
|
+
|
|
288
|
+
def test_load_with_post(self):
|
|
289
|
+
"""Test load with post."""
|
|
290
|
+
parser = Parser()
|
|
291
|
+
ast = parser.parse(
|
|
292
|
+
'load json from "https://jsonplaceholder.typicode.com/posts" post {userId: 1} as data return data'
|
|
293
|
+
)
|
|
294
|
+
expected = (
|
|
295
|
+
"ASTNode\n"
|
|
296
|
+
"- Load\n"
|
|
297
|
+
"-- JSON\n"
|
|
298
|
+
"-- From\n"
|
|
299
|
+
"--- Expression\n"
|
|
300
|
+
"---- String (https://jsonplaceholder.typicode.com/posts)\n"
|
|
301
|
+
"-- Post\n"
|
|
302
|
+
"--- Expression\n"
|
|
303
|
+
"---- AssociativeArray\n"
|
|
304
|
+
"----- KeyValuePair\n"
|
|
305
|
+
"------ String (userId)\n"
|
|
306
|
+
"------ Expression\n"
|
|
307
|
+
"------- Number (1)\n"
|
|
308
|
+
"-- Alias (data)\n"
|
|
309
|
+
"- Return\n"
|
|
310
|
+
"-- Expression (data)\n"
|
|
311
|
+
"--- Reference (data)"
|
|
312
|
+
)
|
|
313
|
+
assert ast.print() == expected
|
|
314
|
+
|
|
315
|
+
def test_nested_aggregate_functions(self):
|
|
316
|
+
"""Test nested aggregate functions."""
|
|
317
|
+
parser = Parser()
|
|
318
|
+
with pytest.raises(Exception, match="Aggregate functions cannot be nested"):
|
|
319
|
+
parser.parse("RETURN sum(sum(1))")
|
|
320
|
+
|
|
321
|
+
def test_with_and_return_with_renamed_variable(self):
|
|
322
|
+
"""Test with and return with renamed variable."""
|
|
323
|
+
parser = Parser()
|
|
324
|
+
ast = parser.parse("WITH 1 AS n RETURN n AS m")
|
|
325
|
+
expected = (
|
|
326
|
+
"ASTNode\n"
|
|
327
|
+
"- With\n"
|
|
328
|
+
"-- Expression (n)\n"
|
|
329
|
+
"--- Number (1)\n"
|
|
330
|
+
"- Return\n"
|
|
331
|
+
"-- Expression (m)\n"
|
|
332
|
+
"--- Reference (n)"
|
|
333
|
+
)
|
|
334
|
+
assert ast.print() == expected
|
|
335
|
+
|
|
336
|
+
def test_with_and_return_with_variable_lookup(self):
|
|
337
|
+
"""Test with and return with variable lookup."""
|
|
338
|
+
parser = Parser()
|
|
339
|
+
ast = parser.parse("WITH {a: n} AS obj RETURN obj.a")
|
|
340
|
+
expected = (
|
|
341
|
+
"ASTNode\n"
|
|
342
|
+
"- With\n"
|
|
343
|
+
"-- Expression (obj)\n"
|
|
344
|
+
"--- AssociativeArray\n"
|
|
345
|
+
"---- KeyValuePair\n"
|
|
346
|
+
"----- String (a)\n"
|
|
347
|
+
"----- Expression\n"
|
|
348
|
+
"------ Reference (n)\n"
|
|
349
|
+
"- Return\n"
|
|
350
|
+
"-- Expression\n"
|
|
351
|
+
"--- Lookup\n"
|
|
352
|
+
"---- Identifier (a)\n"
|
|
353
|
+
"---- Reference (obj)"
|
|
354
|
+
)
|
|
355
|
+
assert ast.print() == expected
|
|
356
|
+
|
|
357
|
+
def test_unwind(self):
|
|
358
|
+
"""Test unwind."""
|
|
359
|
+
parser = Parser()
|
|
360
|
+
ast = parser.parse("WITH [1, 2, 4] as n unwind n as i return i")
|
|
361
|
+
expected = (
|
|
362
|
+
"ASTNode\n"
|
|
363
|
+
"- With\n"
|
|
364
|
+
"-- Expression (n)\n"
|
|
365
|
+
"--- JSONArray\n"
|
|
366
|
+
"---- Expression\n"
|
|
367
|
+
"----- Number (1)\n"
|
|
368
|
+
"---- Expression\n"
|
|
369
|
+
"----- Number (2)\n"
|
|
370
|
+
"---- Expression\n"
|
|
371
|
+
"----- Number (4)\n"
|
|
372
|
+
"- Unwind\n"
|
|
373
|
+
"-- Expression (i)\n"
|
|
374
|
+
"--- Reference (n)\n"
|
|
375
|
+
"- Return\n"
|
|
376
|
+
"-- Expression (i)\n"
|
|
377
|
+
"--- Reference (i)"
|
|
378
|
+
)
|
|
379
|
+
assert ast.print() == expected
|
|
380
|
+
|
|
381
|
+
def test_predicate_function(self):
|
|
382
|
+
"""Test predicate function."""
|
|
383
|
+
parser = Parser()
|
|
384
|
+
ast = parser.parse("RETURN sum(n in [1, 2, 3] | n where n > 1)")
|
|
385
|
+
expected = (
|
|
386
|
+
"ASTNode\n"
|
|
387
|
+
"- Return\n"
|
|
388
|
+
"-- Expression\n"
|
|
389
|
+
"--- PredicateFunction (sum)\n"
|
|
390
|
+
"---- Reference (n)\n"
|
|
391
|
+
"---- Expression\n"
|
|
392
|
+
"----- JSONArray\n"
|
|
393
|
+
"------ Expression\n"
|
|
394
|
+
"------- Number (1)\n"
|
|
395
|
+
"------ Expression\n"
|
|
396
|
+
"------- Number (2)\n"
|
|
397
|
+
"------ Expression\n"
|
|
398
|
+
"------- Number (3)\n"
|
|
399
|
+
"---- Expression\n"
|
|
400
|
+
"----- Reference (n)\n"
|
|
401
|
+
"---- Where\n"
|
|
402
|
+
"----- Expression\n"
|
|
403
|
+
"------ GreaterThan\n"
|
|
404
|
+
"------- Reference (n)\n"
|
|
405
|
+
"------- Number (1)"
|
|
406
|
+
)
|
|
407
|
+
assert ast.print() == expected
|
|
408
|
+
|
|
409
|
+
def test_case_statement(self):
|
|
410
|
+
"""Test case statement."""
|
|
411
|
+
parser = Parser()
|
|
412
|
+
ast = parser.parse("RETURN CASE WHEN 1 THEN 2 ELSE 3 END")
|
|
413
|
+
expected = (
|
|
414
|
+
"ASTNode\n"
|
|
415
|
+
"- Return\n"
|
|
416
|
+
"-- Expression\n"
|
|
417
|
+
"--- Case\n"
|
|
418
|
+
"---- When\n"
|
|
419
|
+
"----- Expression\n"
|
|
420
|
+
"------ Number (1)\n"
|
|
421
|
+
"---- Then\n"
|
|
422
|
+
"----- Expression\n"
|
|
423
|
+
"------ Number (2)\n"
|
|
424
|
+
"---- Else\n"
|
|
425
|
+
"----- Expression\n"
|
|
426
|
+
"------ Number (3)"
|
|
427
|
+
)
|
|
428
|
+
assert ast.print() == expected
|
|
429
|
+
|
|
430
|
+
def test_functions_with_wrong_number_of_arguments(self):
|
|
431
|
+
"""Test functions with wrong number of arguments."""
|
|
432
|
+
parser = Parser()
|
|
433
|
+
with pytest.raises(Exception, match="Function range expected 2 parameters, but got 1"):
|
|
434
|
+
parser.parse("RETURN range(1)")
|
|
435
|
+
with pytest.raises(Exception, match="Function range expected 2 parameters, but got 3"):
|
|
436
|
+
parser.parse("RETURN range(1, 2, 3)")
|
|
437
|
+
with pytest.raises(Exception, match="Function avg expected 1 parameters, but got 3"):
|
|
438
|
+
parser.parse("RETURN avg(1, 2, 3)")
|
|
439
|
+
with pytest.raises(Exception, match="Function size expected 1 parameters, but got 2"):
|
|
440
|
+
parser.parse("RETURN size(1, 2)")
|
|
441
|
+
with pytest.raises(Exception, match="Function round expected 1 parameters, but got 2"):
|
|
442
|
+
parser.parse("RETURN round(1, 2)")
|
|
443
|
+
|
|
444
|
+
def test_non_well_formed_statements(self):
|
|
445
|
+
"""Test non-well formed statements."""
|
|
446
|
+
parser = Parser()
|
|
447
|
+
with pytest.raises(Exception, match="Only one RETURN statement is allowed"):
|
|
448
|
+
parser.parse("return 1 return 1")
|
|
449
|
+
# Note: Python implementation throws "Only one RETURN" for this case too
|
|
450
|
+
with pytest.raises(Exception, match="Only one RETURN statement is allowed"):
|
|
451
|
+
parser.parse("return 1 with 1 as n")
|
|
452
|
+
|
|
453
|
+
def test_associative_array_with_backtick_string(self):
|
|
454
|
+
"""Test associative array with backtick string."""
|
|
455
|
+
parser = Parser()
|
|
456
|
+
ast = parser.parse("RETURN {`key`: `value`}")
|
|
457
|
+
expected = (
|
|
458
|
+
"ASTNode\n"
|
|
459
|
+
"- Return\n"
|
|
460
|
+
"-- Expression\n"
|
|
461
|
+
"--- AssociativeArray\n"
|
|
462
|
+
"---- KeyValuePair\n"
|
|
463
|
+
"----- String (key)\n"
|
|
464
|
+
"----- Expression\n"
|
|
465
|
+
"------ Reference (value)"
|
|
466
|
+
)
|
|
467
|
+
assert ast.print() == expected
|
|
468
|
+
|
|
469
|
+
def test_limit(self):
|
|
470
|
+
"""Test limit."""
|
|
471
|
+
parser = Parser()
|
|
472
|
+
ast = parser.parse("unwind range(1, 10) as n limit 5 return n")
|
|
473
|
+
expected = (
|
|
474
|
+
"ASTNode\n"
|
|
475
|
+
"- Unwind\n"
|
|
476
|
+
"-- Expression (n)\n"
|
|
477
|
+
"--- Function (range)\n"
|
|
478
|
+
"---- Expression\n"
|
|
479
|
+
"----- Number (1)\n"
|
|
480
|
+
"---- Expression\n"
|
|
481
|
+
"----- Number (10)\n"
|
|
482
|
+
"- Limit\n"
|
|
483
|
+
"- Return\n"
|
|
484
|
+
"-- Expression (n)\n"
|
|
485
|
+
"--- Reference (n)"
|
|
486
|
+
)
|
|
487
|
+
assert ast.print() == expected
|
|
488
|
+
|
|
489
|
+
def test_return_negative_number(self):
|
|
490
|
+
"""Test return -2."""
|
|
491
|
+
parser = Parser()
|
|
492
|
+
ast = parser.parse("return -2")
|
|
493
|
+
expected = (
|
|
494
|
+
"ASTNode\n"
|
|
495
|
+
"- Return\n"
|
|
496
|
+
"-- Expression\n"
|
|
497
|
+
"--- Number (-2)"
|
|
498
|
+
)
|
|
499
|
+
assert ast.print() == expected
|
|
500
|
+
|
|
501
|
+
def test_call_operation(self):
|
|
502
|
+
"""Test call operation."""
|
|
503
|
+
parser = Parser()
|
|
504
|
+
ast = parser.parse("CALL test() YIELD result RETURN result")
|
|
505
|
+
expected = (
|
|
506
|
+
"ASTNode\n"
|
|
507
|
+
"- Call\n"
|
|
508
|
+
"-- Expression (result)\n"
|
|
509
|
+
"--- Reference (result)\n"
|
|
510
|
+
"- Return\n"
|
|
511
|
+
"-- Expression (result)\n"
|
|
512
|
+
"--- Reference (result)"
|
|
513
|
+
)
|
|
514
|
+
assert ast.print() == expected
|
|
515
|
+
|
|
516
|
+
def test_f_string(self):
|
|
517
|
+
"""Test f-string."""
|
|
518
|
+
parser = Parser()
|
|
519
|
+
ast = parser.parse("with 1 as value RETURN f'Value is: {value}.'")
|
|
520
|
+
expected = (
|
|
521
|
+
"ASTNode\n"
|
|
522
|
+
"- With\n"
|
|
523
|
+
"-- Expression (value)\n"
|
|
524
|
+
"--- Number (1)\n"
|
|
525
|
+
"- Return\n"
|
|
526
|
+
"-- Expression\n"
|
|
527
|
+
"--- FString\n"
|
|
528
|
+
"---- String (Value is: )\n"
|
|
529
|
+
"---- Expression\n"
|
|
530
|
+
"----- Reference (value)\n"
|
|
531
|
+
"---- String (.)"
|
|
532
|
+
)
|
|
533
|
+
assert ast.print() == expected
|
|
534
|
+
|
|
535
|
+
def test_not_equal_operator(self):
|
|
536
|
+
"""Test not equal operator."""
|
|
537
|
+
parser = Parser()
|
|
538
|
+
ast = parser.parse("RETURN 1 <> 2")
|
|
539
|
+
expected = (
|
|
540
|
+
"ASTNode\n"
|
|
541
|
+
"- Return\n"
|
|
542
|
+
"-- Expression\n"
|
|
543
|
+
"--- NotEquals\n"
|
|
544
|
+
"---- Number (1)\n"
|
|
545
|
+
"---- Number (2)"
|
|
546
|
+
)
|
|
547
|
+
assert ast.print() == expected
|
|
548
|
+
|
|
549
|
+
def test_equal_operator(self):
|
|
550
|
+
"""Test equal operator."""
|
|
551
|
+
parser = Parser()
|
|
552
|
+
ast = parser.parse("RETURN 1 = 2")
|
|
553
|
+
expected = (
|
|
554
|
+
"ASTNode\n"
|
|
555
|
+
"- Return\n"
|
|
556
|
+
"-- Expression\n"
|
|
557
|
+
"--- Equals\n"
|
|
558
|
+
"---- Number (1)\n"
|
|
559
|
+
"---- Number (2)"
|
|
560
|
+
)
|
|
561
|
+
assert ast.print() == expected
|
|
562
|
+
|
|
563
|
+
def test_not_operator(self):
|
|
564
|
+
"""Test not operator."""
|
|
565
|
+
parser = Parser()
|
|
566
|
+
ast = parser.parse("RETURN NOT true")
|
|
567
|
+
expected = (
|
|
568
|
+
"ASTNode\n"
|
|
569
|
+
"- Return\n"
|
|
570
|
+
"-- Expression\n"
|
|
571
|
+
"--- Not\n"
|
|
572
|
+
"---- Expression\n"
|
|
573
|
+
"----- Boolean"
|
|
574
|
+
)
|
|
575
|
+
assert ast.print() == expected
|
|
576
|
+
|
|
577
|
+
def test_create_node_operation(self):
|
|
578
|
+
"""Test create node operation."""
|
|
579
|
+
parser = Parser()
|
|
580
|
+
ast = parser.parse(
|
|
581
|
+
"""
|
|
582
|
+
CREATE VIRTUAL (:Person) AS {
|
|
583
|
+
unwind range(1, 3) AS id
|
|
584
|
+
return id, f'Person {id}' AS name
|
|
585
|
+
}
|
|
586
|
+
"""
|
|
587
|
+
)
|
|
588
|
+
expected = (
|
|
589
|
+
"ASTNode\n"
|
|
590
|
+
"- CreateNode"
|
|
591
|
+
)
|
|
592
|
+
assert ast.print() == expected
|
|
593
|
+
|
|
594
|
+
def test_match_operation(self):
|
|
595
|
+
"""Test match operation."""
|
|
596
|
+
parser = Parser()
|
|
597
|
+
ast = parser.parse("MATCH (n:Person) RETURN n")
|
|
598
|
+
expected = (
|
|
599
|
+
"ASTNode\n"
|
|
600
|
+
"- Match\n"
|
|
601
|
+
"- Return\n"
|
|
602
|
+
"-- Expression (n)\n"
|
|
603
|
+
"--- Reference (n)"
|
|
604
|
+
)
|
|
605
|
+
assert ast.print() == expected
|
|
606
|
+
|
|
607
|
+
def test_create_relationship_operation(self):
|
|
608
|
+
"""Test create relationship operation."""
|
|
609
|
+
parser = Parser()
|
|
610
|
+
ast = parser.parse(
|
|
611
|
+
"""
|
|
612
|
+
CREATE VIRTUAL (:Person)-[:KNOWS]-(:Person) AS {
|
|
613
|
+
unwind [
|
|
614
|
+
{from_id: 1, to_id: 2, since: '2020-01-01'},
|
|
615
|
+
{from_id: 2, to_id: 3, since: '2021-01-01'}
|
|
616
|
+
] AS pair
|
|
617
|
+
return pair.from_id AS left_id, pair.to_id AS right_id
|
|
618
|
+
}
|
|
619
|
+
"""
|
|
620
|
+
)
|
|
621
|
+
expected = (
|
|
622
|
+
"ASTNode\n"
|
|
623
|
+
"- CreateRelationship"
|
|
624
|
+
)
|
|
625
|
+
assert ast.print() == expected
|
|
626
|
+
|
|
627
|
+
def test_match_with_graph_pattern_including_relationships(self):
|
|
628
|
+
"""Test match with graph pattern including relationships."""
|
|
629
|
+
parser = Parser()
|
|
630
|
+
ast = parser.parse("MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a, b")
|
|
631
|
+
expected = (
|
|
632
|
+
"ASTNode\n"
|
|
633
|
+
"- Match\n"
|
|
634
|
+
"- Return\n"
|
|
635
|
+
"-- Expression (a)\n"
|
|
636
|
+
"--- Reference (a)\n"
|
|
637
|
+
"-- Expression (b)\n"
|
|
638
|
+
"--- Reference (b)"
|
|
639
|
+
)
|
|
640
|
+
assert ast.print() == expected
|
|
641
|
+
|
|
642
|
+
def test_parse_relationship_with_hops(self):
|
|
643
|
+
"""Test parse relationship with hops."""
|
|
644
|
+
parser = Parser()
|
|
645
|
+
ast = parser.parse("MATCH (a:Test)-[:KNOWS*1..3]->(b:Test) RETURN a, b")
|
|
646
|
+
expected = (
|
|
647
|
+
"ASTNode\n"
|
|
648
|
+
"- Match\n"
|
|
649
|
+
"- Return\n"
|
|
650
|
+
"-- Expression (a)\n"
|
|
651
|
+
"--- Reference (a)\n"
|
|
652
|
+
"-- Expression (b)\n"
|
|
653
|
+
"--- Reference (b)"
|
|
654
|
+
)
|
|
655
|
+
assert ast.print() == expected
|
|
656
|
+
|
|
657
|
+
def test_parse_statement_with_graph_pattern_in_where_clause(self):
|
|
658
|
+
"""Test parse statement with graph pattern in where clause."""
|
|
659
|
+
parser = Parser()
|
|
660
|
+
ast = parser.parse("MATCH (a:Person) WHERE (a)-[:KNOWS]->(:Person) RETURN a")
|
|
661
|
+
expected = (
|
|
662
|
+
"ASTNode\n"
|
|
663
|
+
"- Match\n"
|
|
664
|
+
"- Where\n"
|
|
665
|
+
"-- Expression\n"
|
|
666
|
+
"--- PatternExpression\n"
|
|
667
|
+
"---- NodeReference\n"
|
|
668
|
+
"---- Relationship\n"
|
|
669
|
+
"---- Node\n"
|
|
670
|
+
"- Return\n"
|
|
671
|
+
"-- Expression (a)\n"
|
|
672
|
+
"--- Reference (a)"
|
|
673
|
+
)
|
|
674
|
+
assert ast.print() == expected
|