iris-vector-graph 1.62.1__tar.gz → 1.63.1__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.
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/PKG-INFO +7 -3
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/README.md +4 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/bulk_loader.py +5 -5
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/translator.py +3 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/engine.py +85 -25
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/schema.py +6 -31
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/pyproject.toml +3 -3
- iris_vector_graph-1.63.1/tests/unit/test_bfs_arno.py +249 -0
- iris_vector_graph-1.63.1/tests/unit/test_cypher_benchmark_scale.py +213 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/.gitignore +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/LICENSE +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/demo_biomedical.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/demo_fraud_detection.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/demo_fraud_detection_sql.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/demo_utils.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/demo_working_system.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical/loaders.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical/resolver.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical/types.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical_legacy/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical_legacy/biomedical_engine.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical_legacy/biomedical_schema.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/biomedical_legacy/legacy_wrapper.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/fraud/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/fraud/loaders.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/fraud/resolver.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/domains/fraud/types.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/graphQL.http +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/hybrid_vector_graph_query.cypher +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/examples/rest.http +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Algorithms.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/ArnoAccel.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/BM25Index.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/BenchFormat.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/BenchSeeder.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Benchmark.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Edge.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/EdgeScan.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/GraphIndex.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/IVFIndex.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Loader.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/MCPService.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/MCPToolSet.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/MCPTools.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Meta.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/PLAIDSearch.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/PageRank.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/PyOps.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Service.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Snapshot.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Subgraph.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/TemporalIndex.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/TestEdge.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/Traversal.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/Graph/KG/VecIndex.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/PageRankEmbedded.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/User.Exec.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_src/src/iris/vector/graph/GraphOperators.cls +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/bolt_server.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/capabilities.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/algorithms/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/algorithms/paths.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/ast.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/lexer.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/parser.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher_api.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/embedded.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/fusion.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/constants.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/engine.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/pooling.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/resolvers.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/gql/schema.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/models.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/operators.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/py.typed +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/security.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/text_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/utils.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/vector_utils.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/fhir_bridges.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/fraud_sample_data.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/globals_schema.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/graph_path_globals.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/graph_walk_tvf.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/migrations/000_base_schema_iris.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/migrations/001_add_nodepk_table.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/migrations/001_rollback_nodepk.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/migrations/002_add_fk_constraints.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/operators.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/operators_fixed.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/procedures/kg_PageRank.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/rdf_reifications.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/sql/schema.sql +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/TESTING.md +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmark_parser.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/benchmark_neo4j.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/bfs_benchmark.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/establish_baseline.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/graph_gen.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/iris_baseline_run.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/iris_os_run.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/load_neo4j.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/benchmarks/synthetic_baseline.csv +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/conftest.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/test_cypher_api.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/test_cypher_api_errors.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/test_graphql_queries.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/test_graphql_schema.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/contract/test_ppr_api.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/curl_suite.sh +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/conftest.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_biomedical_demo.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_biomedical_ui.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_cypher_coerce_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_cypher_sprints_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_cypher_vector_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_fhir_bridges_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_fraud_demo.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_fraud_ui.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_gql_autogen_startup.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_gql_cypher_passthrough.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_gql_node_queries.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_gql_semantic_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_gql_traversal.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_graph_kernels_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_hla_kg_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_multi_query_engine_platform.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_named_paths_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_nkg_index_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_operator_wiring_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_plaid_search_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_ppr_cls_fast_path.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_ppr_guided_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_procedure_installation.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_reification_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_schema_procedures_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_subgraph_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_subquery_call_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/e2e/test_vecindex_e2e.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/conftest.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/gql/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/gql/test_graphql_mutations.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/gql/test_graphql_nested_queries.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/gql/test_graphql_queries.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/gql/test_graphql_vector_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_bidirectional_ppr.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cls_layer.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_advanced.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_enhancements.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_multi_type.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_rd.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_rel_vars.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_single_type.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_untyped.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_cypher_vector_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_embeddings_api.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_fastapi_graphql.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_fhir_bridges_integration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_named_paths_integration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_advanced_benchmarks.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_constraints.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_graph_analytics.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_migration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_performance.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_nodepk_production_scale.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_objectscript_classes.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_pagerank_sql_optimization.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_reification_integration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_schema_migration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_stored_procedure_install.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/integration/test_subquery_call_integration.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/performance/conftest.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/performance/scale_benchmark.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/performance/test_ppr_stress.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/performance/test_stress_v1_5.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/run_all_tests.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_iris_rest_api.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_networkx_loader.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_performance_benchmarks.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_pyops_vector_conversion.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_python_operators.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_python_sdk.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_schema_validation.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_sql_queries.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/python/test_vector_functions.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/cypher/__init__.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/cypher/test_lexer.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/cypher/test_lexer_advanced.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/cypher/test_parser.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/cypher/test_parser_advanced.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_batch_mutations.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_bm25_index.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_bolt_server.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cls_deployment.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_benchmark.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_case_when.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_e2e_new_features.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_functions.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_parser.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_posos_bugs.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_procedures.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_translator.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_union_exists.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_var_length.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_cypher_vector_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_edge_embeddings.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_edgeprop_ndjson.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_embedded.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_engine_dimension_fix.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_engine_embeddings.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_fhir_bridges.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_get_nodes.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_graph_kernels.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_graphql_dataloader.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_ingest_formats.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_ivf_index.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_named_graphs.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_named_paths.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_operators_wiring.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_plaid_search.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_ppr_guided_subgraph.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_reification.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_schema_init.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_schema_procedures.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_shortest_path.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_snapshot.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_sql_splitter.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_sql_table_bridge.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_subgraph.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_subquery_call.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_temporal_cypher.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_temporal_edges.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_unified_edge_store.py +0 -0
- {iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/tests/unit/test_weighted_shortest_path.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-vector-graph
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.63.1
|
|
4
4
|
Summary: Transactional Graph + Vector retrieval system for InterSystems IRIS with hybrid search, openCypher, and GraphQL APIs
|
|
5
5
|
Project-URL: Homepage, https://github.com/intersystems-community/iris-vector-graph
|
|
6
6
|
Project-URL: Documentation, https://github.com/intersystems-community/iris-vector-graph/tree/main/docs
|
|
@@ -23,14 +23,14 @@ Classifier: Topic :: Database
|
|
|
23
23
|
Classifier: Topic :: Scientific/Engineering
|
|
24
24
|
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
25
25
|
Requires-Python: >=3.10
|
|
26
|
-
Requires-Dist: intersystems-
|
|
26
|
+
Requires-Dist: intersystems-iris>=1.0.0
|
|
27
27
|
Provides-Extra: biodata
|
|
28
28
|
Requires-Dist: biopython>=1.81; extra == 'biodata'
|
|
29
29
|
Requires-Dist: bioservices>=1.11.0; extra == 'biodata'
|
|
30
30
|
Requires-Dist: mygene>=3.2.0; extra == 'biodata'
|
|
31
31
|
Requires-Dist: obonet>=1.0.0; extra == 'biodata'
|
|
32
32
|
Provides-Extra: core
|
|
33
|
-
Requires-Dist: intersystems-
|
|
33
|
+
Requires-Dist: intersystems-iris>=1.0.0; extra == 'core'
|
|
34
34
|
Provides-Extra: demo
|
|
35
35
|
Requires-Dist: python-fasthtml>=0.12.0; extra == 'demo'
|
|
36
36
|
Provides-Extra: dev
|
|
@@ -655,6 +655,10 @@ anchors = engine.get_kg_anchors(icd_codes=["J18.0", "E11.9"])
|
|
|
655
655
|
|
|
656
656
|
## Changelog
|
|
657
657
|
|
|
658
|
+
### v1.63.0 (2026-04-25)
|
|
659
|
+
- feat: Arno/Rust fast path for BFS (`_execute_var_length_cypher`) — when `libarno_callout.so` is loaded with `Graph.KG.NKGAccel.BFSJson`, var-length Cypher queries use Rust BFS over `^NKG` integer adjacency instead of ObjectScript `BFSFastJson`. Projected 128ms → <30ms p50 for 6K+ result BFS at 10K/50K scale. Falls back transparently to `BFSFastJson` when Arno not loaded. (spec 079, arno spec 035)
|
|
660
|
+
|
|
661
|
+
|
|
658
662
|
### v1.62.1 (2026-04-25)
|
|
659
663
|
- fix: `WITH n, count(r) AS cnt WHERE cnt > N` — IRIS SQLCODE -23 fixed; CTEs containing GROUP BY now emit inline subqueries `FROM (...GROUP BY...) Stage1` instead of `WITH Stage1 AS (...GROUP BY...) SELECT ... FROM Stage1` (IRIS 2025.x doesn't support aggregation in CTEs)
|
|
660
664
|
- fix: `WITH HAVING` now uses the full aggregate expression (e.g. `COUNT(e.p) >= 2`) not the alias (`cnt >= 2`) — IRIS doesn't allow column aliases in HAVING
|
|
@@ -583,6 +583,10 @@ anchors = engine.get_kg_anchors(icd_codes=["J18.0", "E11.9"])
|
|
|
583
583
|
|
|
584
584
|
## Changelog
|
|
585
585
|
|
|
586
|
+
### v1.63.0 (2026-04-25)
|
|
587
|
+
- feat: Arno/Rust fast path for BFS (`_execute_var_length_cypher`) — when `libarno_callout.so` is loaded with `Graph.KG.NKGAccel.BFSJson`, var-length Cypher queries use Rust BFS over `^NKG` integer adjacency instead of ObjectScript `BFSFastJson`. Projected 128ms → <30ms p50 for 6K+ result BFS at 10K/50K scale. Falls back transparently to `BFSFastJson` when Arno not loaded. (spec 079, arno spec 035)
|
|
588
|
+
|
|
589
|
+
|
|
586
590
|
### v1.62.1 (2026-04-25)
|
|
587
591
|
- fix: `WITH n, count(r) AS cnt WHERE cnt > N` — IRIS SQLCODE -23 fixed; CTEs containing GROUP BY now emit inline subqueries `FROM (...GROUP BY...) Stage1` instead of `WITH Stage1 AS (...GROUP BY...) SELECT ... FROM Stage1` (IRIS 2025.x doesn't support aggregation in CTEs)
|
|
588
592
|
- fix: `WITH HAVING` now uses the full aggregate expression (e.g. `COUNT(e.p) >= 2`) not the alias (`cnt >= 2`) — IRIS doesn't allow column aliases in HAVING
|
|
@@ -273,8 +273,8 @@ class BulkLoader:
|
|
|
273
273
|
Must be called after load_edges(use_noindex=True).
|
|
274
274
|
Also rebuilds bitmap extent indexes for correct COUNT(*).
|
|
275
275
|
"""
|
|
276
|
-
import
|
|
277
|
-
iris_obj =
|
|
276
|
+
import iris
|
|
277
|
+
iris_obj = iris.createIRIS(self.conn)
|
|
278
278
|
results = {}
|
|
279
279
|
|
|
280
280
|
for cls in ["Graph.KG.rdfedges", "Graph.KG.rdflabels", "Graph.KG.rdfprops", "Graph.KG.nodes"]:
|
|
@@ -301,8 +301,8 @@ class BulkLoader:
|
|
|
301
301
|
Returns True if successful.
|
|
302
302
|
"""
|
|
303
303
|
try:
|
|
304
|
-
import
|
|
305
|
-
iris_obj =
|
|
304
|
+
import iris
|
|
305
|
+
iris_obj = iris.createIRIS(self.conn)
|
|
306
306
|
logger.info("Building ^KG + ^NKG globals from SQL tables...")
|
|
307
307
|
t0 = time.time()
|
|
308
308
|
iris_obj.classMethodVoid("Graph.KG.Traversal", "BuildKG")
|
|
@@ -422,7 +422,7 @@ def main():
|
|
|
422
422
|
logger.info(f"Graph: {G.number_of_nodes():,} nodes, {G.number_of_edges():,} edges")
|
|
423
423
|
|
|
424
424
|
|
|
425
|
-
from
|
|
425
|
+
from iris.dbapi._DBAPI import connect # intersystems_iris.dbapi = iris.dbapi
|
|
426
426
|
conn = connect(args.host, args.port, args.namespace, args.user, args.password)
|
|
427
427
|
logger.info(f"Connected to IRIS {args.host}:{args.port}/{args.namespace}")
|
|
428
428
|
|
{iris_vector_graph-1.62.1 → iris_vector_graph-1.63.1}/iris_vector_graph/cypher/translator.py
RENAMED
|
@@ -1744,6 +1744,9 @@ def translate_relationship_pattern(
|
|
|
1744
1744
|
def _resolve_id_param(node):
|
|
1745
1745
|
id_val = node.properties.get("id")
|
|
1746
1746
|
if id_val is None:
|
|
1747
|
+
if node.variable and node.variable in context.input_params:
|
|
1748
|
+
val = context.input_params[node.variable]
|
|
1749
|
+
return f"${node.variable}" if isinstance(val, str) else None
|
|
1747
1750
|
return None
|
|
1748
1751
|
if isinstance(id_val, ast.Variable):
|
|
1749
1752
|
return f"${id_val.name}"
|
|
@@ -1025,6 +1025,31 @@ class IRISGraphEngine:
|
|
|
1025
1025
|
source_id = _resolve(vl.get("src_id_param"))
|
|
1026
1026
|
target_id = _resolve(vl.get("dst_id_param"))
|
|
1027
1027
|
|
|
1028
|
+
if source_id is None and parameters:
|
|
1029
|
+
src_var = vl.get("source_var")
|
|
1030
|
+
if src_var and src_var in parameters:
|
|
1031
|
+
source_id = str(parameters[src_var])
|
|
1032
|
+
else:
|
|
1033
|
+
source_id = next(
|
|
1034
|
+
(str(v) for v in parameters.values() if isinstance(v, str)), None
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
if target_id is None and parameters:
|
|
1038
|
+
dst_var = vl.get("target_var")
|
|
1039
|
+
if dst_var and dst_var in parameters:
|
|
1040
|
+
target_id = str(parameters[dst_var])
|
|
1041
|
+
else:
|
|
1042
|
+
vals = [str(v) for v in parameters.values() if isinstance(v, str)]
|
|
1043
|
+
target_id = vals[1] if len(vals) > 1 else None
|
|
1044
|
+
|
|
1045
|
+
if source_id is None or target_id is None:
|
|
1046
|
+
sql_params = sql_query.parameters[0] if sql_query.parameters else []
|
|
1047
|
+
str_params = [p for p in sql_params if isinstance(p, str) and not p.startswith("Graph_KG")]
|
|
1048
|
+
if source_id is None and len(str_params) >= 1:
|
|
1049
|
+
source_id = str_params[0]
|
|
1050
|
+
if target_id is None and len(str_params) >= 2:
|
|
1051
|
+
target_id = str_params[1]
|
|
1052
|
+
|
|
1028
1053
|
if source_id is None or target_id is None:
|
|
1029
1054
|
raise ValueError(
|
|
1030
1055
|
"shortestPath requires both source and target node IDs to be bound. "
|
|
@@ -1131,7 +1156,11 @@ class IRISGraphEngine:
|
|
|
1131
1156
|
source_id = item
|
|
1132
1157
|
break
|
|
1133
1158
|
if source_id is None and parameters:
|
|
1134
|
-
|
|
1159
|
+
src_var = vl.get("source_var")
|
|
1160
|
+
if src_var and src_var in parameters:
|
|
1161
|
+
source_id = str(parameters[src_var])
|
|
1162
|
+
else:
|
|
1163
|
+
source_id = next(iter(parameters.values()), None)
|
|
1135
1164
|
|
|
1136
1165
|
if source_id is None:
|
|
1137
1166
|
return {
|
|
@@ -1142,26 +1171,52 @@ class IRISGraphEngine:
|
|
|
1142
1171
|
"metadata": sql_query.query_metadata,
|
|
1143
1172
|
}
|
|
1144
1173
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1174
|
+
max_results = 0
|
|
1175
|
+
if sql_query.sql:
|
|
1176
|
+
import re as _re
|
|
1177
|
+
sql_str = sql_query.sql if isinstance(sql_query.sql, str) else (sql_query.sql[0] if sql_query.sql else "")
|
|
1178
|
+
m = _re.search(r"\bLIMIT\s+(\d+)", sql_str, _re.IGNORECASE)
|
|
1179
|
+
if m:
|
|
1180
|
+
max_results = int(m.group(1))
|
|
1181
|
+
|
|
1182
|
+
bfs_results = None
|
|
1183
|
+
if self._detect_arno() and self._arno_capabilities.get("bfs"):
|
|
1184
|
+
try:
|
|
1185
|
+
bfs_json = self._arno_call(
|
|
1186
|
+
"Graph.KG.NKGAccel",
|
|
1187
|
+
"BFSJson",
|
|
1188
|
+
source_id,
|
|
1189
|
+
predicates_json,
|
|
1190
|
+
max_hops,
|
|
1191
|
+
max_results,
|
|
1192
|
+
)
|
|
1193
|
+
bfs_results = _json.loads(str(bfs_json)) if bfs_json else []
|
|
1194
|
+
logger.debug("Arno BFSJson: %d results for %s", len(bfs_results), source_id)
|
|
1195
|
+
except Exception as e:
|
|
1196
|
+
logger.warning(f"Arno BFSJson failed, falling back to BFSFastJson: {e}")
|
|
1197
|
+
bfs_results = None
|
|
1198
|
+
|
|
1199
|
+
if bfs_results is None:
|
|
1200
|
+
try:
|
|
1201
|
+
bfs_json = _call_classmethod(
|
|
1202
|
+
self.conn,
|
|
1203
|
+
"Graph.KG.Traversal",
|
|
1204
|
+
"BFSFastJson",
|
|
1205
|
+
source_id,
|
|
1206
|
+
predicates_json,
|
|
1207
|
+
max_hops,
|
|
1208
|
+
"",
|
|
1209
|
+
)
|
|
1210
|
+
bfs_results = _json.loads(str(bfs_json)) if bfs_json else []
|
|
1211
|
+
except Exception as e:
|
|
1212
|
+
logger.warning(f"BFSFastJson failed: {e}")
|
|
1213
|
+
return {
|
|
1214
|
+
"columns": [],
|
|
1215
|
+
"rows": [],
|
|
1216
|
+
"sql": "",
|
|
1217
|
+
"params": [],
|
|
1218
|
+
"metadata": sql_query.query_metadata,
|
|
1219
|
+
}
|
|
1165
1220
|
|
|
1166
1221
|
if min_hops > 1:
|
|
1167
1222
|
min_step_per_node: dict = {}
|
|
@@ -1188,9 +1243,14 @@ class IRISGraphEngine:
|
|
|
1188
1243
|
seen.add(oid)
|
|
1189
1244
|
target_ids.append(oid)
|
|
1190
1245
|
|
|
1246
|
+
import re as _re
|
|
1247
|
+
sql_str = sql_query.sql if isinstance(sql_query.sql, str) else ""
|
|
1248
|
+
alias_match = _re.search(r'SELECT\s+DISTINCT\s+\S+\s+AS\s+(\w+)|SELECT\s+\S+\s+AS\s+(\w+)', sql_str, _re.IGNORECASE)
|
|
1249
|
+
col_name = (alias_match.group(1) or alias_match.group(2)) if alias_match else "b_id"
|
|
1250
|
+
|
|
1191
1251
|
if not target_ids:
|
|
1192
1252
|
return {
|
|
1193
|
-
"columns": [
|
|
1253
|
+
"columns": [col_name, "b_labels", "b_props"],
|
|
1194
1254
|
"rows": [],
|
|
1195
1255
|
"sql": "",
|
|
1196
1256
|
"params": [],
|
|
@@ -1210,7 +1270,7 @@ class IRISGraphEngine:
|
|
|
1210
1270
|
)
|
|
1211
1271
|
|
|
1212
1272
|
return {
|
|
1213
|
-
"columns": [
|
|
1273
|
+
"columns": [col_name, "b_labels", "b_props"],
|
|
1214
1274
|
"rows": [list(r) for r in rows],
|
|
1215
1275
|
"sql": f"BFSFastJson({source_id}, {predicates_json}, {max_hops})",
|
|
1216
1276
|
"params": [],
|
|
@@ -4890,9 +4950,9 @@ class IRISGraphEngine:
|
|
|
4890
4950
|
|
|
4891
4951
|
return iris.createIRIS(self.conn)
|
|
4892
4952
|
except (TypeError, AttributeError):
|
|
4893
|
-
import
|
|
4953
|
+
import iris
|
|
4894
4954
|
|
|
4895
|
-
return
|
|
4955
|
+
return iris.createIRIS(self.conn)
|
|
4896
4956
|
|
|
4897
4957
|
def vec_create_index(
|
|
4898
4958
|
self,
|
|
@@ -17,43 +17,18 @@ logger = logging.getLogger(__name__)
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _call_classmethod(conn_or_cursor, class_name: str, method_name: str, *args) -> Any:
|
|
20
|
-
"""Call an IRIS ObjectScript class method using the native API.
|
|
21
|
-
|
|
22
|
-
Works with both the ``iris`` package (iris.createIRIS) and the
|
|
23
|
-
``intersystems_iris`` package (intersystems_iris.createIRIS). Tries
|
|
24
|
-
``iris.createIRIS`` first because it accepts the connection objects
|
|
25
|
-
returned by ``iris.connect()`` (the standard test-fixture connection).
|
|
26
|
-
|
|
27
|
-
Resolves the connection from either a connection object directly or from
|
|
28
|
-
``cursor._connection`` if a cursor is passed.
|
|
29
|
-
|
|
30
|
-
Returns the method's return value, or raises if the class/method does not
|
|
31
|
-
exist or the native API is unavailable.
|
|
32
|
-
"""
|
|
33
|
-
# Accept either a connection or a cursor
|
|
34
20
|
if hasattr(conn_or_cursor, "cursor"):
|
|
35
|
-
conn = conn_or_cursor
|
|
21
|
+
conn = conn_or_cursor
|
|
36
22
|
elif hasattr(conn_or_cursor, "_connection"):
|
|
37
|
-
conn = conn_or_cursor._connection
|
|
23
|
+
conn = conn_or_cursor._connection
|
|
38
24
|
else:
|
|
39
|
-
conn = conn_or_cursor
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try:
|
|
43
|
-
import iris as _iris_pkg # type: ignore[import]
|
|
44
|
-
|
|
45
|
-
iris_obj = _iris_pkg.createIRIS(conn)
|
|
46
|
-
return iris_obj.classMethodValue(class_name, method_name, *args)
|
|
47
|
-
except (ImportError, AttributeError, TypeError):
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
# Fall back to intersystems_iris.createIRIS (accepts intersystems_iris.IRISConnection)
|
|
51
|
-
import intersystems_iris as _iris_pkg2 # type: ignore[import]
|
|
52
|
-
|
|
53
|
-
iris_obj = _iris_pkg2.createIRIS(conn)
|
|
25
|
+
conn = conn_or_cursor
|
|
26
|
+
import iris as _iris_pkg
|
|
27
|
+
iris_obj = _iris_pkg.createIRIS(conn)
|
|
54
28
|
return iris_obj.classMethodValue(class_name, method_name, *args)
|
|
55
29
|
|
|
56
30
|
|
|
31
|
+
|
|
57
32
|
class GraphSchema:
|
|
58
33
|
"""Domain-agnostic RDF-style graph schema management"""
|
|
59
34
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iris-vector-graph"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.63.1"
|
|
8
8
|
description = "Transactional Graph + Vector retrieval system for InterSystems IRIS with hybrid search, openCypher, and GraphQL APIs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -38,12 +38,12 @@ requires-python = ">=3.10"
|
|
|
38
38
|
|
|
39
39
|
# Core dependencies
|
|
40
40
|
dependencies = [
|
|
41
|
-
"intersystems-
|
|
41
|
+
"intersystems-iris>=1.0.0",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
[project.optional-dependencies]
|
|
45
45
|
core = [
|
|
46
|
-
"intersystems-
|
|
46
|
+
"intersystems-iris>=1.0.0",
|
|
47
47
|
]
|
|
48
48
|
full = [
|
|
49
49
|
"numpy>=1.24.0",
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
SKIP_IRIS_TESTS = os.environ.get("SKIP_IRIS_TESTS", "false").lower() == "true"
|
|
9
|
+
SKIP_ARNO_TESTS = os.environ.get("SKIP_ARNO_TESTS", "true").lower() == "true"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestBFSArnoUnit:
|
|
13
|
+
|
|
14
|
+
def _make_engine(self):
|
|
15
|
+
from iris_vector_graph.engine import IRISGraphEngine
|
|
16
|
+
conn = MagicMock()
|
|
17
|
+
cursor = MagicMock()
|
|
18
|
+
conn.cursor.return_value = cursor
|
|
19
|
+
cursor.fetchall.return_value = []
|
|
20
|
+
engine = IRISGraphEngine(conn, embedding_dimension=4)
|
|
21
|
+
engine._arno_available = True
|
|
22
|
+
engine._arno_capabilities = {"nkg_data": True, "bfs": True, "algorithms": ["bfs"]}
|
|
23
|
+
return engine
|
|
24
|
+
|
|
25
|
+
def test_arno_bfs_path_called_when_bfs_capability_present(self):
|
|
26
|
+
engine = self._make_engine()
|
|
27
|
+
expected = [{"s": "A", "p": "BINDS", "o": "B", "w": 1.0, "step": 1}]
|
|
28
|
+
|
|
29
|
+
with patch.object(engine, "_arno_call", return_value=json.dumps(expected)) as mock_call:
|
|
30
|
+
with patch.object(engine, "_detect_arno", return_value=True):
|
|
31
|
+
from iris_vector_graph.cypher.translator import SQLQuery
|
|
32
|
+
vl = {
|
|
33
|
+
"types": ["BINDS"],
|
|
34
|
+
"min_hops": 1,
|
|
35
|
+
"max_hops": 3,
|
|
36
|
+
"properties": {},
|
|
37
|
+
"return_path_funcs": [],
|
|
38
|
+
"src_id_param": "A",
|
|
39
|
+
"dst_id_param": None,
|
|
40
|
+
"source_var": "a",
|
|
41
|
+
"source_alias": "n0",
|
|
42
|
+
"target_var": "b",
|
|
43
|
+
"target_alias": "n1",
|
|
44
|
+
"direction": "out",
|
|
45
|
+
"shortest": False,
|
|
46
|
+
"all_shortest": False,
|
|
47
|
+
}
|
|
48
|
+
sq = SQLQuery(
|
|
49
|
+
sql="",
|
|
50
|
+
parameters=[["A"]],
|
|
51
|
+
var_length_paths=[vl],
|
|
52
|
+
)
|
|
53
|
+
with patch.object(engine, "get_nodes", return_value=[{"id": "B", "labels": ["Gene"]}]):
|
|
54
|
+
result = engine._execute_var_length_cypher(sq)
|
|
55
|
+
|
|
56
|
+
mock_call.assert_called_once_with(
|
|
57
|
+
"Graph.KG.NKGAccel", "BFSJson", "A", '["BINDS"]', 3, 0
|
|
58
|
+
)
|
|
59
|
+
assert result["rows"] is not None
|
|
60
|
+
|
|
61
|
+
def test_fallback_to_bfsfast_when_no_bfs_capability(self):
|
|
62
|
+
engine = self._make_engine()
|
|
63
|
+
engine._arno_capabilities = {"nkg_data": True, "algorithms": []}
|
|
64
|
+
|
|
65
|
+
fallback_result = [{"s": "A", "p": "BINDS", "o": "B", "w": 1.0, "step": 1}]
|
|
66
|
+
|
|
67
|
+
with patch("iris_vector_graph.engine._call_classmethod", return_value=json.dumps(fallback_result)):
|
|
68
|
+
with patch.object(engine, "_detect_arno", return_value=False):
|
|
69
|
+
from iris_vector_graph.cypher.translator import SQLQuery
|
|
70
|
+
vl = {
|
|
71
|
+
"types": [], "min_hops": 1, "max_hops": 2, "properties": {},
|
|
72
|
+
"return_path_funcs": [], "src_id_param": "A", "dst_id_param": None,
|
|
73
|
+
"source_var": "a", "source_alias": "n0", "target_var": "b",
|
|
74
|
+
"target_alias": "n1", "direction": "out", "shortest": False, "all_shortest": False,
|
|
75
|
+
}
|
|
76
|
+
sq = SQLQuery(sql="", parameters=[["A"]], var_length_paths=[vl])
|
|
77
|
+
with patch.object(engine, "get_nodes", return_value=[{"id": "B", "labels": []}]):
|
|
78
|
+
result = engine._execute_var_length_cypher(sq)
|
|
79
|
+
|
|
80
|
+
assert result is not None
|
|
81
|
+
|
|
82
|
+
def test_arno_bfs_error_falls_back_to_bfsfast(self):
|
|
83
|
+
engine = self._make_engine()
|
|
84
|
+
|
|
85
|
+
fallback_result = [{"s": "A", "p": "BINDS", "o": "B", "w": 1.0, "step": 1}]
|
|
86
|
+
|
|
87
|
+
with patch.object(engine, "_arno_call", side_effect=RuntimeError("Arno not loaded")):
|
|
88
|
+
with patch.object(engine, "_detect_arno", return_value=True):
|
|
89
|
+
with patch("iris_vector_graph.engine._call_classmethod", return_value=json.dumps(fallback_result)):
|
|
90
|
+
from iris_vector_graph.cypher.translator import SQLQuery
|
|
91
|
+
vl = {
|
|
92
|
+
"types": [], "min_hops": 1, "max_hops": 2, "properties": {},
|
|
93
|
+
"return_path_funcs": [], "src_id_param": "A", "dst_id_param": None,
|
|
94
|
+
"source_var": "a", "source_alias": "n0", "target_var": "b",
|
|
95
|
+
"target_alias": "n1", "direction": "out", "shortest": False, "all_shortest": False,
|
|
96
|
+
}
|
|
97
|
+
sq = SQLQuery(sql="", parameters=[["A"]], var_length_paths=[vl])
|
|
98
|
+
with patch.object(engine, "get_nodes", return_value=[{"id": "B", "labels": []}]):
|
|
99
|
+
result = engine._execute_var_length_cypher(sq)
|
|
100
|
+
|
|
101
|
+
assert result is not None
|
|
102
|
+
|
|
103
|
+
def test_max_results_derived_from_sql_limit(self):
|
|
104
|
+
engine = self._make_engine()
|
|
105
|
+
captured = {}
|
|
106
|
+
|
|
107
|
+
def capture_call(cls, method, *args):
|
|
108
|
+
captured["args"] = args
|
|
109
|
+
return "[]"
|
|
110
|
+
|
|
111
|
+
with patch.object(engine, "_arno_call", side_effect=capture_call):
|
|
112
|
+
with patch.object(engine, "_detect_arno", return_value=True):
|
|
113
|
+
from iris_vector_graph.cypher.translator import SQLQuery
|
|
114
|
+
vl = {
|
|
115
|
+
"types": [], "min_hops": 1, "max_hops": 2, "properties": {},
|
|
116
|
+
"return_path_funcs": [], "src_id_param": "A", "dst_id_param": None,
|
|
117
|
+
"source_var": "a", "source_alias": "n0", "target_var": "b",
|
|
118
|
+
"target_alias": "n1", "direction": "out", "shortest": False, "all_shortest": False,
|
|
119
|
+
}
|
|
120
|
+
sq = SQLQuery(
|
|
121
|
+
sql="SELECT n FROM Stage1 LIMIT 100",
|
|
122
|
+
parameters=[["A"]],
|
|
123
|
+
var_length_paths=[vl],
|
|
124
|
+
)
|
|
125
|
+
engine._execute_var_length_cypher(sq)
|
|
126
|
+
|
|
127
|
+
assert captured.get("args") is not None
|
|
128
|
+
assert captured["args"][-1] == 100
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.mark.skipif(SKIP_IRIS_TESTS, reason="SKIP_IRIS_TESTS=true")
|
|
132
|
+
@pytest.mark.skipif(SKIP_ARNO_TESTS, reason="SKIP_ARNO_TESTS=true — Arno .so not loaded")
|
|
133
|
+
class TestBFSArnoE2E:
|
|
134
|
+
|
|
135
|
+
@pytest.fixture(autouse=True)
|
|
136
|
+
def setup(self, iris_connection):
|
|
137
|
+
from iris_vector_graph.engine import IRISGraphEngine
|
|
138
|
+
self.conn = iris_connection
|
|
139
|
+
self.engine = IRISGraphEngine(iris_connection, embedding_dimension=4)
|
|
140
|
+
self.engine.initialize_schema()
|
|
141
|
+
self._run = uuid.uuid4().hex[:8]
|
|
142
|
+
yield
|
|
143
|
+
cursor = self.conn.cursor()
|
|
144
|
+
try:
|
|
145
|
+
cursor.execute(
|
|
146
|
+
f"DELETE FROM Graph_KG.rdf_edges WHERE s LIKE 'bfs079_{self._run}%' OR o_id LIKE 'bfs079_{self._run}%'"
|
|
147
|
+
)
|
|
148
|
+
cursor.execute(f"DELETE FROM Graph_KG.rdf_labels WHERE s LIKE 'bfs079_{self._run}%'")
|
|
149
|
+
cursor.execute(f"DELETE FROM Graph_KG.nodes WHERE node_id LIKE 'bfs079_{self._run}%'")
|
|
150
|
+
self.conn.commit()
|
|
151
|
+
except Exception:
|
|
152
|
+
self.conn.rollback()
|
|
153
|
+
|
|
154
|
+
def _node(self, suffix, label="Gene"):
|
|
155
|
+
nid = f"bfs079_{self._run}_{suffix}"
|
|
156
|
+
self.engine.create_node(nid, labels=[label])
|
|
157
|
+
return nid
|
|
158
|
+
|
|
159
|
+
def _edge(self, s, p, o):
|
|
160
|
+
self.engine.create_edge(s, p, o)
|
|
161
|
+
|
|
162
|
+
def test_bfs_arno_correctness(self):
|
|
163
|
+
a = self._node("A")
|
|
164
|
+
b = self._node("B")
|
|
165
|
+
c = self._node("C")
|
|
166
|
+
d = self._node("D")
|
|
167
|
+
self._edge(a, "BINDS", b)
|
|
168
|
+
self._edge(b, "BINDS", c)
|
|
169
|
+
self._edge(c, "BINDS", d)
|
|
170
|
+
|
|
171
|
+
result_arno = self.engine.execute_cypher(
|
|
172
|
+
"MATCH (x)-[r*1..3]->(y) WHERE x.id = $id RETURN y.id",
|
|
173
|
+
{"id": a},
|
|
174
|
+
)
|
|
175
|
+
arno_ids = {row[0] for row in result_arno["rows"]}
|
|
176
|
+
|
|
177
|
+
self.engine._arno_available = False
|
|
178
|
+
self.engine._arno_capabilities = {}
|
|
179
|
+
|
|
180
|
+
result_fallback = self.engine.execute_cypher(
|
|
181
|
+
"MATCH (x)-[r*1..3]->(y) WHERE x.id = $id RETURN y.id",
|
|
182
|
+
{"id": a},
|
|
183
|
+
)
|
|
184
|
+
fallback_ids = {row[0] for row in result_fallback["rows"]}
|
|
185
|
+
|
|
186
|
+
assert arno_ids == fallback_ids, f"Arno result {arno_ids} != fallback {fallback_ids}"
|
|
187
|
+
|
|
188
|
+
def test_bfs_arno_perf(self):
|
|
189
|
+
import time, statistics
|
|
190
|
+
hub = self._node("hub")
|
|
191
|
+
for i in range(20):
|
|
192
|
+
mid = self._node(f"mid{i}")
|
|
193
|
+
leaf = self._node(f"leaf{i}")
|
|
194
|
+
self._edge(hub, "BINDS", mid)
|
|
195
|
+
self._edge(mid, "BINDS", leaf)
|
|
196
|
+
|
|
197
|
+
times = []
|
|
198
|
+
for _ in range(10):
|
|
199
|
+
t0 = time.perf_counter()
|
|
200
|
+
self.engine.execute_cypher(
|
|
201
|
+
"MATCH (x)-[r*1..3]->(y) WHERE x.id = $id RETURN y.id",
|
|
202
|
+
{"id": hub},
|
|
203
|
+
)
|
|
204
|
+
times.append((time.perf_counter() - t0) * 1000)
|
|
205
|
+
|
|
206
|
+
med = statistics.median(times)
|
|
207
|
+
assert med < 30, f"Arno BFS p50={med:.1f}ms exceeds 30ms target"
|
|
208
|
+
|
|
209
|
+
def test_bfs_arno_predicate_filter(self):
|
|
210
|
+
a = self._node("pred_a")
|
|
211
|
+
b_binds = self._node("pred_b_binds")
|
|
212
|
+
b_regs = self._node("pred_b_regs")
|
|
213
|
+
self._edge(a, "BINDS", b_binds)
|
|
214
|
+
self._edge(a, "REGULATES", b_regs)
|
|
215
|
+
|
|
216
|
+
result = self.engine.execute_cypher(
|
|
217
|
+
"MATCH (x)-[r:BINDS*1..2]->(y) WHERE x.id = $id RETURN y.id",
|
|
218
|
+
{"id": a},
|
|
219
|
+
)
|
|
220
|
+
ids = {row[0] for row in result["rows"]}
|
|
221
|
+
assert b_binds in ids
|
|
222
|
+
assert b_regs not in ids
|
|
223
|
+
|
|
224
|
+
def test_bfs_arno_fallback(self):
|
|
225
|
+
a = self._node("fb_a")
|
|
226
|
+
b = self._node("fb_b")
|
|
227
|
+
self._edge(a, "BINDS", b)
|
|
228
|
+
|
|
229
|
+
self.engine._arno_available = False
|
|
230
|
+
self.engine._arno_capabilities = {}
|
|
231
|
+
|
|
232
|
+
result = self.engine.execute_cypher(
|
|
233
|
+
"MATCH (x)-[r*1..2]->(y) WHERE x.id = $id RETURN y.id",
|
|
234
|
+
{"id": a},
|
|
235
|
+
)
|
|
236
|
+
ids = {row[0] for row in result["rows"]}
|
|
237
|
+
assert b in ids
|
|
238
|
+
|
|
239
|
+
def test_bfs_arno_max_results(self):
|
|
240
|
+
hub = self._node("max_hub")
|
|
241
|
+
for i in range(20):
|
|
242
|
+
n = self._node(f"max_leaf{i}")
|
|
243
|
+
self._edge(hub, "BINDS", n)
|
|
244
|
+
|
|
245
|
+
result = self.engine.execute_cypher(
|
|
246
|
+
"MATCH (x)-[r*1..1]->(y) WHERE x.id = $id RETURN y.id LIMIT 5",
|
|
247
|
+
{"id": hub},
|
|
248
|
+
)
|
|
249
|
+
assert len(result["rows"]) <= 5
|