iris-vector-graph 1.59.0__tar.gz → 1.59.2__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.
Files changed (239) hide show
  1. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/PKG-INFO +9 -1
  2. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/README.md +8 -0
  3. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/translator.py +15 -0
  4. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/engine.py +141 -29
  5. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/pyproject.toml +1 -1
  6. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_translator.py +30 -0
  7. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/.gitignore +0 -0
  8. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/LICENSE +0 -0
  9. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/demo_biomedical.py +0 -0
  10. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/demo_fraud_detection.py +0 -0
  11. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/demo_fraud_detection_sql.py +0 -0
  12. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/demo_utils.py +0 -0
  13. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/demo_working_system.py +0 -0
  14. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/__init__.py +0 -0
  15. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical/__init__.py +0 -0
  16. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical/loaders.py +0 -0
  17. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical/resolver.py +0 -0
  18. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical/types.py +0 -0
  19. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical_legacy/__init__.py +0 -0
  20. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical_legacy/biomedical_engine.py +0 -0
  21. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical_legacy/biomedical_schema.py +0 -0
  22. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/biomedical_legacy/legacy_wrapper.py +0 -0
  23. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/fraud/__init__.py +0 -0
  24. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/fraud/loaders.py +0 -0
  25. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/fraud/resolver.py +0 -0
  26. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/domains/fraud/types.py +0 -0
  27. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/graphQL.http +0 -0
  28. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/hybrid_vector_graph_query.cypher +0 -0
  29. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/examples/rest.http +0 -0
  30. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Algorithms.cls +0 -0
  31. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/ArnoAccel.cls +0 -0
  32. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/BM25Index.cls +0 -0
  33. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/BenchFormat.cls +0 -0
  34. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/BenchSeeder.cls +0 -0
  35. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Benchmark.cls +0 -0
  36. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Edge.cls +0 -0
  37. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/EdgeScan.cls +0 -0
  38. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/GraphIndex.cls +0 -0
  39. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/IVFIndex.cls +0 -0
  40. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Loader.cls +0 -0
  41. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/MCPService.cls +0 -0
  42. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/MCPToolSet.cls +0 -0
  43. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/MCPTools.cls +0 -0
  44. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Meta.cls +0 -0
  45. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/PLAIDSearch.cls +0 -0
  46. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/PageRank.cls +0 -0
  47. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/PyOps.cls +0 -0
  48. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Service.cls +0 -0
  49. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Snapshot.cls +0 -0
  50. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Subgraph.cls +0 -0
  51. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/TemporalIndex.cls +0 -0
  52. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/TestEdge.cls +0 -0
  53. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/Traversal.cls +0 -0
  54. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/Graph/KG/VecIndex.cls +0 -0
  55. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/PageRankEmbedded.cls +0 -0
  56. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/User.Exec.cls +0 -0
  57. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_src/src/iris/vector/graph/GraphOperators.cls +0 -0
  58. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/__init__.py +0 -0
  59. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/bolt_server.py +0 -0
  60. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/bulk_loader.py +0 -0
  61. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/capabilities.py +0 -0
  62. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/__init__.py +0 -0
  63. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/algorithms/__init__.py +0 -0
  64. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/algorithms/paths.py +0 -0
  65. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/ast.py +0 -0
  66. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/lexer.py +0 -0
  67. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher/parser.py +0 -0
  68. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/cypher_api.py +0 -0
  69. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/embedded.py +0 -0
  70. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/fusion.py +0 -0
  71. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/__init__.py +0 -0
  72. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/constants.py +0 -0
  73. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/engine.py +0 -0
  74. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/pooling.py +0 -0
  75. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/resolvers.py +0 -0
  76. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/gql/schema.py +0 -0
  77. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/models.py +0 -0
  78. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/operators.py +0 -0
  79. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/py.typed +0 -0
  80. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/schema.py +0 -0
  81. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/security.py +0 -0
  82. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/text_search.py +0 -0
  83. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/utils.py +0 -0
  84. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/iris_vector_graph/vector_utils.py +0 -0
  85. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/fhir_bridges.sql +0 -0
  86. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/fraud_sample_data.sql +0 -0
  87. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/globals_schema.sql +0 -0
  88. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/graph_path_globals.sql +0 -0
  89. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/graph_walk_tvf.sql +0 -0
  90. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/migrations/000_base_schema_iris.sql +0 -0
  91. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/migrations/001_add_nodepk_table.sql +0 -0
  92. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/migrations/001_rollback_nodepk.sql +0 -0
  93. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/migrations/002_add_fk_constraints.sql +0 -0
  94. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/operators.sql +0 -0
  95. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/operators_fixed.sql +0 -0
  96. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/procedures/kg_PageRank.sql +0 -0
  97. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/rdf_reifications.sql +0 -0
  98. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/sql/schema.sql +0 -0
  99. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/TESTING.md +0 -0
  100. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmark_parser.py +0 -0
  101. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/benchmark_neo4j.py +0 -0
  102. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/bfs_benchmark.py +0 -0
  103. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/establish_baseline.py +0 -0
  104. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/graph_gen.py +0 -0
  105. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/iris_baseline_run.py +0 -0
  106. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/iris_os_run.py +0 -0
  107. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/load_neo4j.py +0 -0
  108. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/benchmarks/synthetic_baseline.csv +0 -0
  109. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/conftest.py +0 -0
  110. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/__init__.py +0 -0
  111. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/test_cypher_api.py +0 -0
  112. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/test_cypher_api_errors.py +0 -0
  113. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/test_graphql_queries.py +0 -0
  114. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/test_graphql_schema.py +0 -0
  115. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/contract/test_ppr_api.py +0 -0
  116. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/curl_suite.sh +0 -0
  117. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/__init__.py +0 -0
  118. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/conftest.py +0 -0
  119. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_biomedical_demo.py +0 -0
  120. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_biomedical_ui.py +0 -0
  121. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_cypher_coerce_e2e.py +0 -0
  122. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_cypher_sprints_e2e.py +0 -0
  123. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_cypher_vector_search.py +0 -0
  124. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_fhir_bridges_e2e.py +0 -0
  125. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_fraud_demo.py +0 -0
  126. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_fraud_ui.py +0 -0
  127. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_gql_autogen_startup.py +0 -0
  128. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_gql_cypher_passthrough.py +0 -0
  129. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_gql_node_queries.py +0 -0
  130. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_gql_semantic_search.py +0 -0
  131. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_gql_traversal.py +0 -0
  132. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_graph_kernels_e2e.py +0 -0
  133. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_hla_kg_e2e.py +0 -0
  134. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_multi_query_engine_platform.py +0 -0
  135. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_named_paths_e2e.py +0 -0
  136. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_nkg_index_e2e.py +0 -0
  137. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_operator_wiring_e2e.py +0 -0
  138. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_plaid_search_e2e.py +0 -0
  139. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_ppr_cls_fast_path.py +0 -0
  140. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_ppr_guided_e2e.py +0 -0
  141. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_procedure_installation.py +0 -0
  142. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_reification_e2e.py +0 -0
  143. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_schema_procedures_e2e.py +0 -0
  144. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_subgraph_e2e.py +0 -0
  145. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_subquery_call_e2e.py +0 -0
  146. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/e2e/test_vecindex_e2e.py +0 -0
  147. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/__init__.py +0 -0
  148. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/conftest.py +0 -0
  149. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/gql/__init__.py +0 -0
  150. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/gql/test_graphql_mutations.py +0 -0
  151. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/gql/test_graphql_nested_queries.py +0 -0
  152. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/gql/test_graphql_queries.py +0 -0
  153. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/gql/test_graphql_vector_search.py +0 -0
  154. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_bidirectional_ppr.py +0 -0
  155. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cls_layer.py +0 -0
  156. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_advanced.py +0 -0
  157. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_enhancements.py +0 -0
  158. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_multi_type.py +0 -0
  159. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_rd.py +0 -0
  160. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_rel_vars.py +0 -0
  161. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_single_type.py +0 -0
  162. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_untyped.py +0 -0
  163. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_cypher_vector_search.py +0 -0
  164. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_embeddings_api.py +0 -0
  165. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_fastapi_graphql.py +0 -0
  166. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_fhir_bridges_integration.py +0 -0
  167. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_named_paths_integration.py +0 -0
  168. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_advanced_benchmarks.py +0 -0
  169. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_constraints.py +0 -0
  170. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_graph_analytics.py +0 -0
  171. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_migration.py +0 -0
  172. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_performance.py +0 -0
  173. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_nodepk_production_scale.py +0 -0
  174. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_objectscript_classes.py +0 -0
  175. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_pagerank_sql_optimization.py +0 -0
  176. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_reification_integration.py +0 -0
  177. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_schema_migration.py +0 -0
  178. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_stored_procedure_install.py +0 -0
  179. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/integration/test_subquery_call_integration.py +0 -0
  180. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/performance/conftest.py +0 -0
  181. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/performance/scale_benchmark.py +0 -0
  182. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/performance/test_ppr_stress.py +0 -0
  183. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/performance/test_stress_v1_5.py +0 -0
  184. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/run_all_tests.py +0 -0
  185. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_iris_rest_api.py +0 -0
  186. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_networkx_loader.py +0 -0
  187. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_performance_benchmarks.py +0 -0
  188. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_pyops_vector_conversion.py +0 -0
  189. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_python_operators.py +0 -0
  190. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_python_sdk.py +0 -0
  191. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_schema_validation.py +0 -0
  192. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_sql_queries.py +0 -0
  193. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/python/test_vector_functions.py +0 -0
  194. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/cypher/__init__.py +0 -0
  195. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/cypher/test_lexer.py +0 -0
  196. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/cypher/test_lexer_advanced.py +0 -0
  197. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/cypher/test_parser.py +0 -0
  198. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/cypher/test_parser_advanced.py +0 -0
  199. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_batch_mutations.py +0 -0
  200. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_bm25_index.py +0 -0
  201. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_bolt_server.py +0 -0
  202. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cls_deployment.py +0 -0
  203. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_case_when.py +0 -0
  204. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_functions.py +0 -0
  205. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_parser.py +0 -0
  206. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_posos_bugs.py +0 -0
  207. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_procedures.py +0 -0
  208. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_union_exists.py +0 -0
  209. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_var_length.py +0 -0
  210. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_cypher_vector_search.py +0 -0
  211. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_edge_embeddings.py +0 -0
  212. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_edgeprop_ndjson.py +0 -0
  213. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_embedded.py +0 -0
  214. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_engine_dimension_fix.py +0 -0
  215. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_engine_embeddings.py +0 -0
  216. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_fhir_bridges.py +0 -0
  217. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_get_nodes.py +0 -0
  218. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_graph_kernels.py +0 -0
  219. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_graphql_dataloader.py +0 -0
  220. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_ingest_formats.py +0 -0
  221. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_ivf_index.py +0 -0
  222. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_named_graphs.py +0 -0
  223. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_named_paths.py +0 -0
  224. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_operators_wiring.py +0 -0
  225. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_plaid_search.py +0 -0
  226. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_ppr_guided_subgraph.py +0 -0
  227. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_reification.py +0 -0
  228. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_schema_init.py +0 -0
  229. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_schema_procedures.py +0 -0
  230. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_shortest_path.py +0 -0
  231. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_snapshot.py +0 -0
  232. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_sql_splitter.py +0 -0
  233. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_sql_table_bridge.py +0 -0
  234. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_subgraph.py +0 -0
  235. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_subquery_call.py +0 -0
  236. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_temporal_cypher.py +0 -0
  237. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_temporal_edges.py +0 -0
  238. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/tests/unit/test_unified_edge_store.py +0 -0
  239. {iris_vector_graph-1.59.0 → iris_vector_graph-1.59.2}/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.59.0
3
+ Version: 1.59.2
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
@@ -655,6 +655,14 @@ anchors = engine.get_kg_anchors(icd_codes=["J18.0", "E11.9"])
655
655
 
656
656
  ## Changelog
657
657
 
658
+ ### v1.59.2 (2026-04-24)
659
+ - fix: Cypher `WHERE x IN $param` and `WHERE x IN [list]` now correctly emit `IN (?,?,?)` — previously emitted `IN ?` which IRIS DBAPI can't expand. Enables batch multi-node queries like `MATCH (a)-[r]-(b) WHERE a.id IN $node_ids RETURN ...` (20× speedup for 2-hop expansion vs N sequential queries).
660
+
661
+
662
+ ### v1.59.1 (2026-04-21)
663
+ - perf: `embed_nodes()` and `embed_edges()` — 4–10x speedup for SentenceTransformer embedders: batch `model.encode(texts_list)` replaces N serial calls; `executemany()` replaces N per-row INSERTs; batch `DELETE WHERE id IN (...)` replaces N individual DELETEs. Estimated 94min → 10–25min for 205K nodes. Falls back gracefully for non-SentenceTransformer embedders and IRIS EMBEDDING() path.
664
+
665
+
658
666
  ### v1.59.0 (2026-04-21)
659
667
  - feat: `embed_edges(model, text_fn, where, batch_size, force, progress_callback)` — embed every `(s, p, o_id)` triple into `kg_EdgeEmbeddings(VECTOR(DOUBLE))` (spec 065)
660
668
  - feat: `edge_vector_search(query_embedding, top_k, score_threshold)` — cosine similarity search over edge embeddings
@@ -583,6 +583,14 @@ anchors = engine.get_kg_anchors(icd_codes=["J18.0", "E11.9"])
583
583
 
584
584
  ## Changelog
585
585
 
586
+ ### v1.59.2 (2026-04-24)
587
+ - fix: Cypher `WHERE x IN $param` and `WHERE x IN [list]` now correctly emit `IN (?,?,?)` — previously emitted `IN ?` which IRIS DBAPI can't expand. Enables batch multi-node queries like `MATCH (a)-[r]-(b) WHERE a.id IN $node_ids RETURN ...` (20× speedup for 2-hop expansion vs N sequential queries).
588
+
589
+
590
+ ### v1.59.1 (2026-04-21)
591
+ - perf: `embed_nodes()` and `embed_edges()` — 4–10x speedup for SentenceTransformer embedders: batch `model.encode(texts_list)` replaces N serial calls; `executemany()` replaces N per-row INSERTs; batch `DELETE WHERE id IN (...)` replaces N individual DELETEs. Estimated 94min → 10–25min for 205K nodes. Falls back gracefully for non-SentenceTransformer embedders and IRIS EMBEDDING() path.
592
+
593
+
586
594
  ### v1.59.0 (2026-04-21)
587
595
  - feat: `embed_edges(model, text_fn, where, batch_size, force, progress_callback)` — embed every `(s, p, o_id)` triple into `kg_EdgeEmbeddings(VECTOR(DOUBLE))` (spec 065)
588
596
  - feat: `edge_vector_search(query_embedding, top_k, score_threshold)` — cosine similarity search over edge embeddings
@@ -2026,6 +2026,21 @@ def translate_boolean_expression(expr, context) -> str:
2026
2026
  return f"{left} IS NULL"
2027
2027
  if op == ast.BooleanOperator.IS_NOT_NULL:
2028
2028
  return f"{left} IS NOT NULL"
2029
+ if op == ast.BooleanOperator.IN:
2030
+ if isinstance(right_expr, ast.Literal) and isinstance(right_expr.value, list):
2031
+ items = right_expr.value
2032
+ placeholders = ", ".join(
2033
+ context.add_where_param(
2034
+ item.value if isinstance(item, ast.Literal) else item
2035
+ )
2036
+ for item in items
2037
+ )
2038
+ return f"{left} IN ({placeholders})"
2039
+ if isinstance(right_expr, ast.Variable) and right_expr.name in context.input_params:
2040
+ val = context.input_params[right_expr.name]
2041
+ if isinstance(val, list):
2042
+ placeholders = ", ".join(context.add_where_param(v) for v in val)
2043
+ return f"{left} IN ({placeholders})"
2029
2044
  right = translate_expression(right_expr, context, segment="where")
2030
2045
  if op in (
2031
2046
  ast.BooleanOperator.LESS_THAN,
@@ -3425,6 +3425,8 @@ class IRISGraphEngine:
3425
3425
  node_id, key, val = row[0], row[1], row[2]
3426
3426
  props_by_node.setdefault(node_id, {})[key] = val
3427
3427
 
3428
+ texts: List[str] = []
3429
+ valid_ids: List[str] = []
3428
3430
  for node_id in batch_ids:
3429
3431
  props = props_by_node.get(node_id, {})
3430
3432
  if text_fn is not None:
@@ -3443,24 +3445,77 @@ class IRISGraphEngine:
3443
3445
  skipped += 1
3444
3446
  continue
3445
3447
 
3446
- try:
3447
- emb = self.embed_text(text)
3448
- emb_str = ",".join(str(x) for x in emb)
3448
+ texts.append(text)
3449
+ valid_ids.append(node_id)
3450
+
3451
+ if not texts:
3452
+ self.conn.commit()
3453
+ n_done = batch_start + len(batch_ids)
3454
+ if progress_callback:
3455
+ progress_callback(n_done, n_to_embed)
3456
+ continue
3457
+
3458
+ try:
3459
+ use_batch = False
3460
+ if not self.embedding_config and self.embedder is not None:
3449
3461
  try:
3450
- cursor.execute(
3451
- f"DELETE FROM {_table('kg_NodeEmbeddings')} WHERE id = ?",
3452
- [node_id],
3453
- )
3454
- except Exception:
3462
+ from sentence_transformers import SentenceTransformer as _ST
3463
+ use_batch = isinstance(self.embedder, _ST)
3464
+ except ImportError:
3455
3465
  pass
3466
+ if use_batch:
3467
+ raw = self.embedder.encode(
3468
+ texts, batch_size=min(64, len(texts)), show_progress_bar=False
3469
+ )
3470
+ embeddings = [row.tolist() for row in raw]
3471
+ else:
3472
+ embeddings = [self.embed_text(t) for t in texts]
3473
+ except Exception as ex:
3474
+ logger.warning(f"embed_nodes: batch encode failed, falling back per-node: {ex}")
3475
+ embeddings = []
3476
+ for t in texts:
3477
+ try:
3478
+ embeddings.append(self.embed_text(t))
3479
+ except Exception as ex2:
3480
+ logger.warning(f"embed_nodes: embed_text failed: {ex2}")
3481
+ embeddings.append(None)
3482
+
3483
+ insert_params = []
3484
+ for node_id, emb in zip(valid_ids, embeddings):
3485
+ if emb is None:
3486
+ errors += 1
3487
+ continue
3488
+ emb_str = ",".join(str(x) for x in emb)
3489
+ insert_params.append((node_id, emb_str))
3490
+
3491
+ if insert_params:
3492
+ ids_to_delete = [p[0] for p in insert_params]
3493
+ del_placeholders = ", ".join("?" * len(ids_to_delete))
3494
+ try:
3456
3495
  cursor.execute(
3496
+ f"DELETE FROM {_table('kg_NodeEmbeddings')} WHERE id IN ({del_placeholders})",
3497
+ ids_to_delete,
3498
+ )
3499
+ except Exception:
3500
+ pass
3501
+ try:
3502
+ cursor.executemany(
3457
3503
  f"INSERT INTO {_table('kg_NodeEmbeddings')} (id, emb) VALUES (?, TO_VECTOR(?))",
3458
- [node_id, emb_str],
3504
+ insert_params,
3459
3505
  )
3460
- embedded += 1
3506
+ embedded += len(insert_params)
3461
3507
  except Exception as ex:
3462
- logger.warning(f"embed_nodes: failed to embed {node_id}: {ex}")
3463
- errors += 1
3508
+ logger.warning(f"embed_nodes: executemany failed, falling back per-row: {ex}")
3509
+ for node_id, emb_str in insert_params:
3510
+ try:
3511
+ cursor.execute(
3512
+ f"INSERT INTO {_table('kg_NodeEmbeddings')} (id, emb) VALUES (?, TO_VECTOR(?))",
3513
+ [node_id, emb_str],
3514
+ )
3515
+ embedded += 1
3516
+ except Exception as ex2:
3517
+ logger.warning(f"embed_nodes: insert failed for {node_id}: {ex2}")
3518
+ errors += 1
3464
3519
 
3465
3520
  self.conn.commit()
3466
3521
  n_done = batch_start + len(batch_ids)
@@ -3531,6 +3586,8 @@ class IRISGraphEngine:
3531
3586
  for batch_start in range(0, n_to_embed, batch_size):
3532
3587
  batch = to_embed[batch_start : batch_start + batch_size]
3533
3588
 
3589
+ texts: List[str] = []
3590
+ valid_edges: List[tuple] = []
3534
3591
  for s, p, o_id in batch:
3535
3592
  if text_fn is not None:
3536
3593
  try:
@@ -3549,29 +3606,84 @@ class IRISGraphEngine:
3549
3606
  skipped += 1
3550
3607
  continue
3551
3608
 
3552
- try:
3553
- emb = self.embed_text(text)
3554
- emb_str = ",".join(str(x) for x in emb)
3609
+ texts.append(text)
3610
+ valid_edges.append((s, p, o_id))
3611
+
3612
+ if not texts:
3613
+ self.conn.commit()
3614
+ n_done = batch_start + len(batch)
3615
+ if progress_callback:
3616
+ progress_callback(n_done, n_to_embed)
3617
+ continue
3618
+
3619
+ try:
3620
+ use_batch = False
3621
+ if not self.embedding_config and self.embedder is not None:
3555
3622
  try:
3556
- cursor.execute(
3557
- f"DELETE FROM {_table('kg_EdgeEmbeddings')} "
3558
- "WHERE s=? AND p=? AND o_id=?",
3559
- [s, p, o_id],
3560
- )
3561
- except Exception:
3623
+ from sentence_transformers import SentenceTransformer as _ST
3624
+ use_batch = isinstance(self.embedder, _ST)
3625
+ except ImportError:
3562
3626
  pass
3563
- cursor.execute(
3627
+ if use_batch:
3628
+ raw = self.embedder.encode(texts, batch_size=min(64, len(texts)), show_progress_bar=False)
3629
+ embeddings = [row.tolist() for row in raw]
3630
+ else:
3631
+ embeddings = [self.embed_text(t) for t in texts]
3632
+ except Exception as ex:
3633
+ logger.warning(f"embed_edges: batch encode failed, falling back per-edge: {ex}")
3634
+ embeddings = []
3635
+ for t in texts:
3636
+ try:
3637
+ embeddings.append(self.embed_text(t))
3638
+ except Exception as ex2:
3639
+ logger.warning(f"embed_edges: embed_text failed: {ex2}")
3640
+ embeddings.append(None)
3641
+
3642
+ insert_params = []
3643
+ for (s, p, o_id), emb in zip(valid_edges, embeddings):
3644
+ if emb is None:
3645
+ errors += 1
3646
+ continue
3647
+ emb_str = ",".join(str(x) for x in emb)
3648
+ insert_params.append((s, p, o_id, emb_str))
3649
+
3650
+ if insert_params:
3651
+ try:
3652
+ del_params = [(p[0], p[1], p[2]) for p in insert_params]
3653
+ for s, p, o_id in del_params:
3654
+ try:
3655
+ cursor.execute(
3656
+ f"DELETE FROM {_table('kg_EdgeEmbeddings')} "
3657
+ "WHERE s=? AND p=? AND o_id=?",
3658
+ [s, p, o_id],
3659
+ )
3660
+ except Exception:
3661
+ pass
3662
+ except Exception:
3663
+ pass
3664
+ try:
3665
+ cursor.executemany(
3564
3666
  f"INSERT INTO {_table('kg_EdgeEmbeddings')} "
3565
3667
  "(s, p, o_id, emb) VALUES (?, ?, ?, TO_VECTOR(?))",
3566
- [s, p, o_id, emb_str],
3668
+ insert_params,
3567
3669
  )
3568
- embedded += 1
3670
+ embedded += len(insert_params)
3569
3671
  except Exception as ex:
3570
- logger.warning(
3571
- "embed_edges: failed to embed (%s, %s, %s): %s",
3572
- s, p, o_id, ex,
3573
- )
3574
- errors += 1
3672
+ logger.warning(f"embed_edges: executemany failed, falling back per-row: {ex}")
3673
+ for s, p, o_id, emb_str in insert_params:
3674
+ try:
3675
+ cursor.execute(
3676
+ f"INSERT INTO {_table('kg_EdgeEmbeddings')} "
3677
+ "(s, p, o_id, emb) VALUES (?, ?, ?, TO_VECTOR(?))",
3678
+ [s, p, o_id, emb_str],
3679
+ )
3680
+ embedded += 1
3681
+ except Exception as ex2:
3682
+ logger.warning(
3683
+ "embed_edges: insert failed for (%s, %s, %s): %s",
3684
+ s, p, o_id, ex2,
3685
+ )
3686
+ errors += 1
3575
3687
 
3576
3688
  self.conn.commit()
3577
3689
  n_done = batch_start + len(batch)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iris-vector-graph"
7
- version = "1.59.0"
7
+ version = "1.59.2"
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"
@@ -151,3 +151,33 @@ def test_anonymous_both_nodes_pattern():
151
151
  """MATCH ()-[r]->(b) works without crashing."""
152
152
  sql = translate_to_sql(parse_query("MATCH ()-[r]->(b) RETURN b.id LIMIT 3"))
153
153
  assert "rdf_edges" in sql.sql or "MatchEdges" in sql.sql
154
+
155
+
156
+ def test_in_list_param_expands_placeholders():
157
+ """WHERE a.id IN $ids with a list param must produce IN (?,?,?) not IN ?."""
158
+ q = "MATCH (a)-[r]-(b) WHERE a.id IN $node_ids RETURN a.id AS src, type(r) AS rel, b.id AS dst LIMIT 100"
159
+ result = translate_to_sql(parse_query(q), {"node_ids": ["node1", "node2", "node3"]})
160
+ assert "IN (?, ?, ?)" in result.sql or "IN (?,?,?)" in result.sql.replace(" ", ""), \
161
+ f"Expected IN (?,?,?) expansion, got: {result.sql}"
162
+ params = result.parameters[0] if result.parameters else []
163
+ assert "node1" in params and "node2" in params and "node3" in params, \
164
+ f"Expected list values as individual params, got: {params}"
165
+ assert not any(isinstance(p, list) for p in params), \
166
+ f"No nested list should appear in params after expansion, got: {params}"
167
+
168
+
169
+ def test_in_literal_list_expands_placeholders():
170
+ """WHERE a.id IN ['x','y','z'] must produce IN (?,?,?) not IN [literal]."""
171
+ q = "MATCH (a) WHERE a.id IN ['python','rust','go'] RETURN a.id"
172
+ result = translate_to_sql(parse_query(q), {})
173
+ assert "IN (?, ?, ?)" in result.sql or "IN (?,?,?)" in result.sql.replace(" ", ""), \
174
+ f"Expected IN (?,?,?) expansion for literal list, got: {result.sql}"
175
+ params = result.parameters[0] if result.parameters else []
176
+ assert "python" in params and "rust" in params and "go" in params
177
+
178
+
179
+ def test_in_param_empty_list_handled():
180
+ """WHERE a.id IN $ids with empty list should not crash (returns no results)."""
181
+ q = "MATCH (a) WHERE a.id IN $ids RETURN a.id"
182
+ result = translate_to_sql(parse_query(q), {"ids": []})
183
+ assert result.sql is not None