cypher-graphdb 0.7.2__tar.gz → 0.7.3__tar.gz
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.
- {cypher_graphdb-0.7.2/src/cypher_graphdb.egg-info → cypher_graphdb-0.7.3}/PKG-INFO +1 -1
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backend.py +10 -5
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agebulkwriter.py +48 -32
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agegraphdb.py +11 -8
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/ageserializer.py +8 -4
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agesqlbuilder.py +62 -36
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/memgraph/memgraphdb.py +19 -7
- cypher_graphdb-0.7.3/src/cypher_graphdb/bulk_refs.py +85 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/indexing.py +26 -5
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3/src/cypher_graphdb.egg-info}/PKG-INFO +1 -1
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/SOURCES.txt +2 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/scm_file_list.json +2 -0
- cypher_graphdb-0.7.3/src/cypher_graphdb.egg-info/scm_version.json +8 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_indexes_and_bulk.py +323 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_age_serializer.py +10 -3
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_age_sqlbuilder.py +50 -0
- cypher_graphdb-0.7.3/tests/unit/test_bulk_refs.py +85 -0
- cypher_graphdb-0.7.2/src/cypher_graphdb.egg-info/scm_version.json +0 -8
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.env.age.example +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.env.example +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.env.memgraph.example +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.github/workflows/audit.yml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.github/workflows/ci.yml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.github/workflows/publish.yml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.github/workflows/release.yml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.gitignore +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/.pre-commit-config.yaml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/AGENTS.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/CHANGELOG.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/CONTRIBUTING.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/ENGINEERING-STANDARDS.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/LICENSE.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/NOTICE +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/README.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/Taskfile.yml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/README.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/adr/0001-explicit-model-providers.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/adr/index.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/cli-prompt-parsing.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/duckdb-migration.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/falkordb-integration.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/fluent-cypher-query-builder.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/format-registry.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/hierarchical-export-format.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/index.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/model-provider-isolation.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/model-provider-label-collision.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/multiple-statement-execution.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/packaging-and-extras.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/parameterized-queries-for-typed-models.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/query-result-immutable-design.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/read-only-mode.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/design/stateless-multi-graph-support.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/bulk-and-indexes.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/cli.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/connecting.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/getting-started.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/import-export.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/index.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/queries.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/docs/usage/typed-models.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/pyproject.toml +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/scripts/release.config.sh +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/scripts/release.sh +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/scripts/verify.sh +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/setup.cfg +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/__main__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/_extras.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/args.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backendprovider.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agerowfactories.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agesearch.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agtype.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agtype_parser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/memgraph/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/memgraph/memgraphrowfactories.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cardinality.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/app.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/banner.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/command_manager.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/command_map.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/command_registry.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commandposition.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/add_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/apply_config_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/base_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/clear_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/commit_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/connect_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/create_edge_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/create_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/create_linked_node_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/create_node_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/delete_graphobj_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/disconnect_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/drop_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_backends_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_graphs_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_indexes_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_labels_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_models_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_parsed_query_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_schema_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/dump_statistics_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/execute_cypher_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/execute_file_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/exit_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/export_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/fetch_all_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/fetch_edges_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/fetch_nodes_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/format_output_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/get_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/gid_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/graph_exists_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/graph_op_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/graph_to_tree_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/help_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/import_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/last_result_op_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/load_models_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/resolve_edges_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/rollback_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/search_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/set_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/sql_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/update_graphobj_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/commands/use_graph_command.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/completer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/config.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/file_executor.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/graphdata.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/graphdb.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/_overview.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/_template.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/add_graph.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/exit.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/export_graph.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/import_graph.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help/last_result.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/help.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/prompt.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/promptgrammar.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/promptparser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/provider.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/renderer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/runtime.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/schema_cmd.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cli/settings.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/command_reader.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/config.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/connection_guard.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/Cypher.interp +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/Cypher.tokens +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherBaseListener.java +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherLexer.interp +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherLexer.java +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherLexer.tokens +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherListener.java +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/.antlr/CypherParser.java +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/Cypher.g4 +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/Cypher.interp +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/Cypher.tokens +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/CypherLexer.interp +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/CypherLexer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/CypherLexer.tokens +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/CypherListener.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/CypherParser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypher/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherbuilder.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/_host.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/batch.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/bulk_normalize.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/connection.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/criteria.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/cyphergraphdb.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/expand.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/pagination.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/pagination_mixin.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/result.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/schema.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/search.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/sql.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cyphergraphdb/stream_mixin.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherjson.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherparser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherquery/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherquery/builder.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherquery/params.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/cypherquery/patterns.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/dbpool.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/display.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/exceptions.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/formatprovider.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/graphops.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/main.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/modelinfo.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/modelprovider.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/modelrelations.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/models.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/options.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/py.typed +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/schema/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/schema/converter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/schema/core.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/schema/generator.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/settings.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/statistics.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/base_exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/base_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/csv_exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/csv_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/csv_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/data_flattener.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/excel_exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/excel_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/excel_row_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/file_exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/file_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/formats/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/formats/csv.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/formats/excel.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/formats/json.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/formats/yaml.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/hierarchical_exporter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/hierarchical_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/hierarchical_row_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/json_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/json_yaml_data_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/row_collector.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/row_set.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/row_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/tabular_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/tools/yaml_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/collection_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/column_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/connection_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/conversion_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/core_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/schema_merge.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/schema_to_llm.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/schema_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/settings_repr.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/utils/string_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/dependency_links.txt +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/entry_points.txt +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/requires.txt +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb.egg-info/top_level.txt +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/README.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/conftest.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/conftest.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_basic_operations.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_create_or_merge.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_cypher_query_builder.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_cypher_query_writes.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_example.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_expand.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_mass_delete.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_pagination.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_parameters.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_query_execution_error.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_read_only_mode.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/integration/test_streaming.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/README.md +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/cli/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/cli/test_cmd_map.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/cli/test_command_registry.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/cli/test_promptgrammar.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/cli/test_promptparser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/mock_backend.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_agtype_parser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_backend_capabilities.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_bulk_normalize.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_column_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_command_reader.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_connection_guard.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_cypherbuilder.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_cypherparser.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_cypherquery.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_cypherquery_facade.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_cypherquery_patterns.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_dbpool.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_dict_access_mixin.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_expand.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_extend_relation_decorator.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_extend_relations.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_formatprovider.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_graph_id_zero.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_graphops.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_indexing.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_json_schema_loading.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_json_schema_vs_decorators.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_model_inheritance.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_modelinfo.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_modelprovider_loading.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_modelprovider_schemas.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_models.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_pagination.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_prepared_statement_recovery.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_query_execution_error.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_query_timeout.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_schema_converter.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/test_settings.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/__init__.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_data_flattener.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_exporters.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_hierarchical_importer.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_json_yaml_data_source.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_resource_management.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/tools/test_tabular_import.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_collection_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_connection_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_conversion_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_core_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_schema_merge.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_schema_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_settings_repr.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/tests/unit/utils/test_string_utils.py +0 -0
- {cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cypher_graphdb
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: ORM like library and CLI for cypher query language supporting graph databases.
|
|
5
5
|
Author-email: Wolfgang Miller <wolfgang.miller@petrarca-labs.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -7,6 +7,7 @@ CypherBackend: Abstract base class for implementing graph database backends.
|
|
|
7
7
|
|
|
8
8
|
import abc
|
|
9
9
|
import re
|
|
10
|
+
from collections.abc import Sequence
|
|
10
11
|
from enum import Enum, auto
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
@@ -453,23 +454,27 @@ class CypherBackend(abc.ABC):
|
|
|
453
454
|
edges: list[dict],
|
|
454
455
|
src_label: str,
|
|
455
456
|
dst_label: str,
|
|
456
|
-
src_ref_prop: str = "id",
|
|
457
|
-
dst_ref_prop: str = "id",
|
|
457
|
+
src_ref_prop: str | Sequence[str] = "id",
|
|
458
|
+
dst_ref_prop: str | Sequence[str] = "id",
|
|
458
459
|
batch_size: int = 500,
|
|
459
460
|
) -> int:
|
|
460
|
-
"""Create edges in batches by matching src/dst nodes on a reference
|
|
461
|
+
"""Create edges in batches by matching src/dst nodes on a reference key.
|
|
461
462
|
|
|
462
463
|
Each dict in edges must have "src" and "dst" keys whose values match
|
|
463
464
|
the src_ref_prop/dst_ref_prop properties on source/destination nodes.
|
|
464
465
|
Any additional keys are set as properties on the created edge.
|
|
465
466
|
|
|
467
|
+
A ref prop may name a single property or an ordered sequence of properties
|
|
468
|
+
(a composite key); for a composite key each "src"/"dst" is a parallel
|
|
469
|
+
sequence of values in the same order.
|
|
470
|
+
|
|
466
471
|
Args:
|
|
467
472
|
label: Edge label for all created edges.
|
|
468
473
|
edges: List of dicts with "src", "dst", and optional edge properties.
|
|
469
474
|
src_label: Label of source nodes. Required for optimal performance on AGE.
|
|
470
475
|
dst_label: Label of destination nodes. Required for optimal performance on AGE.
|
|
471
|
-
src_ref_prop: Property
|
|
472
|
-
dst_ref_prop:
|
|
476
|
+
src_ref_prop: Property, or ordered sequence of properties, on source nodes to match against "src".
|
|
477
|
+
dst_ref_prop: As ``src_ref_prop``, for destination nodes.
|
|
473
478
|
batch_size: Number of edges per UNWIND batch.
|
|
474
479
|
|
|
475
480
|
Returns:
|
{cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agebulkwriter.py
RENAMED
|
@@ -14,6 +14,7 @@ committed immediately (DDL cannot run inside a user transaction in PostgreSQL)
|
|
|
14
14
|
without affecting any surrounding data already written by the caller.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
from collections.abc import Sequence
|
|
17
18
|
from typing import Literal
|
|
18
19
|
|
|
19
20
|
import psycopg
|
|
@@ -21,6 +22,7 @@ from loguru import logger
|
|
|
21
22
|
from psycopg.sql import SQL, Composed, Identifier
|
|
22
23
|
from psycopg.sql import Literal as SQLLiteral
|
|
23
24
|
|
|
25
|
+
from cypher_graphdb.bulk_refs import as_ref_props, ref_key
|
|
24
26
|
from cypher_graphdb.utils import chunk_list
|
|
25
27
|
|
|
26
28
|
from .ageserializer import escape_value
|
|
@@ -62,7 +64,7 @@ class AGEBulkWriter:
|
|
|
62
64
|
# edges all resolving against Method.qualified_name).
|
|
63
65
|
# Invalidated by invalidate_graphid_cache() after node inserts that
|
|
64
66
|
# change the graphid mapping.
|
|
65
|
-
self._graphid_cache: dict[tuple[str, str], dict[
|
|
67
|
+
self._graphid_cache: dict[tuple[str, tuple[str, ...]], dict[tuple, int]] = {}
|
|
66
68
|
# Cached list of edge label names for bulk_delete_nodes.
|
|
67
69
|
# Edge labels are stable within a connection lifetime -- populated on
|
|
68
70
|
# first call to _list_edge_labels() and reused for all subsequent deletes.
|
|
@@ -124,8 +126,8 @@ class AGEBulkWriter:
|
|
|
124
126
|
edges: list[dict],
|
|
125
127
|
src_label: str,
|
|
126
128
|
dst_label: str,
|
|
127
|
-
src_ref_prop: str = "id",
|
|
128
|
-
dst_ref_prop: str = "id",
|
|
129
|
+
src_ref_prop: str | Sequence[str] = "id",
|
|
130
|
+
dst_ref_prop: str | Sequence[str] = "id",
|
|
129
131
|
batch_size: int = 500,
|
|
130
132
|
) -> int:
|
|
131
133
|
"""Insert edges via direct SQL INSERT into the AGE edge label table.
|
|
@@ -143,8 +145,10 @@ class AGEBulkWriter:
|
|
|
143
145
|
edges: List of dicts with at least "src" and "dst" keys.
|
|
144
146
|
src_label: Vertex label of source nodes.
|
|
145
147
|
dst_label: Vertex label of destination nodes.
|
|
146
|
-
src_ref_prop: Property
|
|
147
|
-
|
|
148
|
+
src_ref_prop: Property (or ordered sequence of properties) on source
|
|
149
|
+
nodes to match "src" values. A sequence forms a composite key;
|
|
150
|
+
each edge's "src" is then a parallel sequence of values.
|
|
151
|
+
dst_ref_prop: As ``src_ref_prop``, for destination nodes.
|
|
148
152
|
batch_size: Number of rows per executemany batch.
|
|
149
153
|
|
|
150
154
|
Returns:
|
|
@@ -155,17 +159,20 @@ class AGEBulkWriter:
|
|
|
155
159
|
|
|
156
160
|
label_id = self._ensure_label(label, kind="e")
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
src_refs = {e["src"] for e in edges}
|
|
161
|
-
dst_refs = {e["dst"] for e in edges}
|
|
162
|
+
src_props = as_ref_props(src_ref_prop)
|
|
163
|
+
dst_props = as_ref_props(dst_ref_prop)
|
|
162
164
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
# Collect the unique (composite) ref keys we actually need -- avoids
|
|
166
|
+
# loading the entire label table (200K+ rows in a shared graph).
|
|
167
|
+
src_refs = {ref_key(e["src"], len(src_props)) for e in edges}
|
|
168
|
+
dst_refs = {ref_key(e["dst"], len(dst_props)) for e in edges}
|
|
169
|
+
|
|
170
|
+
# Build {ref_key: graphid} maps for only the referenced nodes
|
|
171
|
+
src_map = self._build_graphid_index(src_label, src_props, ref_values=src_refs)
|
|
172
|
+
if src_label == dst_label and src_props == dst_props:
|
|
173
|
+
dst_map = self._build_graphid_index(dst_label, dst_props, ref_values=src_refs | dst_refs)
|
|
167
174
|
else:
|
|
168
|
-
dst_map = self._build_graphid_index(dst_label,
|
|
175
|
+
dst_map = self._build_graphid_index(dst_label, dst_props, ref_values=dst_refs)
|
|
169
176
|
|
|
170
177
|
total = 0
|
|
171
178
|
skipped = 0
|
|
@@ -173,8 +180,8 @@ class AGEBulkWriter:
|
|
|
173
180
|
for batch in chunk_list(edges, batch_size):
|
|
174
181
|
value_parts = []
|
|
175
182
|
for edge in batch:
|
|
176
|
-
src_gid = src_map.get(edge["src"])
|
|
177
|
-
dst_gid = dst_map.get(edge["dst"])
|
|
183
|
+
src_gid = src_map.get(ref_key(edge["src"], len(src_props)))
|
|
184
|
+
dst_gid = dst_map.get(ref_key(edge["dst"], len(dst_props)))
|
|
178
185
|
if src_gid is None or dst_gid is None:
|
|
179
186
|
skipped += 1
|
|
180
187
|
continue
|
|
@@ -479,51 +486,60 @@ class AGEBulkWriter:
|
|
|
479
486
|
for k in keys_to_drop:
|
|
480
487
|
del self._graphid_cache[k]
|
|
481
488
|
|
|
482
|
-
def _build_graphid_index(
|
|
483
|
-
|
|
489
|
+
def _build_graphid_index(
|
|
490
|
+
self, label: str, ref_props: Sequence[str], ref_values: set[tuple] | None = None
|
|
491
|
+
) -> dict[tuple, int]:
|
|
492
|
+
"""Load a {ref_key_tuple: graphid} mapping for nodes of a label.
|
|
493
|
+
|
|
494
|
+
``ref_props`` is the ordered key: one property for a simple key, several
|
|
495
|
+
for a composite one. Keys in the returned map are value tuples aligned
|
|
496
|
+
with ``ref_props`` (always a tuple, even for a single prop), so a composite
|
|
497
|
+
key never collapses two nodes that merely share one property value.
|
|
484
498
|
|
|
485
|
-
When ``ref_values`` is provided
|
|
486
|
-
|
|
487
|
-
graphs where a label table may
|
|
488
|
-
|
|
489
|
-
edge batch.
|
|
499
|
+
When ``ref_values`` is provided (a set of value tuples), only loads
|
|
500
|
+
graphids for those specific keys (WHERE ... IN). Critical for shared
|
|
501
|
+
graphs where a label table may hold hundreds of thousands of rows across
|
|
502
|
+
all sources, but only a small subset is needed for the current edge batch.
|
|
490
503
|
|
|
491
504
|
When ``ref_values`` is None, loads the entire table (used for small
|
|
492
505
|
labels like structural edges).
|
|
493
506
|
|
|
494
|
-
Results are cached per (label,
|
|
507
|
+
Results are cached per (label, ref_props) and incrementally extended
|
|
495
508
|
when new ref_values are requested.
|
|
496
509
|
|
|
497
510
|
Values returned by agtype_access_operator are agtype-quoted strings
|
|
498
511
|
(e.g. '"mth:foo.bar"'), so we strip the surrounding quotes.
|
|
499
512
|
"""
|
|
500
|
-
|
|
513
|
+
props = tuple(ref_props)
|
|
514
|
+
cache_key = (label, props)
|
|
501
515
|
cached = self._graphid_cache.get(cache_key)
|
|
502
516
|
|
|
503
517
|
if ref_values is not None and cached is not None:
|
|
504
|
-
# Check if all requested
|
|
518
|
+
# Check if all requested keys are already in the cache
|
|
505
519
|
missing = ref_values - cached.keys()
|
|
506
520
|
if not missing:
|
|
507
521
|
return cached
|
|
508
|
-
# Only query the missing
|
|
522
|
+
# Only query the missing keys
|
|
509
523
|
ref_values = missing
|
|
510
524
|
|
|
511
525
|
if ref_values is not None and ref_values:
|
|
512
|
-
sql_stmt = SQLBuilder.lookup_node_graphids_filtered_sql(self._graph_name, label,
|
|
526
|
+
sql_stmt = SQLBuilder.lookup_node_graphids_filtered_sql(self._graph_name, label, props, ref_values)
|
|
513
527
|
else:
|
|
514
|
-
sql_stmt = SQLBuilder.lookup_node_graphids_sql(self._graph_name, label,
|
|
528
|
+
sql_stmt = SQLBuilder.lookup_node_graphids_sql(self._graph_name, label, props)
|
|
515
529
|
|
|
516
530
|
with self._conn.cursor() as cursor:
|
|
517
531
|
cursor.execute(sql_stmt)
|
|
518
532
|
rows = cursor.fetchall()
|
|
519
|
-
|
|
533
|
+
# Each row is (val_0, ..., val_n, graphid); the key is the tuple of values.
|
|
534
|
+
n = len(props)
|
|
535
|
+
result = {tuple(str(v).strip('"') for v in r[:n]): r[n] for r in rows}
|
|
520
536
|
|
|
521
537
|
# Merge into cache
|
|
522
538
|
if cached is not None:
|
|
523
539
|
cached.update(result)
|
|
524
|
-
logger.debug("Graphid index extended for {}.{}: +{} entries (total {})", label,
|
|
540
|
+
logger.debug("Graphid index extended for {}.{}: +{} entries (total {})", label, props, len(result), len(cached))
|
|
525
541
|
return cached
|
|
526
542
|
|
|
527
543
|
self._graphid_cache[cache_key] = result
|
|
528
|
-
logger.debug("Graphid index built for {}.{}: {} entries", label,
|
|
544
|
+
logger.debug("Graphid index built for {}.{}: {} entries", label, props, len(result))
|
|
529
545
|
return result
|
|
@@ -13,7 +13,7 @@ import hashlib
|
|
|
13
13
|
import json
|
|
14
14
|
import re
|
|
15
15
|
import time
|
|
16
|
-
from collections.abc import Iterator
|
|
16
|
+
from collections.abc import Iterator, Sequence
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
19
19
|
import psycopg
|
|
@@ -26,6 +26,7 @@ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_ex
|
|
|
26
26
|
|
|
27
27
|
# Import directly from the main package
|
|
28
28
|
from cypher_graphdb.backend import BackendCapability, CypherBackend, ExecStatistics, SqlStatistics
|
|
29
|
+
from cypher_graphdb.bulk_refs import as_ref_props, edge_match_pattern
|
|
29
30
|
|
|
30
31
|
# Deferred at module level to avoid a circular import:
|
|
31
32
|
# agegraphdb -> pagination (cyphergraphdb package) -> cyphergraphdb -> agegraphdb
|
|
@@ -1027,11 +1028,11 @@ class AGEGraphDB(CypherBackend):
|
|
|
1027
1028
|
edges: list[dict],
|
|
1028
1029
|
src_label: str,
|
|
1029
1030
|
dst_label: str,
|
|
1030
|
-
src_ref_prop: str = "id",
|
|
1031
|
-
dst_ref_prop: str = "id",
|
|
1031
|
+
src_ref_prop: str | Sequence[str] = "id",
|
|
1032
|
+
dst_ref_prop: str | Sequence[str] = "id",
|
|
1032
1033
|
batch_size: int = 500,
|
|
1033
1034
|
) -> int:
|
|
1034
|
-
"""Create edges in batches by matching src/dst nodes on a reference
|
|
1035
|
+
"""Create edges in batches by matching src/dst nodes on a reference key.
|
|
1035
1036
|
|
|
1036
1037
|
When ``direct_bulk_insert`` is True and both ``src_label`` and ``dst_label``
|
|
1037
1038
|
are specified, uses direct SQL INSERT via :class:`AGEBulkWriter` with
|
|
@@ -1042,8 +1043,10 @@ class AGEGraphDB(CypherBackend):
|
|
|
1042
1043
|
edges: List of dicts with at least "src" and "dst" keys.
|
|
1043
1044
|
src_label: Label of source nodes. Required for optimal performance on AGE.
|
|
1044
1045
|
dst_label: Label of destination nodes. Required for optimal performance on AGE.
|
|
1045
|
-
src_ref_prop: Property
|
|
1046
|
-
|
|
1046
|
+
src_ref_prop: Property (or ordered sequence of properties) on source
|
|
1047
|
+
nodes to match against "src". A sequence forms a composite key and
|
|
1048
|
+
each edge's "src" is then a parallel sequence of values.
|
|
1049
|
+
dst_ref_prop: As ``src_ref_prop``, for destination nodes.
|
|
1047
1050
|
batch_size: Number of edges per batch.
|
|
1048
1051
|
|
|
1049
1052
|
Returns:
|
|
@@ -1059,8 +1062,8 @@ class AGEGraphDB(CypherBackend):
|
|
|
1059
1062
|
)
|
|
1060
1063
|
|
|
1061
1064
|
# Cypher UNWIND fallback (direct_bulk_insert disabled).
|
|
1062
|
-
src_pat =
|
|
1063
|
-
dst_pat =
|
|
1065
|
+
src_pat = edge_match_pattern("a", src_label, as_ref_props(src_ref_prop), "src")
|
|
1066
|
+
dst_pat = edge_match_pattern("b", dst_label, as_ref_props(dst_ref_prop), "dst")
|
|
1064
1067
|
|
|
1065
1068
|
total = 0
|
|
1066
1069
|
for batch in chunk_list(edges, batch_size):
|
{cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/ageserializer.py
RENAMED
|
@@ -37,12 +37,14 @@ def escape_string_inner(v: str) -> str:
|
|
|
37
37
|
def escape_value(v: object) -> str:
|
|
38
38
|
"""Escape a Python value for inline AGE Cypher literals.
|
|
39
39
|
|
|
40
|
-
Handles None, bool, int, float, and str-coercible
|
|
41
|
-
escaped for double-quoted Cypher literals: backslashes,
|
|
42
|
-
newlines, carriage returns, tabs, and null bytes are all
|
|
40
|
+
Handles None, bool, int, float, list/tuple (nested), and str-coercible
|
|
41
|
+
values. Strings are escaped for double-quoted Cypher literals: backslashes,
|
|
42
|
+
double quotes, newlines, carriage returns, tabs, and null bytes are all
|
|
43
|
+
handled. A list/tuple becomes a Cypher list literal -- used for composite
|
|
44
|
+
edge-endpoint match keys (``e.src`` / ``e.dst``).
|
|
43
45
|
|
|
44
46
|
Args:
|
|
45
|
-
v: Python value (None, bool, int, float, or str-coercible).
|
|
47
|
+
v: Python value (None, bool, int, float, list/tuple, or str-coercible).
|
|
46
48
|
|
|
47
49
|
Returns:
|
|
48
50
|
Cypher literal string representation.
|
|
@@ -53,6 +55,8 @@ def escape_value(v: object) -> str:
|
|
|
53
55
|
return "true" if v else "false"
|
|
54
56
|
if isinstance(v, int | float):
|
|
55
57
|
return str(v)
|
|
58
|
+
if isinstance(v, (list, tuple)):
|
|
59
|
+
return "[" + ", ".join(escape_value(item) for item in v) + "]"
|
|
56
60
|
return f'"{escape_string_inner(str(v))}"'
|
|
57
61
|
|
|
58
62
|
|
{cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/age/agesqlbuilder.py
RENAMED
|
@@ -5,6 +5,7 @@ interface with Apache AGE graph functionality, including graph creation,
|
|
|
5
5
|
Cypher query execution, and full-text search operations.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from collections.abc import Sequence
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
|
|
10
11
|
from psycopg import sql
|
|
@@ -280,52 +281,77 @@ class SQLBuilder:
|
|
|
280
281
|
"WHERE g.name = %s AND l.name = %s"
|
|
281
282
|
)
|
|
282
283
|
|
|
283
|
-
@
|
|
284
|
-
def
|
|
285
|
-
"""
|
|
284
|
+
@staticmethod
|
|
285
|
+
def _prop_access(prop: str) -> sql.Composed:
|
|
286
|
+
"""agtype_access_operator expression reading one property, matching the expression index."""
|
|
287
|
+
return sql.SQL("ag_catalog.agtype_access_operator(VARIADIC ARRAY[properties, {prop}::ag_catalog.agtype])").format(
|
|
288
|
+
prop=sql.Literal(f'"{prop}"'),
|
|
289
|
+
)
|
|
286
290
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
@classmethod
|
|
292
|
+
def lookup_node_graphids_sql(cls, graph_name: str, label: str, ref_props: Sequence[str]) -> sql.Composed:
|
|
293
|
+
"""SQL to look up graphids of all nodes of a label by one or more property values.
|
|
294
|
+
|
|
295
|
+
Returns ``(ref_value_0, ..., ref_value_n, graphid)`` for every row in the
|
|
296
|
+
label table -- one value column per entry in ``ref_props``, in order, so
|
|
297
|
+
the caller can build a composite key. Use
|
|
298
|
+
``lookup_node_graphids_filtered_sql`` when only a subset is needed
|
|
299
|
+
(avoids scanning hundreds of thousands of rows).
|
|
290
300
|
"""
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
).format(
|
|
301
|
+
value_cols = sql.SQL(", ").join(cls._prop_access(p) + sql.SQL("::text") for p in ref_props)
|
|
302
|
+
return sql.SQL("SELECT {cols}, id FROM {schema}.{table}").format(
|
|
303
|
+
cols=value_cols,
|
|
295
304
|
schema=sql.Identifier(graph_name),
|
|
296
305
|
table=sql.Identifier(label),
|
|
297
|
-
prop=sql.Literal(f'"{ref_prop}"'),
|
|
298
306
|
)
|
|
299
307
|
|
|
300
308
|
@classmethod
|
|
301
|
-
def lookup_node_graphids_filtered_sql(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
expression index
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
def lookup_node_graphids_filtered_sql(
|
|
310
|
+
cls, graph_name: str, label: str, ref_props: Sequence[str], ref_values: set
|
|
311
|
+
) -> sql.Composed:
|
|
312
|
+
"""SQL to look up graphids for a specific set of (composite) property values.
|
|
313
|
+
|
|
314
|
+
``ref_props`` is the ordered list of key properties; ``ref_values`` is a
|
|
315
|
+
set of value tuples (one element per prop, same order).
|
|
316
|
+
|
|
317
|
+
A single prop uses ``WHERE access IN (v, ...)`` -- the fast path that hits
|
|
318
|
+
the expression index. A composite key uses ``WHERE (a0 = v0 AND a1 = v1)
|
|
319
|
+
OR (...) OR ...``: AGE's agtype does not support SQL row-value comparison
|
|
320
|
+
(``(a, b) IN ((x, y))`` raises "cannot cast agtype string to type
|
|
321
|
+
boolean"), so the key is expanded into per-tuple AND groups.
|
|
322
|
+
|
|
323
|
+
Values are cast to ``::ag_catalog.agtype`` to match the expression index
|
|
324
|
+
type. Values containing double quotes or backslashes are escaped for the
|
|
325
|
+
agtype JSON string format before being wrapped via sql.Literal -- identical
|
|
326
|
+
escaping to the Cypher UNWIND path.
|
|
313
327
|
"""
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
328
|
+
|
|
329
|
+
def agtype_literal(v: str) -> sql.SQL:
|
|
330
|
+
return sql.SQL(sql.Literal('"' + escape_string_inner(v) + '"').as_string(None) + "::ag_catalog.agtype")
|
|
331
|
+
|
|
332
|
+
value_cols = sql.SQL(", ").join(cls._prop_access(p) + sql.SQL("::text") for p in ref_props)
|
|
333
|
+
|
|
334
|
+
# Match each key tuple as an AND of per-prop equalities, OR'd across tuples.
|
|
335
|
+
# agtype equality must use the ag_catalog.= operator -- a plain "=" (or a
|
|
336
|
+
# ::text cast) raises "cannot cast agtype string to type boolean". This
|
|
337
|
+
# uniform form covers both the single-prop and composite cases.
|
|
338
|
+
accessors = [cls._prop_access(p) for p in ref_props]
|
|
339
|
+
groups = [
|
|
340
|
+
sql.SQL("(")
|
|
341
|
+
+ sql.SQL(" AND ").join(
|
|
342
|
+
sql.SQL("{a} OPERATOR(ag_catalog.=) {v}").format(a=accessors[i], v=agtype_literal(v))
|
|
343
|
+
for i, v in enumerate(value_tuple)
|
|
344
|
+
)
|
|
345
|
+
+ sql.SQL(")")
|
|
346
|
+
for value_tuple in ref_values
|
|
347
|
+
]
|
|
348
|
+
where = sql.SQL(" OR ").join(groups)
|
|
349
|
+
|
|
350
|
+
return sql.SQL("SELECT {cols}, id FROM {schema}.{table} WHERE {where}").format(
|
|
351
|
+
cols=value_cols,
|
|
325
352
|
schema=sql.Identifier(graph_name),
|
|
326
353
|
table=sql.Identifier(label),
|
|
327
|
-
|
|
328
|
-
in_vals=sql.SQL(in_values),
|
|
354
|
+
where=where,
|
|
329
355
|
)
|
|
330
356
|
|
|
331
357
|
@classmethod
|
{cypher_graphdb-0.7.2 → cypher_graphdb-0.7.3}/src/cypher_graphdb/backends/memgraph/memgraphdb.py
RENAMED
|
@@ -8,7 +8,7 @@ Classes:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import contextlib
|
|
11
|
-
from collections.abc import Generator
|
|
11
|
+
from collections.abc import Generator, Sequence
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
14
|
import mgclient
|
|
@@ -17,6 +17,7 @@ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_ex
|
|
|
17
17
|
|
|
18
18
|
# Import directly from the main package
|
|
19
19
|
from cypher_graphdb.backend import BackendCapability, CypherBackend, ExecStatistics
|
|
20
|
+
from cypher_graphdb.bulk_refs import as_ref_props, edge_match_pattern, ref_param
|
|
20
21
|
from cypher_graphdb.cyphergraphdb.pagination import Page
|
|
21
22
|
from cypher_graphdb.cyphergraphdb.pagination_mixin import _resolve_col_names
|
|
22
23
|
from cypher_graphdb.cypherparser import ParsedCypherQuery
|
|
@@ -660,8 +661,8 @@ class MemgraphDB(CypherBackend):
|
|
|
660
661
|
edges: list[dict],
|
|
661
662
|
src_label: str,
|
|
662
663
|
dst_label: str,
|
|
663
|
-
src_ref_prop: str = "id",
|
|
664
|
-
dst_ref_prop: str = "id",
|
|
664
|
+
src_ref_prop: str | Sequence[str] = "id",
|
|
665
|
+
dst_ref_prop: str | Sequence[str] = "id",
|
|
665
666
|
batch_size: int = 500,
|
|
666
667
|
) -> int:
|
|
667
668
|
"""Create edges in batches using parameterized UNWIND.
|
|
@@ -669,13 +670,17 @@ class MemgraphDB(CypherBackend):
|
|
|
669
670
|
Each dict in edges must have "src" and "dst" keys. Additional keys
|
|
670
671
|
are set as edge properties.
|
|
671
672
|
|
|
673
|
+
A ref prop may name a single property or an ordered sequence of properties
|
|
674
|
+
(a composite key); for a composite key each "src"/"dst" is a parallel
|
|
675
|
+
sequence of values matched element-wise against the props.
|
|
676
|
+
|
|
672
677
|
Args:
|
|
673
678
|
label: Edge label for all created edges.
|
|
674
679
|
edges: List of dicts with "src", "dst", and optional edge properties.
|
|
675
680
|
src_label: Label of source nodes. Required for optimal performance on AGE.
|
|
676
681
|
dst_label: Label of destination nodes. Required for optimal performance on AGE.
|
|
677
|
-
src_ref_prop: Property
|
|
678
|
-
dst_ref_prop:
|
|
682
|
+
src_ref_prop: Property, or ordered sequence of properties, on source nodes to match against "src".
|
|
683
|
+
dst_ref_prop: As ``src_ref_prop``, for destination nodes.
|
|
679
684
|
batch_size: Number of edges per UNWIND batch.
|
|
680
685
|
|
|
681
686
|
Returns:
|
|
@@ -685,9 +690,12 @@ class MemgraphDB(CypherBackend):
|
|
|
685
690
|
if not edges:
|
|
686
691
|
return 0
|
|
687
692
|
|
|
693
|
+
src_props = as_ref_props(src_ref_prop)
|
|
694
|
+
dst_props = as_ref_props(dst_ref_prop)
|
|
695
|
+
|
|
688
696
|
# Build MATCH patterns from label/prop names (not user values -- safe to inline)
|
|
689
|
-
src_pat =
|
|
690
|
-
dst_pat =
|
|
697
|
+
src_pat = edge_match_pattern("a", src_label, src_props, "src")
|
|
698
|
+
dst_pat = edge_match_pattern("b", dst_label, dst_props, "dst")
|
|
691
699
|
|
|
692
700
|
# Collect edge property keys (beyond src/dst) from first row
|
|
693
701
|
edge_prop_keys = [k for k in edges[0] if k not in ("src", "dst")]
|
|
@@ -698,6 +706,10 @@ class MemgraphDB(CypherBackend):
|
|
|
698
706
|
|
|
699
707
|
cypher = f"UNWIND $edges AS e MATCH {src_pat} MATCH {dst_pat} CREATE (a)-[r:{label}]->(b){set_clause}"
|
|
700
708
|
|
|
709
|
+
# mgclient rejects tuples as query parameters; coerce each endpoint by its own
|
|
710
|
+
# key arity (source and destination may differ).
|
|
711
|
+
edges = [{**e, "src": ref_param(e["src"], len(src_props)), "dst": ref_param(e["dst"], len(dst_props))} for e in edges]
|
|
712
|
+
|
|
701
713
|
total = 0
|
|
702
714
|
for batch in chunk_list(edges, batch_size):
|
|
703
715
|
cursor = self._conn.cursor()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Composite endpoint keys for bulk edge creation.
|
|
2
|
+
|
|
3
|
+
``bulk_create_edges`` matches each endpoint node by a *reference key* -- one or
|
|
4
|
+
more properties whose values identify the node. A single property is the common
|
|
5
|
+
case (``"symbol"``), but it is only correct when that property is unique within
|
|
6
|
+
the label. When it is not (e.g. the same ``symbol`` exists in several sources of
|
|
7
|
+
a shared graph), the key must be composite (``("symbol", "source_key")``) so an
|
|
8
|
+
edge binds to the intended node rather than an arbitrary match.
|
|
9
|
+
|
|
10
|
+
These helpers normalise the ``str | Sequence[str]`` ref-prop argument and turn an
|
|
11
|
+
edge's ``src``/``dst`` value (scalar or tuple) into a hashable key aligned with
|
|
12
|
+
the prop order.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Sequence
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def as_ref_props(ref_prop: str | Sequence[str]) -> tuple[str, ...]:
|
|
22
|
+
"""Normalise a ref-prop argument to an ordered tuple of property names.
|
|
23
|
+
|
|
24
|
+
A plain string becomes a one-tuple; a sequence is kept in order. Empty
|
|
25
|
+
sequences are rejected -- an endpoint must be matched on at least one prop.
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(ref_prop, str):
|
|
28
|
+
return (ref_prop,)
|
|
29
|
+
props = tuple(ref_prop)
|
|
30
|
+
if not props:
|
|
31
|
+
raise ValueError("ref_prop must name at least one property")
|
|
32
|
+
if not all(isinstance(p, str) for p in props):
|
|
33
|
+
raise TypeError("ref_prop sequence must contain only property-name strings")
|
|
34
|
+
return props
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ref_key(value: Any, n_props: int) -> tuple[Any, ...]:
|
|
38
|
+
"""Turn an edge's src/dst match value into a key tuple of length ``n_props``.
|
|
39
|
+
|
|
40
|
+
For a single-prop key the value is a scalar (wrapped into a one-tuple); for a
|
|
41
|
+
composite key the value must be a sequence whose length matches ``n_props``.
|
|
42
|
+
A tuple key is always returned so callers can index a composite map uniformly.
|
|
43
|
+
"""
|
|
44
|
+
if n_props == 1:
|
|
45
|
+
# A one-element sequence is accepted too, but a bare scalar is the norm.
|
|
46
|
+
if isinstance(value, (list, tuple)):
|
|
47
|
+
seq = tuple(value)
|
|
48
|
+
if len(seq) != 1:
|
|
49
|
+
raise ValueError(f"expected 1 match value, got {len(seq)}")
|
|
50
|
+
return seq
|
|
51
|
+
return (value,)
|
|
52
|
+
if not isinstance(value, (list, tuple)):
|
|
53
|
+
raise TypeError(f"composite key needs a sequence of {n_props} values, got {type(value).__name__}")
|
|
54
|
+
seq = tuple(value)
|
|
55
|
+
if len(seq) != n_props:
|
|
56
|
+
raise ValueError(f"expected {n_props} match values, got {len(seq)}")
|
|
57
|
+
return seq
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def ref_param(value: Any, n_props: int) -> Any:
|
|
61
|
+
"""Coerce one endpoint's match value to a driver-friendly query parameter.
|
|
62
|
+
|
|
63
|
+
A single-prop key stays a scalar; a composite key becomes a list (some drivers,
|
|
64
|
+
e.g. mgclient, reject tuples as parameters). Decided per endpoint from that
|
|
65
|
+
endpoint's own key arity: source and destination may differ (a composite caller
|
|
66
|
+
matched to a single-prop target), and coercing both on one endpoint's arity would
|
|
67
|
+
split a scalar string into characters.
|
|
68
|
+
"""
|
|
69
|
+
return list(ref_key(value, n_props)) if n_props > 1 else value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def edge_match_pattern(alias: str, node_label: str | None, props: Sequence[str], ref: str) -> str:
|
|
73
|
+
"""Build a Cypher node-match pattern for one edge endpoint inside ``UNWIND ... AS e``.
|
|
74
|
+
|
|
75
|
+
``ref`` is the UNWIND field carrying the match value ("src" or "dst"). A
|
|
76
|
+
single-prop key matches the scalar (``e.src``); a composite key matches each
|
|
77
|
+
property against the corresponding list element (``e.src[0]``, ``e.src[1]``, ...).
|
|
78
|
+
An empty ``node_label`` matches nodes of any label.
|
|
79
|
+
"""
|
|
80
|
+
if len(props) == 1:
|
|
81
|
+
body = f"{props[0]}: e.{ref}"
|
|
82
|
+
else:
|
|
83
|
+
body = ", ".join(f"{p}: e.{ref}[{i}]" for i, p in enumerate(props))
|
|
84
|
+
head = f"{alias}:{node_label}" if node_label else alias
|
|
85
|
+
return f"({head} {{{body}}})"
|
|
@@ -198,11 +198,11 @@ class IndexingMixin(_Host):
|
|
|
198
198
|
src_refs: Sequence[Any] | None = None,
|
|
199
199
|
dst_refs: Sequence[Any] | None = None,
|
|
200
200
|
label: str | None = None,
|
|
201
|
-
src_ref_prop: str = "id",
|
|
202
|
-
dst_ref_prop: str = "id",
|
|
201
|
+
src_ref_prop: str | Sequence[str] = "id",
|
|
202
|
+
dst_ref_prop: str | Sequence[str] = "id",
|
|
203
203
|
batch_size: int = 500,
|
|
204
204
|
) -> int:
|
|
205
|
-
"""Create edges in batches by matching src/dst nodes on a reference
|
|
205
|
+
"""Create edges in batches by matching src/dst nodes on a reference key.
|
|
206
206
|
|
|
207
207
|
Accepts either plain dicts (traditional shape) or decorated
|
|
208
208
|
``GraphEdge`` instances (typed shape).
|
|
@@ -215,6 +215,15 @@ class IndexingMixin(_Host):
|
|
|
215
215
|
``src_refs`` / ``dst_refs`` lists; the edge properties come from
|
|
216
216
|
``flatten_properties()`` on each instance.
|
|
217
217
|
|
|
218
|
+
A ref prop may name a **single** property (the common case) or an ordered
|
|
219
|
+
**sequence** of properties forming a composite key. A composite key is the
|
|
220
|
+
correct choice when no single property is unique within the label (e.g. the
|
|
221
|
+
same ``symbol`` exists in several sources of a shared graph): matching on
|
|
222
|
+
``("symbol", "source_key")`` binds each edge to the intended node instead of
|
|
223
|
+
an arbitrary one that happens to share the symbol. For a composite key each
|
|
224
|
+
``src`` / ``dst`` (or ``src_refs`` / ``dst_refs`` element) is a parallel
|
|
225
|
+
sequence of values, in the same order as the props.
|
|
226
|
+
|
|
218
227
|
Args:
|
|
219
228
|
edges: Either a list of dicts (each with ``src``/``dst`` + props)
|
|
220
229
|
or a list of ``GraphEdge`` instances (all sharing the same
|
|
@@ -226,8 +235,9 @@ class IndexingMixin(_Host):
|
|
|
226
235
|
label: Edge label. Required for dicts; optional for typed input.
|
|
227
236
|
src_label: Label of source nodes. Required for optimal performance on AGE.
|
|
228
237
|
dst_label: Label of destination nodes. Required for optimal performance on AGE.
|
|
229
|
-
src_ref_prop: Property
|
|
230
|
-
|
|
238
|
+
src_ref_prop: Property, or ordered sequence of properties (composite
|
|
239
|
+
key), on source nodes to match against src refs.
|
|
240
|
+
dst_ref_prop: As ``src_ref_prop``, for destination nodes.
|
|
231
241
|
batch_size: Number of edges per batch.
|
|
232
242
|
|
|
233
243
|
Returns:
|
|
@@ -254,6 +264,17 @@ class IndexingMixin(_Host):
|
|
|
254
264
|
src_ref_prop="qualified_name",
|
|
255
265
|
dst_ref_prop="qualified_name",
|
|
256
266
|
)
|
|
267
|
+
|
|
268
|
+
Composite key (disambiguate a non-unique property)::
|
|
269
|
+
|
|
270
|
+
cdb.bulk_create_edges(
|
|
271
|
+
calls,
|
|
272
|
+
src_refs=[("mod.foo.bar", "svc-a"), ("mod.foo.baz", "svc-a")],
|
|
273
|
+
dst_refs=[("mod.target.a", "svc-a"), ("mod.target.b", "svc-a")],
|
|
274
|
+
src_label="Method", dst_label="Method",
|
|
275
|
+
src_ref_prop=("qualified_name", "source_key"),
|
|
276
|
+
dst_ref_prop=("qualified_name", "source_key"),
|
|
277
|
+
)
|
|
257
278
|
"""
|
|
258
279
|
assert self._backend
|
|
259
280
|
derived_label, edge_dicts = normalize_edges_input(edges, src_refs, dst_refs, label)
|