graphdatascience 1.15.1__tar.gz → 1.16__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 (165) hide show
  1. {graphdatascience-1.15.1/graphdatascience.egg-info → graphdatascience-1.16}/PKG-INFO +2 -2
  2. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/cypher_warning_handler.py +4 -7
  3. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_cypher_runner.py +4 -2
  4. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_entity_ops_runner.py +9 -6
  5. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph_data_science.py +6 -1
  6. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/arrow_query_runner.py +17 -3
  7. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/cypher_graph_constructor.py +4 -4
  8. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/neo4j_query_runner.py +89 -25
  9. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/protocol/project_protocols.py +21 -5
  10. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/protocol/write_protocols.py +16 -10
  11. graphdatascience-1.16/graphdatascience/query_runner/query_mode.py +16 -0
  12. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/query_runner.py +15 -1
  13. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/session_query_runner.py +22 -5
  14. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/standalone_session_query_runner.py +16 -2
  15. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/retry_utils/retry_utils.py +1 -1
  16. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/aura_graph_data_science.py +2 -2
  17. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/dbms/protocol_resolver.py +2 -2
  18. graphdatascience-1.16/graphdatascience/session/dbms_connection_info.py +57 -0
  19. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/dedicated_sessions.py +8 -4
  20. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/gds_sessions.py +25 -3
  21. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/session_info.py +1 -1
  22. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/session_sizes.py +1 -0
  23. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/topological_lp/topological_lp_alpha_runner.py +2 -2
  24. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/utils/direct_util_endpoints.py +2 -2
  25. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/utils/util_proc_runner.py +2 -0
  26. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/utils/util_remote_proc_runner.py +2 -2
  27. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/version.py +1 -1
  28. {graphdatascience-1.15.1 → graphdatascience-1.16/graphdatascience.egg-info}/PKG-INFO +2 -2
  29. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience.egg-info/SOURCES.txt +1 -0
  30. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience.egg-info/requires.txt +1 -1
  31. {graphdatascience-1.15.1 → graphdatascience-1.16}/requirements/base/base.txt +1 -1
  32. graphdatascience-1.15.1/graphdatascience/session/dbms_connection_info.py +0 -25
  33. {graphdatascience-1.15.1 → graphdatascience-1.16}/LICENSE +0 -0
  34. {graphdatascience-1.15.1 → graphdatascience-1.16}/MANIFEST.in +0 -0
  35. {graphdatascience-1.15.1 → graphdatascience-1.16}/README.md +0 -0
  36. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/__init__.py +0 -0
  37. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/algo/__init__.py +0 -0
  38. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/algo/algo_endpoints.py +0 -0
  39. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/algo/algo_proc_runner.py +0 -0
  40. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/algo/single_mode_algo_endpoints.py +0 -0
  41. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/call_builder.py +0 -0
  42. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/call_parameters.py +0 -0
  43. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/caller_base.py +0 -0
  44. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/endpoints.py +0 -0
  45. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/__init__.py +0 -0
  46. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/client_only_endpoint.py +0 -0
  47. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/endpoint_suggester.py +0 -0
  48. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/gds_not_installed.py +0 -0
  49. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/illegal_attr_checker.py +0 -0
  50. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/unable_to_connect.py +0 -0
  51. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/error/uncallable_namespace.py +0 -0
  52. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/__init__.py +0 -0
  53. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/base_graph_proc_runner.py +0 -0
  54. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_alpha_proc_runner.py +0 -0
  55. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_beta_proc_runner.py +0 -0
  56. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_create_result.py +0 -0
  57. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_endpoints.py +0 -0
  58. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_export_runner.py +0 -0
  59. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_object.py +0 -0
  60. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_proc_runner.py +0 -0
  61. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_project_runner.py +0 -0
  62. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_remote_proc_runner.py +0 -0
  63. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_remote_project_runner.py +0 -0
  64. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_sample_runner.py +0 -0
  65. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/graph_type_check.py +0 -0
  66. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/nx_loader.py +0 -0
  67. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/graph/ogb_loader.py +0 -0
  68. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/ignored_server_endpoints.py +0 -0
  69. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/__init__.py +0 -0
  70. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/graphsage_model.py +0 -0
  71. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/link_prediction_model.py +0 -0
  72. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model.py +0 -0
  73. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model_alpha_proc_runner.py +0 -0
  74. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model_beta_proc_runner.py +0 -0
  75. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model_endpoints.py +0 -0
  76. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model_proc_runner.py +0 -0
  77. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/model_resolver.py +0 -0
  78. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/node_classification_model.py +0 -0
  79. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/node_regression_model.py +0 -0
  80. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/pipeline_model.py +0 -0
  81. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/model/simple_rel_embedding_model.py +0 -0
  82. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/__init__.py +0 -0
  83. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/classification_training_pipeline.py +0 -0
  84. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/lp_pipeline_create_runner.py +0 -0
  85. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/lp_training_pipeline.py +0 -0
  86. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/nc_pipeline_create_runner.py +0 -0
  87. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/nc_training_pipeline.py +0 -0
  88. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/nr_pipeline_create_runner.py +0 -0
  89. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/nr_training_pipeline.py +0 -0
  90. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/pipeline_alpha_proc_runner.py +0 -0
  91. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/pipeline_beta_proc_runner.py +0 -0
  92. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/pipeline_endpoints.py +0 -0
  93. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/pipeline_proc_runner.py +0 -0
  94. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/pipeline/training_pipeline.py +0 -0
  95. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/py.typed +0 -0
  96. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/__init__.py +0 -0
  97. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/arrow_authentication.py +0 -0
  98. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/arrow_endpoint_version.py +0 -0
  99. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/arrow_graph_constructor.py +0 -0
  100. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/arrow_info.py +0 -0
  101. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/gds_arrow_client.py +0 -0
  102. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/graph_constructor.py +0 -0
  103. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/progress/__init__.py +0 -0
  104. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/progress/progress_provider.py +0 -0
  105. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/progress/query_progress_logger.py +0 -0
  106. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/progress/query_progress_provider.py +0 -0
  107. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/progress/static_progress_provider.py +0 -0
  108. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/protocol/__init__.py +0 -0
  109. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/protocol/status.py +0 -0
  110. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/query_runner/termination_flag.py +0 -0
  111. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/__init__.py +0 -0
  112. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/cora/__init__.py +0 -0
  113. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/cora/cora_nodes.parquet.gzip +0 -0
  114. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/cora/cora_rels.parquet.gzip +0 -0
  115. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/cora/serialize_cora.py +0 -0
  116. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/__init__.py +0 -0
  117. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_acted_in.parquet.gzip +0 -0
  118. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_actors.parquet.gzip +0 -0
  119. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_directed_in.parquet.gzip +0 -0
  120. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_directors.parquet.gzip +0 -0
  121. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_movies_with_genre.parquet.gzip +0 -0
  122. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/imdb_movies_without_genre.parquet.gzip +0 -0
  123. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/imdb/serialize_imdb.py +0 -0
  124. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/karate/__init__.py +0 -0
  125. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/karate/karate_club.parquet.gzip +0 -0
  126. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/__init__.py +0 -0
  127. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/artist_nodes.parquet.gzip +0 -0
  128. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/serialize_lastfm.py +0 -0
  129. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/user_friend_df_directed.parquet.gzip +0 -0
  130. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/user_listen_artist_rels.parquet.gzip +0 -0
  131. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/user_nodes.parquet.gzip +0 -0
  132. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/resources/lastfm/user_tag_artist_rels.parquet.gzip +0 -0
  133. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/retry_utils/__init__.py +0 -0
  134. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/retry_utils/retry_config.py +0 -0
  135. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/semantic_version/__init__.py +0 -0
  136. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/semantic_version/semantic_version.py +0 -0
  137. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/server_version/__init__.py +0 -0
  138. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/server_version/compatible_with.py +0 -0
  139. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/server_version/server_version.py +0 -0
  140. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/__init__.py +0 -0
  141. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/algorithm_category.py +0 -0
  142. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/aura_api.py +0 -0
  143. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/aura_api_responses.py +0 -0
  144. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/aura_api_token_authentication.py +0 -0
  145. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/aurads_sessions.py +0 -0
  146. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/cloud_location.py +0 -0
  147. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/dbms/__init__.py +0 -0
  148. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/dbms/protocol_version.py +0 -0
  149. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/session/region_suggester.py +0 -0
  150. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/system/__init__.py +0 -0
  151. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/system/config_endpoints.py +0 -0
  152. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/system/system_endpoints.py +0 -0
  153. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/topological_lp/__init__.py +0 -0
  154. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/topological_lp/topological_lp_endpoints.py +0 -0
  155. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/utils/__init__.py +0 -0
  156. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience/utils/util_node_property_func_runner.py +0 -0
  157. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience.egg-info/dependency_links.txt +0 -0
  158. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience.egg-info/not-zip-safe +0 -0
  159. {graphdatascience-1.15.1 → graphdatascience-1.16}/graphdatascience.egg-info/top_level.txt +0 -0
  160. {graphdatascience-1.15.1 → graphdatascience-1.16}/pyproject.toml +0 -0
  161. {graphdatascience-1.15.1 → graphdatascience-1.16}/requirements/base/networkx.txt +0 -0
  162. {graphdatascience-1.15.1 → graphdatascience-1.16}/requirements/base/ogb.txt +0 -0
  163. {graphdatascience-1.15.1 → graphdatascience-1.16}/requirements/base/rust-ext.txt +0 -0
  164. {graphdatascience-1.15.1 → graphdatascience-1.16}/setup.cfg +0 -0
  165. {graphdatascience-1.15.1 → graphdatascience-1.16}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphdatascience
3
- Version: 1.15.1
3
+ Version: 1.16
4
4
  Summary: A Python client for the Neo4j Graph Data Science (GDS) library
5
5
  Home-page: https://neo4j.com/product/graph-data-science/
6
6
  Author: Neo4j
@@ -32,7 +32,7 @@ Requires-Dist: multimethod<3.0,>=1.0
32
32
  Requires-Dist: neo4j<6.0,>=4.4.12
33
33
  Requires-Dist: numpy<2.3
34
34
  Requires-Dist: pandas<3.0,>=1.0
35
- Requires-Dist: pyarrow<20.0,>=16.0
35
+ Requires-Dist: pyarrow<21.0,>=17.0
36
36
  Requires-Dist: textdistance<5.0,>=4.0
37
37
  Requires-Dist: tqdm<5.0,>=4.0
38
38
  Requires-Dist: typing-extensions<5.0,>=4.0
@@ -19,15 +19,12 @@ def filter_id_func_deprecation_warning() -> Callable[[F], F]:
19
19
  message=r"^The query used a deprecated function: `id`\.",
20
20
  )
21
21
 
22
+ # previously The query used a deprecated function. ('id' is no longer supported)
23
+ # since 2025.04.0 The query used a deprecated function. ('id' has been replaced by 'elementId or an application-generated id')
24
+ # since 2025.06 The query used a deprecated function. ('id' has been replaced by 'elementId or consider using an application-generated id')
22
25
  warnings.filterwarnings(
23
26
  "ignore",
24
- message=r"^The query used a deprecated function. \('id' is no longer supported\)",
25
- )
26
-
27
- # since 2025.04.0
28
- warnings.filterwarnings(
29
- "ignore",
30
- message=r"^The query used a deprecated function. \('id' has been replaced by 'elementId or an application-generated id'\)",
27
+ message=r"The query used a deprecated function. \('id'.*",
31
28
  )
32
29
 
33
30
  return func(self, *args, **kwargs)
@@ -45,7 +45,9 @@ class GraphCypherRunner(CallerBase):
45
45
 
46
46
  GraphCypherRunner._verify_query_ends_with_return_clause(self._namespace, query)
47
47
 
48
- result: Optional[dict[str, Any]] = self._query_runner.run_cypher(query, params, database, False).squeeze()
48
+ result: Optional[dict[str, Any]] = self._query_runner.run_retryable_cypher(
49
+ query, params, database, custom_error=False
50
+ ).squeeze()
49
51
 
50
52
  if not result:
51
53
  raise ValueError("Projected graph cannot be empty.")
@@ -101,7 +103,7 @@ class GraphCypherRunner(CallerBase):
101
103
  at_end = True
102
104
  break
103
105
 
104
- if query_token == "RETURN":
106
+ if query_token.upper() == "RETURN":
105
107
  # State 1: We found the start of a `RETURN` clause.
106
108
  # Check if it is the `RETURN gds.graph.project` call.
107
109
  # We split tokens on `__separators` and flatten the nested iters.
@@ -158,12 +158,13 @@ class GraphNodePropertiesRunner(GraphEntityOpsBaseRunner):
158
158
  duplicate_properties = set(db_node_properties).intersection(set(node_properties))
159
159
  if duplicate_properties:
160
160
  raise ValueError(
161
- f"Duplicate property keys '{duplicate_properties}' in db_node_properties and " f"node_properties."
161
+ f"Duplicate property keys '{duplicate_properties}' in db_node_properties and node_properties."
162
162
  )
163
163
 
164
164
  unique_node_ids = result["nodeId"].drop_duplicates().tolist()
165
- db_properties_df = query_runner.run_cypher(
166
- GraphNodePropertiesRunner._build_query(db_node_properties), {"ids": unique_node_ids}
165
+
166
+ db_properties_df = query_runner.run_retryable_cypher(
167
+ GraphNodePropertiesRunner._build_query(db_node_properties), params={"ids": unique_node_ids}
167
168
  )
168
169
 
169
170
  if "propertyValue" not in result.keys():
@@ -342,7 +343,7 @@ class GraphRelationshipsRunner(GraphEntityOpsBaseRunner):
342
343
  def stream(self, G: Graph, relationship_types: list[str] = ["*"], **config: Any) -> TopologyDataFrame:
343
344
  self._namespace += ".stream"
344
345
  params = CallParameters(graph_name=G.name(), relationship_types=relationship_types, config=config)
345
- result = self._query_runner.call_procedure(endpoint=self._namespace, params=params)
346
+ result = self._query_runner.call_procedure(endpoint=self._namespace, params=params, retryable=True)
346
347
 
347
348
  return TopologyDataFrame(result)
348
349
 
@@ -360,7 +361,9 @@ class GraphRelationshipsBetaRunner(GraphEntityOpsBaseRunner):
360
361
  self._namespace += ".stream"
361
362
  params = CallParameters(graph_name=G.name(), relationship_types=relationship_types, config=config)
362
363
 
363
- return TopologyDataFrame(self._query_runner.call_procedure(endpoint=self._namespace, params=params))
364
+ return TopologyDataFrame(
365
+ self._query_runner.call_procedure(endpoint=self._namespace, params=params, retryable=True)
366
+ )
364
367
 
365
368
  @property
366
369
  @compatible_with("toUndirected", min_inclusive=ServerVersion(2, 3, 0))
@@ -381,7 +384,7 @@ class GraphPropertyRunner(UncallableNamespace, IllegalAttrChecker):
381
384
  self._namespace += ".stream"
382
385
  params = CallParameters(graph_name=G.name(), graph_property=graph_property, config=config)
383
386
 
384
- return self._query_runner.call_procedure(endpoint=self._namespace, params=params)
387
+ return self._query_runner.call_procedure(endpoint=self._namespace, params=params, retryable=True)
385
388
 
386
389
  @compatible_with("drop", min_inclusive=ServerVersion(2, 2, 0))
387
390
  @graph_type_check
@@ -4,6 +4,7 @@ import warnings
4
4
  from types import TracebackType
5
5
  from typing import Any, Optional, Type, Union
6
6
 
7
+ import neo4j
7
8
  from neo4j import Driver
8
9
  from pandas import DataFrame
9
10
 
@@ -78,8 +79,11 @@ class GraphDataScience(DirectEndpoints, UncallableNamespace):
78
79
  if isinstance(endpoint, QueryRunner):
79
80
  self._query_runner = endpoint
80
81
  else:
82
+ db_auth = None
83
+ if auth:
84
+ db_auth = neo4j.basic_auth(*auth)
81
85
  self._query_runner = Neo4jQueryRunner.create_for_db(
82
- endpoint, auth, aura_ds, database, bookmarks, show_progress
86
+ endpoint, db_auth, aura_ds, database, bookmarks, show_progress
83
87
  )
84
88
 
85
89
  self._server_version = self._query_runner.server_version()
@@ -218,6 +222,7 @@ class GraphDataScience(DirectEndpoints, UncallableNamespace):
218
222
  if isinstance(self._query_runner, ArrowQueryRunner):
219
223
  qr = self._query_runner.fallback_query_runner()
220
224
 
225
+ # not using qr.run_retryable_cypher as we dont know if it can be retried
221
226
  return qr.run_cypher(query, params, database, False)
222
227
 
223
228
  def driver_config(self) -> dict[str, Any]:
@@ -6,6 +6,7 @@ from typing import Any, Optional
6
6
  from pandas import DataFrame
7
7
 
8
8
  from graphdatascience.query_runner.arrow_authentication import ArrowAuthentication
9
+ from graphdatascience.query_runner.query_mode import QueryMode
9
10
  from graphdatascience.retry_utils.retry_config import RetryConfig
10
11
 
11
12
  from ..call_parameters import CallParameters
@@ -68,6 +69,15 @@ class ArrowQueryRunner(QueryRunner):
68
69
  ) -> DataFrame:
69
70
  return self._fallback_query_runner.run_cypher(query, params, database, custom_error)
70
71
 
72
+ def run_retryable_cypher(
73
+ self,
74
+ query: str,
75
+ params: Optional[dict[str, Any]] = None,
76
+ database: Optional[str] = None,
77
+ custom_error: bool = True,
78
+ ) -> DataFrame:
79
+ return self._fallback_query_runner.run_retryable_cypher(query, params, database, custom_error=custom_error)
80
+
71
81
  def call_function(self, endpoint: str, params: Optional[CallParameters] = None) -> Any:
72
82
  return self._fallback_query_runner.call_function(endpoint, params)
73
83
 
@@ -77,7 +87,9 @@ class ArrowQueryRunner(QueryRunner):
77
87
  params: Optional[CallParameters] = None,
78
88
  yields: Optional[list[str]] = None,
79
89
  database: Optional[str] = None,
90
+ mode: QueryMode = QueryMode.READ,
80
91
  logging: bool = False,
92
+ retryable: bool = False,
81
93
  custom_error: bool = True,
82
94
  ) -> DataFrame:
83
95
  if params is None:
@@ -171,7 +183,9 @@ class ArrowQueryRunner(QueryRunner):
171
183
  graph_name, self._database_or_throw(), relationship_types, concurrency
172
184
  )
173
185
 
174
- return self._fallback_query_runner.call_procedure(endpoint, params, yields, database, logging, custom_error)
186
+ return self._fallback_query_runner.call_procedure(
187
+ endpoint, params, yields, database, logging=logging, retryable=retryable, custom_error=custom_error
188
+ )
175
189
 
176
190
  def server_version(self) -> ServerVersion:
177
191
  return self._fallback_query_runner.server_version()
@@ -211,10 +225,10 @@ class ArrowQueryRunner(QueryRunner):
211
225
  self._fallback_query_runner.close()
212
226
  self._gds_arrow_client.close()
213
227
 
214
- def clone(self, host: str, port: int) -> "QueryRunner":
228
+ def cloneWithoutRouting(self, host: str, port: int) -> "QueryRunner":
215
229
  return ArrowQueryRunner(
216
230
  self._gds_arrow_client,
217
- self._fallback_query_runner.clone(host, port),
231
+ self._fallback_query_runner.cloneWithoutRouting(host, port),
218
232
  self._server_version,
219
233
  )
220
234
 
@@ -104,15 +104,14 @@ class CypherGraphConstructor(GraphConstructor):
104
104
 
105
105
  def _should_warn_about_arrow_missing(self) -> bool:
106
106
  try:
107
- license: str = self._query_runner.run_cypher(
107
+ license: str = self._query_runner.run_retryable_cypher(
108
108
  "CALL gds.debug.sysInfo() YIELD key, value WHERE key = 'gdsEdition' RETURN value", custom_error=False
109
109
  ).squeeze()
110
110
  should_warn = license == "Licensed"
111
111
  except Exception as e:
112
112
  # It's not a user's concern whether Arrow is set up or not in AuraDS.
113
- if (
114
- "There is no procedure with the name `gds.debug.sysInfo` "
115
- "registered for this database instance." in str(e)
113
+ if "There is no procedure with the name `gds.debug.sysInfo` registered for this database instance." in str(
114
+ e
116
115
  ):
117
116
  should_warn = False
118
117
  else:
@@ -210,6 +209,7 @@ class CypherGraphConstructor(GraphConstructor):
210
209
  "undirectedRelationshipTypes": self._undirected_relationship_types,
211
210
  }
212
211
 
212
+ # not using retryable here as gds.graph.project adds a graph to the gds graph catalog
213
213
  self._query_runner.run_cypher(
214
214
  query,
215
215
  {
@@ -9,6 +9,8 @@ from typing import Any, NamedTuple, Optional, Union
9
9
  import neo4j
10
10
  from pandas import DataFrame
11
11
 
12
+ from graphdatascience.query_runner.query_mode import QueryMode
13
+
12
14
  from ..call_parameters import CallParameters
13
15
  from ..error.endpoint_suggester import generate_suggestive_error_message
14
16
  from ..error.gds_not_installed import GdsNotFound
@@ -30,14 +32,18 @@ class Neo4jQueryRunner(QueryRunner):
30
32
  @staticmethod
31
33
  def create_for_db(
32
34
  endpoint: Union[str, neo4j.Driver],
33
- auth: Optional[tuple[str, str]] = None,
35
+ auth: Union[tuple[str, str], neo4j.Auth, None] = None,
34
36
  aura_ds: bool = False,
35
37
  database: Optional[str] = None,
36
38
  bookmarks: Optional[Any] = None,
37
39
  show_progress: bool = True,
40
+ config: Optional[dict[str, Any]] = None,
38
41
  ) -> Neo4jQueryRunner:
39
42
  if isinstance(endpoint, str):
40
- config: dict[str, Any] = {"user_agent": f"neo4j-graphdatascience-v{__version__}"}
43
+ if config is None:
44
+ config = {}
45
+
46
+ config["user_agent"] = f"neo4j-graphdatascience-v{__version__}"
41
47
 
42
48
  if aura_ds:
43
49
  Neo4jQueryRunner._configure_aura(config)
@@ -73,7 +79,7 @@ class Neo4jQueryRunner(QueryRunner):
73
79
  @staticmethod
74
80
  def create_for_session(
75
81
  endpoint: str,
76
- auth: Optional[tuple[str, str]] = None,
82
+ auth: Union[tuple[str, str], neo4j.Auth, None] = None,
77
83
  show_progress: bool = True,
78
84
  ) -> Neo4jQueryRunner:
79
85
  driver_config: dict[str, Any] = {"user_agent": f"neo4j-graphdatascience-v{__version__}"}
@@ -100,9 +106,13 @@ class Neo4jQueryRunner(QueryRunner):
100
106
 
101
107
  @staticmethod
102
108
  def _configure_aura(config: dict[str, Any]) -> None:
103
- config["max_connection_lifetime"] = 60 * 8 # 8 minutes
104
- config["keep_alive"] = True
105
- config["max_connection_pool_size"] = 50
109
+ # defaults as documented in https://support.neo4j.com/s/article/1500001173021-How-to-handle-Session-Expired-Errors-while-connecting-to-Neo4j-Aura
110
+ config.setdefault("max_connection_lifetime", 60 * 50) # 50 minutes
111
+ config.setdefault("keep_alive", True)
112
+ config.setdefault("max_connection_pool_size", 50)
113
+
114
+ if Neo4jQueryRunner._NEO4J_DRIVER_VERSION >= SemanticVersion(5, 16, 0):
115
+ config.setdefault("liveness_check_timeout", 60 * 5) # 5 minutes
106
116
 
107
117
  @staticmethod
108
118
  def parse_protocol(endpoint: str) -> str:
@@ -115,7 +125,7 @@ class Neo4jQueryRunner(QueryRunner):
115
125
  self,
116
126
  driver: neo4j.Driver,
117
127
  protocol: str,
118
- auth: Optional[tuple[str, str]] = None,
128
+ auth: Union[tuple[str, str], neo4j.Auth, None] = None,
119
129
  config: dict[str, Any] = {},
120
130
  database: Optional[str] = neo4j.DEFAULT_DATABASE,
121
131
  auto_close: bool = False,
@@ -142,8 +152,10 @@ class Neo4jQueryRunner(QueryRunner):
142
152
  def __run_cypher_simplified_for_query_progress_logger(self, query: str, database: Optional[str]) -> DataFrame:
143
153
  # progress logging should not retry a lot as it perodically fetches the latest progress anyway
144
154
  connectivity_retry_config = Neo4jQueryRunner.ConnectivityRetriesConfig(max_retries=2)
155
+ # not using retryable cypher as failing is okay
145
156
  return self.run_cypher(query=query, database=database, connectivity_retry_config=connectivity_retry_config)
146
157
 
158
+ # only use for user defined queries
147
159
  def run_cypher(
148
160
  self,
149
161
  query: str,
@@ -187,12 +199,50 @@ class Neo4jQueryRunner(QueryRunner):
187
199
 
188
200
  return df
189
201
 
190
- def call_function(self, endpoint: str, params: Optional[CallParameters] = None) -> Any:
202
+ # better retry mechanism than run_cypher. The neo4j driver handles retryable errors internally
203
+ def run_retryable_cypher(
204
+ self,
205
+ query: str,
206
+ params: Optional[dict[str, Any]] = None,
207
+ database: Optional[str] = None,
208
+ custom_error: bool = True,
209
+ mode: Optional[QueryMode] = None,
210
+ connectivity_retry_config: Optional[ConnectivityRetriesConfig] = None,
211
+ ) -> DataFrame:
212
+ if not database:
213
+ database = self._database
214
+
215
+ if self._NEO4J_DRIVER_VERSION < SemanticVersion(5, 5, 0):
216
+ return self.run_cypher(query, params, database, custom_error, connectivity_retry_config)
217
+
218
+ if not mode:
219
+ routing = neo4j.RoutingControl.READ
220
+ else:
221
+ routing = mode.neo4j_routing()
222
+
223
+ try:
224
+ return self._driver.execute_query(
225
+ query_=query,
226
+ parameters_=params,
227
+ database_=database,
228
+ result_transformer_=neo4j.Result.to_df,
229
+ bookmark_manager_=self.bookmarks(),
230
+ routing_=routing,
231
+ )
232
+ except Exception as e:
233
+ if custom_error:
234
+ Neo4jQueryRunner.handle_driver_exception(self._driver, e)
235
+ raise e
236
+ else:
237
+ raise e
238
+
239
+ def call_function(self, endpoint: str, params: Optional[CallParameters] = None, custom_error: bool = True) -> Any:
191
240
  if params is None:
192
241
  params = CallParameters()
193
242
  query = f"RETURN {endpoint}({params.placeholder_str()})"
194
243
 
195
- return self.run_cypher(query, params).squeeze()
244
+ # we can use retryable cypher as we expect all gds functions to be idempotent
245
+ return self.run_retryable_cypher(query, params, custom_error=custom_error, mode=QueryMode.READ).squeeze()
196
246
 
197
247
  def call_procedure(
198
248
  self,
@@ -200,7 +250,9 @@ class Neo4jQueryRunner(QueryRunner):
200
250
  params: Optional[CallParameters] = None,
201
251
  yields: Optional[list[str]] = None,
202
252
  database: Optional[str] = None,
253
+ mode: QueryMode = QueryMode.READ,
203
254
  logging: bool = False,
255
+ retryable: bool = False,
204
256
  custom_error: bool = True,
205
257
  ) -> DataFrame:
206
258
  if params is None:
@@ -210,7 +262,10 @@ class Neo4jQueryRunner(QueryRunner):
210
262
  query = f"CALL {endpoint}({params.placeholder_str()}){yields_clause}"
211
263
 
212
264
  def run_cypher_query() -> DataFrame:
213
- return self.run_cypher(query, params, database, custom_error)
265
+ if retryable:
266
+ return self.run_retryable_cypher(query, params, database, custom_error, mode=mode)
267
+ else:
268
+ return self.run_cypher(query, params, database, custom_error)
214
269
 
215
270
  job_id = None if not params else params.get_job_id()
216
271
  if self._resolve_show_progress(logging) and job_id:
@@ -226,7 +281,7 @@ class Neo4jQueryRunner(QueryRunner):
226
281
  return self._server_version
227
282
 
228
283
  try:
229
- server_version_string = self.run_cypher("RETURN gds.version()", custom_error=False).squeeze()
284
+ server_version_string = self.call_function("gds.version", custom_error=False)
230
285
  server_version = ServerVersion.from_string(server_version_string)
231
286
  self._server_version = server_version
232
287
  return server_version
@@ -300,24 +355,25 @@ class Neo4jQueryRunner(QueryRunner):
300
355
  def set_show_progress(self, show_progress: bool) -> None:
301
356
  self._show_progress = show_progress
302
357
 
303
- def clone(self, host: str, port: int) -> QueryRunner:
304
- endpoint = "{}://{}:{}".format(self._protocol, host, port)
358
+ def cloneWithoutRouting(self, host: str, port: int) -> QueryRunner:
359
+ protocol = self._protocol.replace("neo4j", "bolt")
360
+ endpoint = "{}://{}:{}".format(protocol, host, port)
305
361
  driver = neo4j.GraphDatabase.driver(endpoint, auth=self._auth, **self.driver_config())
306
362
 
307
363
  return Neo4jQueryRunner(
308
- driver,
309
- self._protocol,
310
- self._auth,
311
- self._config,
312
- self._database,
313
- self._auto_close,
314
- self._bookmarks,
315
- self._show_progress,
316
- self._instance_description,
364
+ driver=driver,
365
+ protocol=protocol,
366
+ auth=self._auth,
367
+ config=self._config,
368
+ database=self._database,
369
+ auto_close=self._auto_close,
370
+ bookmarks=self._bookmarks,
371
+ show_progress=self._show_progress,
372
+ instance_description=self._instance_description,
317
373
  )
318
374
 
319
375
  @staticmethod
320
- def handle_driver_exception(session: neo4j.Session, e: Exception) -> None:
376
+ def handle_driver_exception(cypher_executor: Union[neo4j.Session, neo4j.Driver], e: Exception) -> None:
321
377
  reg_gds_hit = re.search(
322
378
  r"There is no procedure with the name `(gds(?:\.\w+)+)` registered for this database instance",
323
379
  str(e),
@@ -327,8 +383,16 @@ class Neo4jQueryRunner(QueryRunner):
327
383
 
328
384
  requested_endpoint = reg_gds_hit.group(1)
329
385
 
330
- list_result = session.run("CALL gds.list() YIELD name")
331
- all_endpoints = list_result.to_df()["name"].tolist()
386
+ if isinstance(cypher_executor, neo4j.Session):
387
+ list_result = cypher_executor.run("CALL gds.list() YIELD name")
388
+ all_endpoints = list_result.to_df()["name"].tolist()
389
+ elif isinstance(cypher_executor, neo4j.Driver):
390
+ result = cypher_executor.execute_query("CALL gds.list() YIELD name", result_transformer_=neo4j.Result.to_df)
391
+ all_endpoints = result["name"].tolist()
392
+ else:
393
+ raise TypeError(
394
+ f"Expected cypher_executor to be a neo4j.Session or neo4j.Driver, got {type(cypher_executor)}"
395
+ )
332
396
 
333
397
  raise SyntaxError(generate_suggestive_error_message(requested_endpoint, all_endpoints)) from e
334
398
 
@@ -68,7 +68,9 @@ class ProjectProtocolV1(ProjectProtocol):
68
68
  logging: bool = False,
69
69
  ) -> DataFrame:
70
70
  versioned_endpoint = ProtocolVersion.V1.versioned_procedure_name(endpoint)
71
- return query_runner.call_procedure(versioned_endpoint, params, yields, database, logging, False)
71
+ return query_runner.call_procedure(
72
+ versioned_endpoint, params, yields, database=database, logging=logging, retryable=False, custom_error=False
73
+ )
72
74
 
73
75
 
74
76
  class ProjectProtocolV2(ProjectProtocol):
@@ -97,7 +99,9 @@ class ProjectProtocolV2(ProjectProtocol):
97
99
  logging: bool = False,
98
100
  ) -> DataFrame:
99
101
  versioned_endpoint = ProtocolVersion.V2.versioned_procedure_name(endpoint)
100
- return query_runner.call_procedure(versioned_endpoint, params, yields, database, logging, False)
102
+ return query_runner.call_procedure(
103
+ versioned_endpoint, params, yields, database=database, logging=logging, retryable=False, custom_error=False
104
+ )
101
105
 
102
106
 
103
107
  class ProjectProtocolV3(ProjectProtocol):
@@ -134,11 +138,17 @@ class ProjectProtocolV3(ProjectProtocol):
134
138
 
135
139
  # We need to pin the driver to a specific cluster member
136
140
  response = query_runner.call_procedure(
137
- ProtocolVersion.V3.versioned_procedure_name(endpoint), params, yields, database, logging, False
141
+ ProtocolVersion.V3.versioned_procedure_name(endpoint),
142
+ params,
143
+ yields,
144
+ database,
145
+ logging=logging,
146
+ custom_error=False,
147
+ retryable=True,
138
148
  ).squeeze()
139
149
  member_host = response["host"]
140
150
  member_port = response["port"] if ("port" in response.index) else 7687
141
- projection_query_runner = query_runner.clone(member_host, member_port)
151
+ projection_query_runner = query_runner.cloneWithoutRouting(member_host, member_port)
142
152
 
143
153
  @retry(
144
154
  reraise=True,
@@ -149,7 +159,13 @@ class ProjectProtocolV3(ProjectProtocol):
149
159
  def project_fn() -> DataFrame:
150
160
  termination_flag.assert_running()
151
161
  return projection_query_runner.call_procedure(
152
- ProtocolVersion.V3.versioned_procedure_name(endpoint), params, yields, database, logging, False
162
+ ProtocolVersion.V3.versioned_procedure_name(endpoint),
163
+ params,
164
+ yields,
165
+ database=database,
166
+ logging=logging,
167
+ retryable=True,
168
+ custom_error=False,
153
169
  )
154
170
 
155
171
  projection_result = project_fn()
@@ -5,9 +5,10 @@ from typing import Any, Optional
5
5
  from pandas import DataFrame
6
6
  from tenacity import retry, retry_if_result, wait_incrementing
7
7
 
8
- from graphdatascience import QueryRunner
9
8
  from graphdatascience.call_parameters import CallParameters
10
9
  from graphdatascience.query_runner.protocol.status import Status
10
+ from graphdatascience.query_runner.query_mode import QueryMode
11
+ from graphdatascience.query_runner.query_runner import QueryRunner
11
12
  from graphdatascience.query_runner.termination_flag import TerminationFlag
12
13
  from graphdatascience.retry_utils.retry_utils import before_log
13
14
  from graphdatascience.session.dbms.protocol_version import ProtocolVersion
@@ -73,9 +74,11 @@ class RemoteWriteBackV1(WriteProtocol):
73
74
  ProtocolVersion.V1.versioned_procedure_name("gds.arrow.write"),
74
75
  parameters,
75
76
  yields,
76
- None,
77
- False,
78
- False,
77
+ retryable=False,
78
+ database=None,
79
+ logging=False,
80
+ mode=QueryMode.WRITE,
81
+ custom_error=False,
79
82
  )
80
83
 
81
84
 
@@ -111,9 +114,11 @@ class RemoteWriteBackV2(WriteProtocol):
111
114
  ProtocolVersion.V2.versioned_procedure_name("gds.arrow.write"),
112
115
  parameters,
113
116
  yields,
114
- None,
115
- False,
116
- False,
117
+ retryable=False,
118
+ database=None,
119
+ logging=False,
120
+ mode=QueryMode.WRITE,
121
+ custom_error=False,
117
122
  )
118
123
 
119
124
 
@@ -157,9 +162,10 @@ class RemoteWriteBackV3(WriteProtocol):
157
162
  ProtocolVersion.V3.versioned_procedure_name("gds.arrow.write"),
158
163
  parameters,
159
164
  yields,
160
- None,
161
- False,
162
- False,
165
+ retryable=True,
166
+ logging=False,
167
+ mode=QueryMode.WRITE,
168
+ custom_error=False,
163
169
  )
164
170
 
165
171
  return write_fn()
@@ -0,0 +1,16 @@
1
+ from enum import Enum
2
+
3
+ import neo4j
4
+
5
+
6
+ class QueryMode(str, Enum):
7
+ READ = "read"
8
+ WRITE = "write"
9
+
10
+ def neo4j_routing(self) -> "neo4j.RoutingControl":
11
+ if self == QueryMode.READ:
12
+ return neo4j.RoutingControl.READ
13
+ elif self == QueryMode.WRITE:
14
+ return neo4j.RoutingControl.WRITE
15
+ else:
16
+ raise ValueError(f"Unknown query mode: {self}")
@@ -3,6 +3,8 @@ from typing import Any, Optional
3
3
 
4
4
  from pandas import DataFrame
5
5
 
6
+ from graphdatascience.query_runner.query_mode import QueryMode
7
+
6
8
  from ..call_parameters import CallParameters
7
9
  from ..server_version.server_version import ServerVersion
8
10
  from .graph_constructor import GraphConstructor
@@ -16,7 +18,9 @@ class QueryRunner(ABC):
16
18
  params: Optional[CallParameters] = None,
17
19
  yields: Optional[list[str]] = None,
18
20
  database: Optional[str] = None,
21
+ mode: QueryMode = QueryMode.READ,
19
22
  logging: bool = False,
23
+ retryable: bool = False,
20
24
  custom_error: bool = True,
21
25
  ) -> DataFrame:
22
26
  pass
@@ -35,6 +39,16 @@ class QueryRunner(ABC):
35
39
  ) -> DataFrame:
36
40
  pass
37
41
 
42
+ @abstractmethod
43
+ def run_retryable_cypher(
44
+ self,
45
+ query: str,
46
+ params: Optional[dict[str, Any]] = None,
47
+ database: Optional[str] = None,
48
+ custom_error: bool = True,
49
+ ) -> DataFrame:
50
+ pass
51
+
38
52
  @abstractmethod
39
53
  def server_version(self) -> ServerVersion:
40
54
  pass
@@ -81,7 +95,7 @@ class QueryRunner(ABC):
81
95
  pass
82
96
 
83
97
  @abstractmethod
84
- def clone(self, host: str, port: int) -> "QueryRunner":
98
+ def cloneWithoutRouting(self, host: str, port: int) -> "QueryRunner":
85
99
  pass
86
100
 
87
101
  def set_server_version(self, _: ServerVersion) -> None: