flowquery 1.0.46 → 1.0.48
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/dist/flowquery.min.js +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/parsing/data_structures/list_comprehension.d.ts +56 -0
- package/dist/parsing/data_structures/list_comprehension.d.ts.map +1 -0
- package/dist/parsing/data_structures/list_comprehension.js +102 -0
- package/dist/parsing/data_structures/list_comprehension.js.map +1 -0
- package/dist/parsing/parser.d.ts +18 -0
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +92 -0
- package/dist/parsing/parser.js.map +1 -1
- package/package.json +4 -1
- package/.editorconfig +0 -21
- package/.gitattributes +0 -3
- package/.github/workflows/npm-publish.yml +0 -32
- package/.github/workflows/python-publish.yml +0 -143
- package/.github/workflows/release.yml +0 -107
- package/.husky/pre-commit +0 -28
- package/.prettierrc +0 -22
- package/CODE_OF_CONDUCT.md +0 -10
- package/FlowQueryLogoIcon.png +0 -0
- package/SECURITY.md +0 -14
- package/SUPPORT.md +0 -13
- package/docs/flowquery.min.js +0 -1
- package/docs/index.html +0 -105
- package/flowquery-py/CONTRIBUTING.md +0 -127
- package/flowquery-py/README.md +0 -67
- package/flowquery-py/misc/data/test.json +0 -10
- package/flowquery-py/misc/data/users.json +0 -242
- package/flowquery-py/notebooks/TestFlowQuery.ipynb +0 -440
- package/flowquery-py/pyproject.toml +0 -121
- package/flowquery-py/setup_env.ps1 +0 -92
- package/flowquery-py/setup_env.sh +0 -87
- package/flowquery-py/src/__init__.py +0 -38
- package/flowquery-py/src/__main__.py +0 -10
- package/flowquery-py/src/compute/__init__.py +0 -6
- package/flowquery-py/src/compute/flowquery.py +0 -68
- package/flowquery-py/src/compute/runner.py +0 -64
- package/flowquery-py/src/extensibility.py +0 -52
- package/flowquery-py/src/graph/__init__.py +0 -31
- package/flowquery-py/src/graph/data.py +0 -136
- package/flowquery-py/src/graph/database.py +0 -141
- package/flowquery-py/src/graph/hops.py +0 -43
- package/flowquery-py/src/graph/node.py +0 -143
- package/flowquery-py/src/graph/node_data.py +0 -26
- package/flowquery-py/src/graph/node_reference.py +0 -50
- package/flowquery-py/src/graph/pattern.py +0 -115
- package/flowquery-py/src/graph/pattern_expression.py +0 -67
- package/flowquery-py/src/graph/patterns.py +0 -42
- package/flowquery-py/src/graph/physical_node.py +0 -41
- package/flowquery-py/src/graph/physical_relationship.py +0 -36
- package/flowquery-py/src/graph/relationship.py +0 -193
- package/flowquery-py/src/graph/relationship_data.py +0 -36
- package/flowquery-py/src/graph/relationship_match_collector.py +0 -85
- package/flowquery-py/src/graph/relationship_reference.py +0 -21
- package/flowquery-py/src/io/__init__.py +0 -5
- package/flowquery-py/src/io/command_line.py +0 -108
- package/flowquery-py/src/parsing/__init__.py +0 -17
- package/flowquery-py/src/parsing/alias.py +0 -20
- package/flowquery-py/src/parsing/alias_option.py +0 -11
- package/flowquery-py/src/parsing/ast_node.py +0 -147
- package/flowquery-py/src/parsing/base_parser.py +0 -84
- package/flowquery-py/src/parsing/components/__init__.py +0 -19
- package/flowquery-py/src/parsing/components/csv.py +0 -8
- package/flowquery-py/src/parsing/components/from_.py +0 -12
- package/flowquery-py/src/parsing/components/headers.py +0 -12
- package/flowquery-py/src/parsing/components/json.py +0 -8
- package/flowquery-py/src/parsing/components/null.py +0 -10
- package/flowquery-py/src/parsing/components/post.py +0 -8
- package/flowquery-py/src/parsing/components/text.py +0 -8
- package/flowquery-py/src/parsing/context.py +0 -50
- package/flowquery-py/src/parsing/data_structures/__init__.py +0 -15
- package/flowquery-py/src/parsing/data_structures/associative_array.py +0 -41
- package/flowquery-py/src/parsing/data_structures/json_array.py +0 -30
- package/flowquery-py/src/parsing/data_structures/key_value_pair.py +0 -38
- package/flowquery-py/src/parsing/data_structures/lookup.py +0 -51
- package/flowquery-py/src/parsing/data_structures/range_lookup.py +0 -42
- package/flowquery-py/src/parsing/expressions/__init__.py +0 -61
- package/flowquery-py/src/parsing/expressions/boolean.py +0 -20
- package/flowquery-py/src/parsing/expressions/expression.py +0 -141
- package/flowquery-py/src/parsing/expressions/expression_map.py +0 -26
- package/flowquery-py/src/parsing/expressions/f_string.py +0 -27
- package/flowquery-py/src/parsing/expressions/identifier.py +0 -21
- package/flowquery-py/src/parsing/expressions/number.py +0 -32
- package/flowquery-py/src/parsing/expressions/operator.py +0 -271
- package/flowquery-py/src/parsing/expressions/reference.py +0 -47
- package/flowquery-py/src/parsing/expressions/string.py +0 -27
- package/flowquery-py/src/parsing/functions/__init__.py +0 -127
- package/flowquery-py/src/parsing/functions/aggregate_function.py +0 -60
- package/flowquery-py/src/parsing/functions/async_function.py +0 -65
- package/flowquery-py/src/parsing/functions/avg.py +0 -55
- package/flowquery-py/src/parsing/functions/coalesce.py +0 -43
- package/flowquery-py/src/parsing/functions/collect.py +0 -75
- package/flowquery-py/src/parsing/functions/count.py +0 -79
- package/flowquery-py/src/parsing/functions/date_.py +0 -61
- package/flowquery-py/src/parsing/functions/datetime_.py +0 -62
- package/flowquery-py/src/parsing/functions/duration.py +0 -159
- package/flowquery-py/src/parsing/functions/element_id.py +0 -50
- package/flowquery-py/src/parsing/functions/function.py +0 -68
- package/flowquery-py/src/parsing/functions/function_factory.py +0 -170
- package/flowquery-py/src/parsing/functions/function_metadata.py +0 -148
- package/flowquery-py/src/parsing/functions/functions.py +0 -67
- package/flowquery-py/src/parsing/functions/head.py +0 -39
- package/flowquery-py/src/parsing/functions/id_.py +0 -49
- package/flowquery-py/src/parsing/functions/join.py +0 -49
- package/flowquery-py/src/parsing/functions/keys.py +0 -34
- package/flowquery-py/src/parsing/functions/last.py +0 -39
- package/flowquery-py/src/parsing/functions/localdatetime.py +0 -60
- package/flowquery-py/src/parsing/functions/localtime.py +0 -57
- package/flowquery-py/src/parsing/functions/max_.py +0 -49
- package/flowquery-py/src/parsing/functions/min_.py +0 -49
- package/flowquery-py/src/parsing/functions/nodes.py +0 -48
- package/flowquery-py/src/parsing/functions/predicate_function.py +0 -47
- package/flowquery-py/src/parsing/functions/predicate_sum.py +0 -49
- package/flowquery-py/src/parsing/functions/properties.py +0 -50
- package/flowquery-py/src/parsing/functions/rand.py +0 -28
- package/flowquery-py/src/parsing/functions/range_.py +0 -41
- package/flowquery-py/src/parsing/functions/reducer_element.py +0 -15
- package/flowquery-py/src/parsing/functions/relationships.py +0 -46
- package/flowquery-py/src/parsing/functions/replace.py +0 -39
- package/flowquery-py/src/parsing/functions/round_.py +0 -34
- package/flowquery-py/src/parsing/functions/schema.py +0 -40
- package/flowquery-py/src/parsing/functions/size.py +0 -34
- package/flowquery-py/src/parsing/functions/split.py +0 -54
- package/flowquery-py/src/parsing/functions/string_distance.py +0 -92
- package/flowquery-py/src/parsing/functions/stringify.py +0 -49
- package/flowquery-py/src/parsing/functions/substring.py +0 -76
- package/flowquery-py/src/parsing/functions/sum.py +0 -51
- package/flowquery-py/src/parsing/functions/tail.py +0 -37
- package/flowquery-py/src/parsing/functions/temporal_utils.py +0 -186
- package/flowquery-py/src/parsing/functions/time_.py +0 -57
- package/flowquery-py/src/parsing/functions/timestamp.py +0 -37
- package/flowquery-py/src/parsing/functions/to_float.py +0 -46
- package/flowquery-py/src/parsing/functions/to_integer.py +0 -46
- package/flowquery-py/src/parsing/functions/to_json.py +0 -35
- package/flowquery-py/src/parsing/functions/to_lower.py +0 -37
- package/flowquery-py/src/parsing/functions/to_string.py +0 -41
- package/flowquery-py/src/parsing/functions/trim.py +0 -37
- package/flowquery-py/src/parsing/functions/type_.py +0 -47
- package/flowquery-py/src/parsing/functions/value_holder.py +0 -24
- package/flowquery-py/src/parsing/logic/__init__.py +0 -15
- package/flowquery-py/src/parsing/logic/case.py +0 -28
- package/flowquery-py/src/parsing/logic/else_.py +0 -12
- package/flowquery-py/src/parsing/logic/end.py +0 -8
- package/flowquery-py/src/parsing/logic/then.py +0 -12
- package/flowquery-py/src/parsing/logic/when.py +0 -12
- package/flowquery-py/src/parsing/operations/__init__.py +0 -46
- package/flowquery-py/src/parsing/operations/aggregated_return.py +0 -25
- package/flowquery-py/src/parsing/operations/aggregated_with.py +0 -22
- package/flowquery-py/src/parsing/operations/call.py +0 -73
- package/flowquery-py/src/parsing/operations/create_node.py +0 -35
- package/flowquery-py/src/parsing/operations/create_relationship.py +0 -35
- package/flowquery-py/src/parsing/operations/delete_node.py +0 -29
- package/flowquery-py/src/parsing/operations/delete_relationship.py +0 -29
- package/flowquery-py/src/parsing/operations/group_by.py +0 -148
- package/flowquery-py/src/parsing/operations/limit.py +0 -33
- package/flowquery-py/src/parsing/operations/load.py +0 -148
- package/flowquery-py/src/parsing/operations/match.py +0 -52
- package/flowquery-py/src/parsing/operations/operation.py +0 -69
- package/flowquery-py/src/parsing/operations/order_by.py +0 -114
- package/flowquery-py/src/parsing/operations/projection.py +0 -21
- package/flowquery-py/src/parsing/operations/return_op.py +0 -88
- package/flowquery-py/src/parsing/operations/union.py +0 -115
- package/flowquery-py/src/parsing/operations/union_all.py +0 -17
- package/flowquery-py/src/parsing/operations/unwind.py +0 -42
- package/flowquery-py/src/parsing/operations/where.py +0 -43
- package/flowquery-py/src/parsing/operations/with_op.py +0 -18
- package/flowquery-py/src/parsing/parser.py +0 -1384
- package/flowquery-py/src/parsing/parser_state.py +0 -26
- package/flowquery-py/src/parsing/token_to_node.py +0 -109
- package/flowquery-py/src/tokenization/__init__.py +0 -23
- package/flowquery-py/src/tokenization/keyword.py +0 -54
- package/flowquery-py/src/tokenization/operator.py +0 -29
- package/flowquery-py/src/tokenization/string_walker.py +0 -158
- package/flowquery-py/src/tokenization/symbol.py +0 -19
- package/flowquery-py/src/tokenization/token.py +0 -693
- package/flowquery-py/src/tokenization/token_mapper.py +0 -53
- package/flowquery-py/src/tokenization/token_type.py +0 -21
- package/flowquery-py/src/tokenization/tokenizer.py +0 -214
- package/flowquery-py/src/tokenization/trie.py +0 -125
- package/flowquery-py/src/utils/__init__.py +0 -6
- package/flowquery-py/src/utils/object_utils.py +0 -20
- package/flowquery-py/src/utils/string_utils.py +0 -113
- package/flowquery-py/tests/__init__.py +0 -1
- package/flowquery-py/tests/compute/__init__.py +0 -1
- package/flowquery-py/tests/compute/test_runner.py +0 -4902
- package/flowquery-py/tests/graph/__init__.py +0 -1
- package/flowquery-py/tests/graph/test_create.py +0 -56
- package/flowquery-py/tests/graph/test_data.py +0 -73
- package/flowquery-py/tests/graph/test_match.py +0 -40
- package/flowquery-py/tests/parsing/__init__.py +0 -1
- package/flowquery-py/tests/parsing/test_context.py +0 -34
- package/flowquery-py/tests/parsing/test_expression.py +0 -248
- package/flowquery-py/tests/parsing/test_parser.py +0 -1237
- package/flowquery-py/tests/test_extensibility.py +0 -611
- package/flowquery-py/tests/tokenization/__init__.py +0 -1
- package/flowquery-py/tests/tokenization/test_token_mapper.py +0 -60
- package/flowquery-py/tests/tokenization/test_tokenizer.py +0 -198
- package/flowquery-py/tests/tokenization/test_trie.py +0 -30
- package/flowquery-vscode/.vscode-test.mjs +0 -5
- package/flowquery-vscode/.vscodeignore +0 -13
- package/flowquery-vscode/LICENSE +0 -21
- package/flowquery-vscode/README.md +0 -11
- package/flowquery-vscode/demo/FlowQueryVSCodeDemo.gif +0 -0
- package/flowquery-vscode/eslint.config.mjs +0 -25
- package/flowquery-vscode/extension.js +0 -508
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +0 -1
- package/flowquery-vscode/flowquery-worker.js +0 -66
- package/flowquery-vscode/images/FlowQueryLogoIcon.png +0 -0
- package/flowquery-vscode/jsconfig.json +0 -13
- package/flowquery-vscode/libs/page.css +0 -53
- package/flowquery-vscode/libs/table.css +0 -13
- package/flowquery-vscode/libs/tabs.css +0 -66
- package/flowquery-vscode/package-lock.json +0 -2917
- package/flowquery-vscode/package.json +0 -51
- package/flowquery-vscode/test/extension.test.js +0 -196
- package/flowquery-vscode/test/worker.test.js +0 -25
- package/flowquery-vscode/vsc-extension-quickstart.md +0 -42
- package/jest.config.js +0 -14
- package/misc/apps/RAG/README.md +0 -29
- package/misc/apps/RAG/data/chats.json +0 -302
- package/misc/apps/RAG/data/emails.json +0 -182
- package/misc/apps/RAG/data/events.json +0 -226
- package/misc/apps/RAG/data/files.json +0 -172
- package/misc/apps/RAG/data/users.json +0 -158
- package/misc/apps/RAG/jest.config.js +0 -21
- package/misc/apps/RAG/package.json +0 -48
- package/misc/apps/RAG/public/index.html +0 -18
- package/misc/apps/RAG/src/App.css +0 -42
- package/misc/apps/RAG/src/App.tsx +0 -50
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.css +0 -172
- package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +0 -380
- package/misc/apps/RAG/src/components/ApiKeySettings.tsx +0 -245
- package/misc/apps/RAG/src/components/ChatContainer.css +0 -67
- package/misc/apps/RAG/src/components/ChatContainer.tsx +0 -242
- package/misc/apps/RAG/src/components/ChatInput.css +0 -23
- package/misc/apps/RAG/src/components/ChatInput.tsx +0 -76
- package/misc/apps/RAG/src/components/ChatMessage.css +0 -160
- package/misc/apps/RAG/src/components/ChatMessage.tsx +0 -286
- package/misc/apps/RAG/src/components/FlowQueryAgent.ts +0 -708
- package/misc/apps/RAG/src/components/FlowQueryRunner.css +0 -113
- package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +0 -371
- package/misc/apps/RAG/src/components/index.ts +0 -28
- package/misc/apps/RAG/src/graph/index.ts +0 -19
- package/misc/apps/RAG/src/graph/initializeGraph.ts +0 -254
- package/misc/apps/RAG/src/index.tsx +0 -29
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +0 -327
- package/misc/apps/RAG/src/prompts/index.ts +0 -10
- package/misc/apps/RAG/src/tests/graph.test.ts +0 -35
- package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +0 -130
- package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +0 -208
- package/misc/apps/RAG/src/utils/Llm.ts +0 -248
- package/misc/apps/RAG/src/utils/index.ts +0 -12
- package/misc/apps/RAG/tsconfig.json +0 -22
- package/misc/apps/RAG/webpack.config.js +0 -43
- package/misc/apps/README.md +0 -1
- package/misc/queries/analyze_catfacts.cql +0 -75
- package/misc/queries/azure_openai_completions.cql +0 -13
- package/misc/queries/azure_openai_models.cql +0 -9
- package/misc/queries/mock_pipeline.cql +0 -84
- package/misc/queries/openai_completions.cql +0 -15
- package/misc/queries/openai_models.cql +0 -13
- package/misc/queries/test.cql +0 -6
- package/misc/queries/tool_inference.cql +0 -24
- package/misc/queries/wisdom.cql +0 -6
- package/misc/queries/wisdom_letter_histogram.cql +0 -8
- package/src/compute/flowquery.ts +0 -46
- package/src/compute/runner.ts +0 -66
- package/src/extensibility.ts +0 -45
- package/src/graph/data.ts +0 -130
- package/src/graph/database.ts +0 -143
- package/src/graph/hops.ts +0 -22
- package/src/graph/node.ts +0 -122
- package/src/graph/node_data.ts +0 -18
- package/src/graph/node_reference.ts +0 -38
- package/src/graph/pattern.ts +0 -110
- package/src/graph/pattern_expression.ts +0 -48
- package/src/graph/patterns.ts +0 -36
- package/src/graph/physical_node.ts +0 -23
- package/src/graph/physical_relationship.ts +0 -23
- package/src/graph/relationship.ts +0 -167
- package/src/graph/relationship_data.ts +0 -31
- package/src/graph/relationship_match_collector.ts +0 -64
- package/src/graph/relationship_reference.ts +0 -25
- package/src/index.browser.ts +0 -46
- package/src/index.node.ts +0 -55
- package/src/index.ts +0 -12
- package/src/io/command_line.ts +0 -74
- package/src/parsing/alias.ts +0 -23
- package/src/parsing/alias_option.ts +0 -5
- package/src/parsing/ast_node.ts +0 -153
- package/src/parsing/base_parser.ts +0 -98
- package/src/parsing/components/csv.ts +0 -9
- package/src/parsing/components/from.ts +0 -12
- package/src/parsing/components/headers.ts +0 -12
- package/src/parsing/components/json.ts +0 -9
- package/src/parsing/components/null.ts +0 -9
- package/src/parsing/components/post.ts +0 -9
- package/src/parsing/components/text.ts +0 -9
- package/src/parsing/context.ts +0 -54
- package/src/parsing/data_structures/associative_array.ts +0 -43
- package/src/parsing/data_structures/json_array.ts +0 -31
- package/src/parsing/data_structures/key_value_pair.ts +0 -37
- package/src/parsing/data_structures/lookup.ts +0 -44
- package/src/parsing/data_structures/range_lookup.ts +0 -36
- package/src/parsing/expressions/boolean.ts +0 -21
- package/src/parsing/expressions/expression.ts +0 -150
- package/src/parsing/expressions/expression_map.ts +0 -22
- package/src/parsing/expressions/f_string.ts +0 -26
- package/src/parsing/expressions/identifier.ts +0 -22
- package/src/parsing/expressions/number.ts +0 -40
- package/src/parsing/expressions/operator.ts +0 -354
- package/src/parsing/expressions/reference.ts +0 -45
- package/src/parsing/expressions/string.ts +0 -34
- package/src/parsing/functions/aggregate_function.ts +0 -58
- package/src/parsing/functions/async_function.ts +0 -64
- package/src/parsing/functions/avg.ts +0 -47
- package/src/parsing/functions/coalesce.ts +0 -49
- package/src/parsing/functions/collect.ts +0 -54
- package/src/parsing/functions/count.ts +0 -54
- package/src/parsing/functions/date.ts +0 -63
- package/src/parsing/functions/datetime.ts +0 -63
- package/src/parsing/functions/duration.ts +0 -143
- package/src/parsing/functions/element_id.ts +0 -51
- package/src/parsing/functions/function.ts +0 -60
- package/src/parsing/functions/function_factory.ts +0 -195
- package/src/parsing/functions/function_metadata.ts +0 -217
- package/src/parsing/functions/functions.ts +0 -70
- package/src/parsing/functions/head.ts +0 -42
- package/src/parsing/functions/id.ts +0 -51
- package/src/parsing/functions/join.ts +0 -40
- package/src/parsing/functions/keys.ts +0 -29
- package/src/parsing/functions/last.ts +0 -42
- package/src/parsing/functions/localdatetime.ts +0 -63
- package/src/parsing/functions/localtime.ts +0 -58
- package/src/parsing/functions/max.ts +0 -37
- package/src/parsing/functions/min.ts +0 -37
- package/src/parsing/functions/nodes.ts +0 -54
- package/src/parsing/functions/predicate_function.ts +0 -48
- package/src/parsing/functions/predicate_sum.ts +0 -47
- package/src/parsing/functions/properties.ts +0 -56
- package/src/parsing/functions/rand.ts +0 -21
- package/src/parsing/functions/range.ts +0 -37
- package/src/parsing/functions/reducer_element.ts +0 -10
- package/src/parsing/functions/relationships.ts +0 -52
- package/src/parsing/functions/replace.ts +0 -38
- package/src/parsing/functions/round.ts +0 -28
- package/src/parsing/functions/schema.ts +0 -39
- package/src/parsing/functions/size.ts +0 -28
- package/src/parsing/functions/split.ts +0 -45
- package/src/parsing/functions/string_distance.ts +0 -83
- package/src/parsing/functions/stringify.ts +0 -37
- package/src/parsing/functions/substring.ts +0 -68
- package/src/parsing/functions/sum.ts +0 -41
- package/src/parsing/functions/tail.ts +0 -39
- package/src/parsing/functions/temporal_utils.ts +0 -180
- package/src/parsing/functions/time.ts +0 -58
- package/src/parsing/functions/timestamp.ts +0 -37
- package/src/parsing/functions/to_float.ts +0 -50
- package/src/parsing/functions/to_integer.ts +0 -50
- package/src/parsing/functions/to_json.ts +0 -28
- package/src/parsing/functions/to_lower.ts +0 -28
- package/src/parsing/functions/to_string.ts +0 -32
- package/src/parsing/functions/trim.ts +0 -28
- package/src/parsing/functions/type.ts +0 -39
- package/src/parsing/functions/value_holder.ts +0 -13
- package/src/parsing/logic/case.ts +0 -26
- package/src/parsing/logic/else.ts +0 -12
- package/src/parsing/logic/end.ts +0 -9
- package/src/parsing/logic/then.ts +0 -12
- package/src/parsing/logic/when.ts +0 -12
- package/src/parsing/operations/aggregated_return.ts +0 -22
- package/src/parsing/operations/aggregated_with.ts +0 -18
- package/src/parsing/operations/call.ts +0 -69
- package/src/parsing/operations/create_node.ts +0 -39
- package/src/parsing/operations/create_relationship.ts +0 -38
- package/src/parsing/operations/delete_node.ts +0 -33
- package/src/parsing/operations/delete_relationship.ts +0 -32
- package/src/parsing/operations/group_by.ts +0 -137
- package/src/parsing/operations/limit.ts +0 -31
- package/src/parsing/operations/load.ts +0 -146
- package/src/parsing/operations/match.ts +0 -54
- package/src/parsing/operations/operation.ts +0 -69
- package/src/parsing/operations/order_by.ts +0 -126
- package/src/parsing/operations/projection.ts +0 -18
- package/src/parsing/operations/return.ts +0 -76
- package/src/parsing/operations/union.ts +0 -114
- package/src/parsing/operations/union_all.ts +0 -16
- package/src/parsing/operations/unwind.ts +0 -36
- package/src/parsing/operations/where.ts +0 -42
- package/src/parsing/operations/with.ts +0 -20
- package/src/parsing/parser.ts +0 -1641
- package/src/parsing/parser_state.ts +0 -25
- package/src/parsing/token_to_node.ts +0 -114
- package/src/tokenization/keyword.ts +0 -50
- package/src/tokenization/operator.ts +0 -25
- package/src/tokenization/string_walker.ts +0 -197
- package/src/tokenization/symbol.ts +0 -15
- package/src/tokenization/token.ts +0 -764
- package/src/tokenization/token_mapper.ts +0 -53
- package/src/tokenization/token_type.ts +0 -16
- package/src/tokenization/tokenizer.ts +0 -250
- package/src/tokenization/trie.ts +0 -117
- package/src/utils/object_utils.ts +0 -17
- package/src/utils/string_utils.ts +0 -114
- package/tests/compute/runner.test.ts +0 -4559
- package/tests/extensibility.test.ts +0 -643
- package/tests/graph/create.test.ts +0 -36
- package/tests/graph/data.test.ts +0 -58
- package/tests/graph/match.test.ts +0 -29
- package/tests/parsing/context.test.ts +0 -27
- package/tests/parsing/expression.test.ts +0 -303
- package/tests/parsing/parser.test.ts +0 -1327
- package/tests/tokenization/token_mapper.test.ts +0 -47
- package/tests/tokenization/tokenizer.test.ts +0 -191
- package/tests/tokenization/trie.test.ts +0 -20
- package/tsconfig.json +0 -19
- package/typedoc.json +0 -16
- package/vscode-settings.json.recommended +0 -16
- package/webpack.config.js +0 -26
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
"""GroupBy implementation for aggregate operations."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from typing import Any, Dict, Generator, List, Optional
|
|
5
|
-
|
|
6
|
-
from ..ast_node import ASTNode
|
|
7
|
-
from ..functions.aggregate_function import AggregateFunction
|
|
8
|
-
from ..functions.reducer_element import ReducerElement
|
|
9
|
-
from .projection import Projection
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _make_hashable(value: Any) -> Any:
|
|
13
|
-
"""Convert a value to a hashable form for use as a dict key."""
|
|
14
|
-
if isinstance(value, dict):
|
|
15
|
-
return json.dumps(value, sort_keys=True, default=str)
|
|
16
|
-
if isinstance(value, list):
|
|
17
|
-
return json.dumps(value, sort_keys=True, default=str)
|
|
18
|
-
return value
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class GroupByNode:
|
|
22
|
-
"""Represents a node in the group-by tree."""
|
|
23
|
-
|
|
24
|
-
def __init__(self, value: Any = None):
|
|
25
|
-
self._value = value
|
|
26
|
-
self._children: Dict[Any, 'GroupByNode'] = {}
|
|
27
|
-
self._elements: Optional[List[ReducerElement]] = None
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def value(self) -> Any:
|
|
31
|
-
return self._value
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def children(self) -> Dict[Any, 'GroupByNode']:
|
|
35
|
-
return self._children
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def elements(self) -> Optional[List[ReducerElement]]:
|
|
39
|
-
return self._elements
|
|
40
|
-
|
|
41
|
-
@elements.setter
|
|
42
|
-
def elements(self, elements: List[ReducerElement]) -> None:
|
|
43
|
-
self._elements = elements
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class GroupBy(Projection):
|
|
47
|
-
"""Implements grouping and aggregation for FlowQuery operations."""
|
|
48
|
-
|
|
49
|
-
def __init__(self, expressions: List[ASTNode]) -> None:
|
|
50
|
-
super().__init__(expressions)
|
|
51
|
-
self._root = GroupByNode()
|
|
52
|
-
self._current = self._root
|
|
53
|
-
self._mappers: Optional[List[Any]] = None
|
|
54
|
-
self._reducers: Optional[List[AggregateFunction]] = None
|
|
55
|
-
self._where: Optional[ASTNode] = None
|
|
56
|
-
|
|
57
|
-
async def run(self) -> None:
|
|
58
|
-
self._reset_tree()
|
|
59
|
-
self._map()
|
|
60
|
-
self._reduce()
|
|
61
|
-
|
|
62
|
-
@property
|
|
63
|
-
def _root_node(self) -> GroupByNode:
|
|
64
|
-
return self._root
|
|
65
|
-
|
|
66
|
-
def _reset_tree(self) -> None:
|
|
67
|
-
self._current = self._root
|
|
68
|
-
|
|
69
|
-
def _map(self) -> None:
|
|
70
|
-
node = self._current
|
|
71
|
-
for mapper in self.mappers:
|
|
72
|
-
value = mapper.value()
|
|
73
|
-
key = _make_hashable(value)
|
|
74
|
-
child = node.children.get(key)
|
|
75
|
-
if child is None:
|
|
76
|
-
child = GroupByNode(value)
|
|
77
|
-
node.children[key] = child
|
|
78
|
-
node = child
|
|
79
|
-
self._current = node
|
|
80
|
-
|
|
81
|
-
def _reduce(self) -> None:
|
|
82
|
-
if self._current.elements is None:
|
|
83
|
-
self._current.elements = [reducer.element() for reducer in self.reducers]
|
|
84
|
-
elements = self._current.elements
|
|
85
|
-
if elements:
|
|
86
|
-
for i, reducer in enumerate(self.reducers):
|
|
87
|
-
reducer.reduce(elements[i])
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def mappers(self) -> List[Any]:
|
|
91
|
-
if self._mappers is None:
|
|
92
|
-
self._mappers = list(self._generate_mappers())
|
|
93
|
-
return self._mappers
|
|
94
|
-
|
|
95
|
-
def _generate_mappers(self) -> Generator[Any, None, None]:
|
|
96
|
-
for expression, _ in self.expressions():
|
|
97
|
-
if hasattr(expression, 'mappable') and expression.mappable():
|
|
98
|
-
yield expression
|
|
99
|
-
|
|
100
|
-
@property
|
|
101
|
-
def reducers(self) -> List[AggregateFunction]:
|
|
102
|
-
if self._reducers is None:
|
|
103
|
-
self._reducers = []
|
|
104
|
-
for child in self.children:
|
|
105
|
-
if hasattr(child, 'reducers'):
|
|
106
|
-
self._reducers.extend(child.reducers())
|
|
107
|
-
return self._reducers
|
|
108
|
-
|
|
109
|
-
def generate_results(
|
|
110
|
-
self,
|
|
111
|
-
mapper_index: int = 0,
|
|
112
|
-
node: Optional[GroupByNode] = None
|
|
113
|
-
) -> Generator[Dict[str, Any], None, None]:
|
|
114
|
-
if node is None:
|
|
115
|
-
node = self._root
|
|
116
|
-
|
|
117
|
-
if mapper_index == 0 and len(node.children) == 0 and len(self.mappers) > 0:
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
if len(node.children) > 0:
|
|
121
|
-
for child in node.children.values():
|
|
122
|
-
self.mappers[mapper_index].overridden = child.value
|
|
123
|
-
yield from self.generate_results(mapper_index + 1, child)
|
|
124
|
-
else:
|
|
125
|
-
if node.elements is None:
|
|
126
|
-
node.elements = [reducer.element() for reducer in self.reducers]
|
|
127
|
-
if node.elements:
|
|
128
|
-
for i, element in enumerate(node.elements):
|
|
129
|
-
self.reducers[i].overridden = element.value
|
|
130
|
-
record: Dict[str, Any] = {}
|
|
131
|
-
for expression, alias in self.expressions():
|
|
132
|
-
record[alias] = expression.value()
|
|
133
|
-
if self.where_condition:
|
|
134
|
-
yield record
|
|
135
|
-
|
|
136
|
-
@property
|
|
137
|
-
def where(self) -> Optional[ASTNode]:
|
|
138
|
-
return self._where
|
|
139
|
-
|
|
140
|
-
@where.setter
|
|
141
|
-
def where(self, where: Optional[ASTNode]) -> None:
|
|
142
|
-
self._where = where
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def where_condition(self) -> Any:
|
|
146
|
-
if self._where is None:
|
|
147
|
-
return True
|
|
148
|
-
return self._where.value()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""Represents a LIMIT operation that limits the number of results."""
|
|
2
|
-
|
|
3
|
-
from .operation import Operation
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Limit(Operation):
|
|
7
|
-
"""Represents a LIMIT operation that limits the number of results."""
|
|
8
|
-
|
|
9
|
-
def __init__(self, limit: int):
|
|
10
|
-
super().__init__()
|
|
11
|
-
self._count = 0
|
|
12
|
-
self._limit = limit
|
|
13
|
-
|
|
14
|
-
@property
|
|
15
|
-
def is_limit_reached(self) -> bool:
|
|
16
|
-
return self._count >= self._limit
|
|
17
|
-
|
|
18
|
-
@property
|
|
19
|
-
def limit_value(self) -> int:
|
|
20
|
-
return self._limit
|
|
21
|
-
|
|
22
|
-
def increment(self) -> None:
|
|
23
|
-
self._count += 1
|
|
24
|
-
|
|
25
|
-
async def run(self) -> None:
|
|
26
|
-
if self._count >= self._limit:
|
|
27
|
-
return
|
|
28
|
-
self._count += 1
|
|
29
|
-
if self.next:
|
|
30
|
-
await self.next.run()
|
|
31
|
-
|
|
32
|
-
def reset(self) -> None:
|
|
33
|
-
self._count = 0
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
"""Represents a LOAD operation that fetches data from external sources."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from typing import Any, Dict, Optional
|
|
5
|
-
|
|
6
|
-
import aiohttp
|
|
7
|
-
|
|
8
|
-
from ..ast_node import ASTNode
|
|
9
|
-
from ..components.headers import Headers
|
|
10
|
-
from ..components.json import JSON as JSONComponent
|
|
11
|
-
from ..components.post import Post
|
|
12
|
-
from ..components.text import Text
|
|
13
|
-
from ..functions.async_function import AsyncFunction
|
|
14
|
-
from .operation import Operation
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Load(Operation):
|
|
18
|
-
"""Represents a LOAD operation that fetches data from external sources."""
|
|
19
|
-
|
|
20
|
-
def __init__(self) -> None:
|
|
21
|
-
super().__init__()
|
|
22
|
-
self._value: Any = None
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def type(self) -> ASTNode:
|
|
26
|
-
"""Gets the data type (JSON, CSV, or Text)."""
|
|
27
|
-
return self.children[0]
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def from_component(self) -> ASTNode:
|
|
31
|
-
"""Gets the From component which contains either a URL expression or an AsyncFunction."""
|
|
32
|
-
return self.children[1]
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def is_async_function(self) -> bool:
|
|
36
|
-
"""Checks if the data source is an async function."""
|
|
37
|
-
return isinstance(self.from_component.first_child(), AsyncFunction)
|
|
38
|
-
|
|
39
|
-
@property
|
|
40
|
-
def async_function(self) -> Optional[AsyncFunction]:
|
|
41
|
-
"""Gets the async function if the source is a function, otherwise None."""
|
|
42
|
-
child = self.from_component.first_child()
|
|
43
|
-
return child if isinstance(child, AsyncFunction) else None
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def from_(self) -> Any:
|
|
47
|
-
return self.children[1].value()
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def headers(self) -> Dict[str, str]:
|
|
51
|
-
if self.child_count() > 2 and isinstance(self.children[2], Headers):
|
|
52
|
-
return self.children[2].value() or {}
|
|
53
|
-
return {}
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def payload(self) -> Optional[ASTNode]:
|
|
57
|
-
post = None
|
|
58
|
-
if self.child_count() > 2 and isinstance(self.children[2], Post):
|
|
59
|
-
post = self.children[2]
|
|
60
|
-
elif self.child_count() > 3 and isinstance(self.children[3], Post):
|
|
61
|
-
post = self.children[3]
|
|
62
|
-
return post.first_child() if post else None
|
|
63
|
-
|
|
64
|
-
def _method(self) -> str:
|
|
65
|
-
return "GET" if self.payload is None else "POST"
|
|
66
|
-
|
|
67
|
-
def _options(self) -> Dict[str, Any]:
|
|
68
|
-
headers = dict(self.headers)
|
|
69
|
-
payload = self.payload
|
|
70
|
-
data = payload.value() if payload else None
|
|
71
|
-
if data is not None and isinstance(data, dict) and "Content-Type" not in headers:
|
|
72
|
-
headers["Content-Type"] = "application/json"
|
|
73
|
-
options: Dict[str, Any] = {
|
|
74
|
-
"method": self._method(),
|
|
75
|
-
"headers": headers,
|
|
76
|
-
}
|
|
77
|
-
if payload is not None:
|
|
78
|
-
options["body"] = json.dumps(payload.value())
|
|
79
|
-
return options
|
|
80
|
-
|
|
81
|
-
async def _load_from_function(self) -> None:
|
|
82
|
-
"""Loads data from an async function source."""
|
|
83
|
-
async_func = self.async_function
|
|
84
|
-
if async_func is None:
|
|
85
|
-
return
|
|
86
|
-
args = async_func.get_arguments()
|
|
87
|
-
async for item in async_func.generate(*args):
|
|
88
|
-
self._value = item
|
|
89
|
-
if self.next:
|
|
90
|
-
await self.next.run()
|
|
91
|
-
|
|
92
|
-
async def _load_from_url(self) -> None:
|
|
93
|
-
"""Loads data from a URL source."""
|
|
94
|
-
async with aiohttp.ClientSession() as session:
|
|
95
|
-
options = self._options()
|
|
96
|
-
method = options.pop("method")
|
|
97
|
-
headers = options.pop("headers", {})
|
|
98
|
-
body = options.pop("body", None)
|
|
99
|
-
|
|
100
|
-
# Set Accept-Encoding to support common compression formats
|
|
101
|
-
# Note: brotli (br) is excluded due to API incompatibility between
|
|
102
|
-
# aiohttp 3.13+ and the brotli package's Decompressor.decompress() method
|
|
103
|
-
if "Accept-Encoding" not in headers:
|
|
104
|
-
headers["Accept-Encoding"] = "gzip, deflate"
|
|
105
|
-
|
|
106
|
-
async with session.request(
|
|
107
|
-
method,
|
|
108
|
-
self.from_,
|
|
109
|
-
headers=headers,
|
|
110
|
-
data=body
|
|
111
|
-
) as response:
|
|
112
|
-
if isinstance(self.type, JSONComponent):
|
|
113
|
-
data = await response.json()
|
|
114
|
-
elif isinstance(self.type, Text):
|
|
115
|
-
data = await response.text()
|
|
116
|
-
else:
|
|
117
|
-
data = await response.text()
|
|
118
|
-
|
|
119
|
-
if isinstance(data, list):
|
|
120
|
-
for item in data:
|
|
121
|
-
self._value = item
|
|
122
|
-
if self.next:
|
|
123
|
-
await self.next.run()
|
|
124
|
-
elif isinstance(data, dict):
|
|
125
|
-
self._value = data
|
|
126
|
-
if self.next:
|
|
127
|
-
await self.next.run()
|
|
128
|
-
elif isinstance(data, str):
|
|
129
|
-
self._value = data
|
|
130
|
-
if self.next:
|
|
131
|
-
await self.next.run()
|
|
132
|
-
|
|
133
|
-
async def load(self) -> None:
|
|
134
|
-
if self.is_async_function:
|
|
135
|
-
await self._load_from_function()
|
|
136
|
-
else:
|
|
137
|
-
await self._load_from_url()
|
|
138
|
-
|
|
139
|
-
async def run(self) -> None:
|
|
140
|
-
try:
|
|
141
|
-
await self.load()
|
|
142
|
-
except Exception as e:
|
|
143
|
-
async_func = self.async_function
|
|
144
|
-
source = async_func.name if async_func else self.from_
|
|
145
|
-
raise RuntimeError(f"Failed to load data from {source}. Error: {e}")
|
|
146
|
-
|
|
147
|
-
def value(self) -> Any:
|
|
148
|
-
return self._value
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"""Represents a MATCH operation for graph pattern matching."""
|
|
2
|
-
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
|
-
from ...graph.node import Node
|
|
6
|
-
from ...graph.pattern import Pattern
|
|
7
|
-
from ...graph.patterns import Patterns
|
|
8
|
-
from .operation import Operation
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Match(Operation):
|
|
12
|
-
"""Represents a MATCH operation for graph pattern matching."""
|
|
13
|
-
|
|
14
|
-
def __init__(self, patterns: Optional[List[Pattern]] = None, optional: bool = False) -> None:
|
|
15
|
-
super().__init__()
|
|
16
|
-
self._patterns = Patterns(patterns or [])
|
|
17
|
-
self._optional = optional
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def patterns(self) -> List[Pattern]:
|
|
21
|
-
return self._patterns.patterns if self._patterns else []
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def optional(self) -> bool:
|
|
25
|
-
return self._optional
|
|
26
|
-
|
|
27
|
-
def __str__(self) -> str:
|
|
28
|
-
return "OptionalMatch" if self._optional else "Match"
|
|
29
|
-
|
|
30
|
-
async def run(self) -> None:
|
|
31
|
-
"""Executes the match operation by chaining the patterns together.
|
|
32
|
-
If optional and no match is found, continues with null values."""
|
|
33
|
-
await self._patterns.initialize()
|
|
34
|
-
matched = False
|
|
35
|
-
|
|
36
|
-
async def to_do_next() -> None:
|
|
37
|
-
nonlocal matched
|
|
38
|
-
matched = True
|
|
39
|
-
if self.next:
|
|
40
|
-
await self.next.run()
|
|
41
|
-
|
|
42
|
-
self._patterns.to_do_next = to_do_next
|
|
43
|
-
await self._patterns.traverse()
|
|
44
|
-
|
|
45
|
-
# For OPTIONAL MATCH: if nothing matched, continue with None values
|
|
46
|
-
if not matched and self._optional:
|
|
47
|
-
for pattern in self._patterns.patterns:
|
|
48
|
-
for element in pattern.chain:
|
|
49
|
-
if isinstance(element, Node):
|
|
50
|
-
element.set_value(None)
|
|
51
|
-
if self.next:
|
|
52
|
-
await self.next.run()
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Base class for all FlowQuery operations."""
|
|
2
|
-
|
|
3
|
-
from abc import ABC
|
|
4
|
-
from typing import Any, Dict, List, Optional
|
|
5
|
-
|
|
6
|
-
from ..ast_node import ASTNode
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Operation(ASTNode, ABC):
|
|
10
|
-
"""Base class for all FlowQuery operations.
|
|
11
|
-
|
|
12
|
-
Operations represent the main statements in FlowQuery (WITH, UNWIND, RETURN, LOAD, WHERE).
|
|
13
|
-
They form a linked list structure and can be executed sequentially.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
def __init__(self) -> None:
|
|
17
|
-
super().__init__()
|
|
18
|
-
self._previous: Optional[Operation] = None
|
|
19
|
-
self._next: Optional[Operation] = None
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def previous(self) -> Optional['Operation']:
|
|
23
|
-
return self._previous
|
|
24
|
-
|
|
25
|
-
@previous.setter
|
|
26
|
-
def previous(self, value: Optional['Operation']) -> None:
|
|
27
|
-
self._previous = value
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def next(self) -> Optional['Operation']:
|
|
31
|
-
return self._next
|
|
32
|
-
|
|
33
|
-
@next.setter
|
|
34
|
-
def next(self, value: Optional['Operation']) -> None:
|
|
35
|
-
self._next = value
|
|
36
|
-
|
|
37
|
-
def add_sibling(self, operation: 'Operation') -> None:
|
|
38
|
-
if self._parent:
|
|
39
|
-
self._parent.add_child(operation)
|
|
40
|
-
operation.previous = self
|
|
41
|
-
self.next = operation
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def is_last(self) -> bool:
|
|
45
|
-
return self._next is None
|
|
46
|
-
|
|
47
|
-
async def run(self) -> None:
|
|
48
|
-
"""Executes this operation. Must be implemented by subclasses.
|
|
49
|
-
|
|
50
|
-
Raises:
|
|
51
|
-
NotImplementedError: If not implemented by subclass
|
|
52
|
-
"""
|
|
53
|
-
raise NotImplementedError("Not implemented")
|
|
54
|
-
|
|
55
|
-
async def finish(self) -> None:
|
|
56
|
-
"""Finishes execution by calling finish on the next operation in the chain."""
|
|
57
|
-
if self.next:
|
|
58
|
-
await self.next.finish()
|
|
59
|
-
|
|
60
|
-
async def initialize(self) -> None:
|
|
61
|
-
if self.next:
|
|
62
|
-
await self.next.initialize()
|
|
63
|
-
|
|
64
|
-
def reset(self) -> None:
|
|
65
|
-
pass
|
|
66
|
-
|
|
67
|
-
@property
|
|
68
|
-
def results(self) -> List[Dict[str, Any]]:
|
|
69
|
-
raise NotImplementedError("Not implemented")
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"""Represents an ORDER BY operation that sorts results."""
|
|
2
|
-
|
|
3
|
-
import functools
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
5
|
-
|
|
6
|
-
from .operation import Operation
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from ..expressions.expression import Expression
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class SortField:
|
|
13
|
-
"""A single sort specification: expression and direction."""
|
|
14
|
-
|
|
15
|
-
def __init__(self, expression: 'Expression', direction: str = "asc"):
|
|
16
|
-
self.expression = expression
|
|
17
|
-
self.direction = direction
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class OrderBy(Operation):
|
|
21
|
-
"""Represents an ORDER BY operation that sorts results.
|
|
22
|
-
|
|
23
|
-
Can be attached to a RETURN operation (sorting its results),
|
|
24
|
-
or used as a standalone accumulating operation after a non-aggregate WITH.
|
|
25
|
-
|
|
26
|
-
Supports both simple field references and arbitrary expressions:
|
|
27
|
-
|
|
28
|
-
Example::
|
|
29
|
-
|
|
30
|
-
RETURN x ORDER BY x DESC
|
|
31
|
-
RETURN x ORDER BY toLower(x.name) ASC
|
|
32
|
-
RETURN x ORDER BY string_distance(toLower(x.name), toLower('Thomas')) ASC
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
def __init__(self, fields: List[SortField]):
|
|
36
|
-
super().__init__()
|
|
37
|
-
self._fields = fields
|
|
38
|
-
self._results: List[Dict[str, Any]] = []
|
|
39
|
-
self._sort_keys: List[List[Any]] = []
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def fields(self) -> List[SortField]:
|
|
43
|
-
return self._fields
|
|
44
|
-
|
|
45
|
-
def capture_sort_keys(self) -> None:
|
|
46
|
-
"""Evaluate every sort-field expression against the current runtime
|
|
47
|
-
context and store the resulting values. Must be called once per
|
|
48
|
-
accumulated row (from ``Return.run()``)."""
|
|
49
|
-
self._sort_keys.append([f.expression.value() for f in self._fields])
|
|
50
|
-
|
|
51
|
-
def sort(self, records: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
52
|
-
"""Sort records using pre-computed sort keys captured during
|
|
53
|
-
accumulation. When no keys have been captured (e.g. aggregated
|
|
54
|
-
returns), falls back to looking up simple reference identifiers
|
|
55
|
-
in each record."""
|
|
56
|
-
from ..expressions.reference import Reference
|
|
57
|
-
|
|
58
|
-
use_keys = len(self._sort_keys) == len(records)
|
|
59
|
-
keys = self._sort_keys
|
|
60
|
-
|
|
61
|
-
# Pre-compute fallback field names for when sort keys aren't
|
|
62
|
-
# available (aggregated returns).
|
|
63
|
-
fallback_fields: List[Optional[str]] = []
|
|
64
|
-
for f in self._fields:
|
|
65
|
-
root = f.expression.first_child()
|
|
66
|
-
if isinstance(root, Reference) and f.expression.child_count() == 1:
|
|
67
|
-
fallback_fields.append(root.identifier)
|
|
68
|
-
else:
|
|
69
|
-
fallback_fields.append(None)
|
|
70
|
-
|
|
71
|
-
indices = list(range(len(records)))
|
|
72
|
-
|
|
73
|
-
def compare(ai: int, bi: int) -> int:
|
|
74
|
-
for f_idx, sf in enumerate(self._fields):
|
|
75
|
-
if use_keys:
|
|
76
|
-
a_val = keys[ai][f_idx]
|
|
77
|
-
b_val = keys[bi][f_idx]
|
|
78
|
-
elif fallback_fields[f_idx] is not None:
|
|
79
|
-
a_val = records[ai].get(fallback_fields[f_idx]) # type: ignore[arg-type]
|
|
80
|
-
b_val = records[bi].get(fallback_fields[f_idx]) # type: ignore[arg-type]
|
|
81
|
-
else:
|
|
82
|
-
continue
|
|
83
|
-
cmp = 0
|
|
84
|
-
if a_val is None and b_val is None:
|
|
85
|
-
cmp = 0
|
|
86
|
-
elif a_val is None:
|
|
87
|
-
cmp = -1
|
|
88
|
-
elif b_val is None:
|
|
89
|
-
cmp = 1
|
|
90
|
-
elif a_val < b_val:
|
|
91
|
-
cmp = -1
|
|
92
|
-
elif a_val > b_val:
|
|
93
|
-
cmp = 1
|
|
94
|
-
if cmp != 0:
|
|
95
|
-
return -cmp if sf.direction == "desc" else cmp
|
|
96
|
-
return 0
|
|
97
|
-
|
|
98
|
-
indices.sort(key=functools.cmp_to_key(compare))
|
|
99
|
-
return [records[i] for i in indices]
|
|
100
|
-
|
|
101
|
-
async def run(self) -> None:
|
|
102
|
-
"""When used as a standalone operation, passes through to next."""
|
|
103
|
-
if self.next:
|
|
104
|
-
await self.next.run()
|
|
105
|
-
|
|
106
|
-
async def initialize(self) -> None:
|
|
107
|
-
self._results = []
|
|
108
|
-
self._sort_keys = []
|
|
109
|
-
if self.next:
|
|
110
|
-
await self.next.initialize()
|
|
111
|
-
|
|
112
|
-
@property
|
|
113
|
-
def results(self) -> List[Dict[str, Any]]:
|
|
114
|
-
return self._results
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Base class for projection operations."""
|
|
2
|
-
|
|
3
|
-
from typing import Any, Generator, List, Tuple
|
|
4
|
-
|
|
5
|
-
from ..ast_node import ASTNode
|
|
6
|
-
from .operation import Operation
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Projection(Operation):
|
|
10
|
-
"""Base class for operations that project expressions."""
|
|
11
|
-
|
|
12
|
-
def __init__(self, expressions: List[ASTNode]):
|
|
13
|
-
super().__init__()
|
|
14
|
-
self.children = expressions
|
|
15
|
-
|
|
16
|
-
def expressions(self) -> Generator[Tuple[Any, str], None, None]:
|
|
17
|
-
"""Yields tuples of (expression, alias) for all child expressions."""
|
|
18
|
-
for i, child in enumerate(self.children):
|
|
19
|
-
expression = child
|
|
20
|
-
alias = getattr(expression, 'alias', None) or f"expr{i}"
|
|
21
|
-
yield (expression, alias)
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""Represents a RETURN operation that produces the final query results."""
|
|
2
|
-
|
|
3
|
-
import copy
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
5
|
-
|
|
6
|
-
from ..ast_node import ASTNode
|
|
7
|
-
from .limit import Limit
|
|
8
|
-
from .order_by import OrderBy
|
|
9
|
-
from .projection import Projection
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from .where import Where
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Return(Projection):
|
|
16
|
-
"""Represents a RETURN operation that produces the final query results.
|
|
17
|
-
|
|
18
|
-
The RETURN operation evaluates expressions and collects them into result records.
|
|
19
|
-
It can optionally have a WHERE clause to filter results.
|
|
20
|
-
|
|
21
|
-
Example:
|
|
22
|
-
# RETURN x, y WHERE x > 0
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def __init__(self, expressions: List[ASTNode]) -> None:
|
|
26
|
-
super().__init__(expressions)
|
|
27
|
-
self._where: Optional['Where'] = None
|
|
28
|
-
self._results: List[Dict[str, Any]] = []
|
|
29
|
-
self._limit: Optional[Limit] = None
|
|
30
|
-
self._order_by: Optional[OrderBy] = None
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def where(self) -> Any:
|
|
34
|
-
if self._where is None:
|
|
35
|
-
return True
|
|
36
|
-
return self._where.value()
|
|
37
|
-
|
|
38
|
-
@where.setter
|
|
39
|
-
def where(self, where: 'Where') -> None:
|
|
40
|
-
self._where = where
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def limit(self) -> Optional[Limit]:
|
|
44
|
-
return self._limit
|
|
45
|
-
|
|
46
|
-
@limit.setter
|
|
47
|
-
def limit(self, limit: Limit) -> None:
|
|
48
|
-
self._limit = limit
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def order_by(self) -> Optional[OrderBy]:
|
|
52
|
-
return self._order_by
|
|
53
|
-
|
|
54
|
-
@order_by.setter
|
|
55
|
-
def order_by(self, order_by: OrderBy) -> None:
|
|
56
|
-
self._order_by = order_by
|
|
57
|
-
|
|
58
|
-
async def run(self) -> None:
|
|
59
|
-
if not self.where:
|
|
60
|
-
return
|
|
61
|
-
# When ORDER BY is present, skip limit during accumulation;
|
|
62
|
-
# limit will be applied after sorting in results property
|
|
63
|
-
if self._order_by is None and self._limit is not None and self._limit.is_limit_reached:
|
|
64
|
-
return
|
|
65
|
-
record: Dict[str, Any] = {}
|
|
66
|
-
for expression, alias in self.expressions():
|
|
67
|
-
raw = expression.value()
|
|
68
|
-
# Deep copy objects to preserve their state
|
|
69
|
-
value = copy.deepcopy(raw) if isinstance(raw, (dict, list)) else raw
|
|
70
|
-
record[alias] = value
|
|
71
|
-
# Capture sort-key values while expression bindings are still live.
|
|
72
|
-
if self._order_by is not None:
|
|
73
|
-
self._order_by.capture_sort_keys()
|
|
74
|
-
self._results.append(record)
|
|
75
|
-
if self._order_by is None and self._limit is not None:
|
|
76
|
-
self._limit.increment()
|
|
77
|
-
|
|
78
|
-
async def initialize(self) -> None:
|
|
79
|
-
self._results = []
|
|
80
|
-
|
|
81
|
-
@property
|
|
82
|
-
def results(self) -> List[Dict[str, Any]]:
|
|
83
|
-
result = self._results
|
|
84
|
-
if self._order_by is not None:
|
|
85
|
-
result = self._order_by.sort(result)
|
|
86
|
-
if self._order_by is not None and self._limit is not None:
|
|
87
|
-
result = result[:self._limit.limit_value]
|
|
88
|
-
return result
|