gestaltdb 0.3.0__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.
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: gestaltdb
3
+ Version: 0.3.0
4
+ Summary: A pure Python GraphDB for attributed graphs.
5
+ Requires-Python: <3.14,>=3.9
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: google>=3.0.0
8
+ Requires-Dist: msgpack>=1.1.2
9
+ Requires-Dist: plyvel>=1.5.1
10
+ Requires-Dist: protobuf>=6.33.6
11
+ Provides-Extra: lmdb
12
+ Requires-Dist: lmdb; extra == "lmdb"
13
+ Provides-Extra: leveldb
14
+ Requires-Dist: plyvel; extra == "leveldb"
15
+ Provides-Extra: msgpack
16
+ Requires-Dist: msgpack; extra == "msgpack"
17
+ Provides-Extra: protobuf
18
+ Requires-Dist: protobuf; extra == "protobuf"
19
+ Provides-Extra: bloom
20
+ Requires-Dist: pybloom-live; extra == "bloom"
21
+ Provides-Extra: coverage
22
+ Requires-Dist: coverage; extra == "coverage"
23
+ Requires-Dist: pytest; extra == "coverage"
24
+ Provides-Extra: docs
25
+ Requires-Dist: furo; extra == "docs"
26
+ Requires-Dist: myst-parser; extra == "docs"
27
+ Requires-Dist: sphinx; extra == "docs"
28
+ Provides-Extra: rocksdb
29
+ Requires-Dist: pyrex-rocksdb>=0.3.0a0; extra == "rocksdb"
30
+ Provides-Extra: arrow
31
+ Requires-Dist: pyarrow; extra == "arrow"
32
+ Provides-Extra: polars
33
+ Requires-Dist: polars; extra == "polars"
34
+ Requires-Dist: pyarrow; extra == "polars"
35
+ Provides-Extra: fast-ingest
36
+ Requires-Dist: pyarrow; extra == "fast-ingest"
37
+ Requires-Dist: polars; extra == "fast-ingest"
38
+ Requires-Dist: pyrex-rocksdb>=0.3.0a0; extra == "fast-ingest"
39
+ Provides-Extra: all
40
+ Requires-Dist: lmdb; extra == "all"
41
+ Requires-Dist: msgpack; extra == "all"
42
+ Requires-Dist: plyvel; extra == "all"
43
+ Requires-Dist: protobuf; extra == "all"
44
+ Requires-Dist: pybloom-live; extra == "all"
45
+ Requires-Dist: pyrex-rocksdb>=0.3.0a0; extra == "all"
46
+ Requires-Dist: pyarrow; extra == "all"
47
+ Requires-Dist: polars; extra == "all"
48
+ Provides-Extra: dev
49
+ Requires-Dist: coverage; extra == "dev"
50
+ Requires-Dist: furo; extra == "dev"
51
+ Requires-Dist: lmdb; extra == "dev"
52
+ Requires-Dist: msgpack; extra == "dev"
53
+ Requires-Dist: myst-parser; extra == "dev"
54
+ Requires-Dist: plyvel; extra == "dev"
55
+ Requires-Dist: protobuf; extra == "dev"
56
+ Requires-Dist: pybloom-live; extra == "dev"
57
+ Requires-Dist: pyrex-rocksdb>=0.3.0a0; extra == "dev"
58
+ Requires-Dist: pyarrow; extra == "dev"
59
+ Requires-Dist: polars; extra == "dev"
60
+ Requires-Dist: pytest; extra == "dev"
61
+ Requires-Dist: sphinx; extra == "dev"
62
+
63
+ # GestaltDB
64
+
65
+ ![Coverage](https://raw.githubusercontent.com/mylonasc/gestaltdb/refs/heads/main/assets/coverage_badge.svg)
66
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue.svg)](https://mylonasc.github.io/gestaltdb/)
67
+
68
+ GestaltDB is a pure Python graph database toolkit for attributed graphs. It stores nodes, edges, labels, typed adjacency records, and property indexes on embedded key-value backends.
69
+
70
+ Documentation: https://mylonasc.github.io/gestaltdb/
71
+
72
+ ## Install From PyPI
73
+
74
+ With pip:
75
+
76
+ ```sh
77
+ python -m pip install gestaltdb
78
+ ```
79
+
80
+ With uv:
81
+
82
+ ```sh
83
+ uv add gestaltdb
84
+ ```
85
+
86
+ Install columnar ingestion dependencies:
87
+
88
+ ```sh
89
+ python -m pip install "gestaltdb[arrow,polars]"
90
+ ```
91
+
92
+ Install all optional backends and serializers:
93
+
94
+ ```sh
95
+ python -m pip install "gestaltdb[all]"
96
+ ```
97
+
98
+ Optional extras include `lmdb`, `leveldb`, `rocksdb`, `arrow`, `polars`, `fast-ingest`, `msgpack`, `protobuf`, `bloom`, `docs`, `dev`, and `all`.
99
+
100
+ ## Basic Example
101
+
102
+ ```python
103
+ from tempfile import TemporaryDirectory
104
+
105
+ from gestaltdb.graphdb import Edge, GraphDB, Node
106
+ from gestaltdb.kvstores import LevelDBStore
107
+ from gestaltdb.serializers import PickleSerializer
108
+
109
+ with TemporaryDirectory() as tmpdir:
110
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), PickleSerializer())
111
+
112
+ graph.put_node(Node(node_id="alice", labels=["Person"], properties={"name": "Alice"}))
113
+ graph.put_node(Node(node_id="bob", labels=["Person"], properties={"name": "Bob"}))
114
+ graph.put_edge(Edge(
115
+ edge_id="alice-knows-bob",
116
+ source="alice",
117
+ target="bob",
118
+ properties={"type": "knows", "since": 2024},
119
+ ))
120
+
121
+ result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
122
+ print(result.records)
123
+
124
+ graph.close()
125
+ ```
126
+
127
+ ## Arrow Ingestion Example
128
+
129
+ This example ingests entity columns from PyArrow arrays. `JSONSerializer` lets GestaltDB build node and edge payloads from structured columns.
130
+
131
+ ```python
132
+ from tempfile import TemporaryDirectory
133
+
134
+ import pyarrow as pa
135
+
136
+ from gestaltdb.graphdb import GraphDB
137
+ from gestaltdb.kvstores import LevelDBStore
138
+ from gestaltdb.serializers import JSONSerializer
139
+
140
+ with TemporaryDirectory() as tmpdir:
141
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())
142
+
143
+ graph.ingest_nodes_arrow_entities(
144
+ pa.array(["alice", "bob", "carol"]),
145
+ labels=pa.array([["Person"], ["Person"], ["Person"]]),
146
+ properties={
147
+ "name": pa.array(["Alice", "Bob", "Carol"]),
148
+ "age": pa.array([34, 36, 29]),
149
+ },
150
+ )
151
+
152
+ graph.ingest_edges_arrow_entities(
153
+ pa.array(["alice-knows-bob", "bob-knows-carol"]),
154
+ pa.array(["alice", "bob"]),
155
+ pa.array(["bob", "carol"]),
156
+ pa.array(["knows", "knows"]),
157
+ properties={"since": pa.array([2024, 2025])},
158
+ )
159
+
160
+ result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
161
+ print(result.records)
162
+
163
+ graph.close()
164
+ ```
165
+
166
+ ## Polars Ingestion Example
167
+
168
+ This example ingests the same graph from Polars DataFrames. Property columns are converted into node and edge payloads during ingestion.
169
+
170
+ ```python
171
+ from tempfile import TemporaryDirectory
172
+
173
+ import polars as pl
174
+
175
+ from gestaltdb.graphdb import GraphDB
176
+ from gestaltdb.kvstores import LevelDBStore
177
+ from gestaltdb.serializers import JSONSerializer
178
+
179
+ nodes = pl.DataFrame({
180
+ "node_id": ["alice", "bob", "carol"],
181
+ "labels": [["Person"], ["Person"], ["Person"]],
182
+ "name": ["Alice", "Bob", "Carol"],
183
+ "age": [34, 36, 29],
184
+ })
185
+
186
+ edges = pl.DataFrame({
187
+ "edge_id": ["alice-knows-bob", "bob-knows-carol"],
188
+ "source": ["alice", "bob"],
189
+ "target": ["bob", "carol"],
190
+ "edge_type": ["knows", "knows"],
191
+ "since": [2024, 2025],
192
+ })
193
+
194
+ with TemporaryDirectory() as tmpdir:
195
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())
196
+
197
+ graph.ingest_nodes_polars_entities(nodes)
198
+ graph.ingest_edges_polars_entities(edges)
199
+
200
+ result = graph.query('MATCH (a:Person) MATCH (a)-[:knows]->(b) RETURN a.name, b.name ORDER BY a.name')
201
+ print(result.records)
202
+
203
+ graph.close()
204
+ ```
205
+
206
+ ## Install From A Checkout
207
+
208
+ From a local checkout:
209
+
210
+ ```sh
211
+ uv sync
212
+ ```
213
+
214
+ Install into another project:
215
+
216
+ ```sh
217
+ uv add /path/to/gestaltdb
218
+ ```
219
+
220
+ With pip:
221
+
222
+ ```sh
223
+ python -m pip install /path/to/gestaltdb
224
+ ```
225
+
226
+ ## Features
227
+
228
+ - Attributed `Node` and `Edge` objects with stable IDs.
229
+ - Native node labels and typed edge traversal through `edge.properties["type"]`.
230
+ - LMDB, LevelDB, and RocksDB/PyRex storage backends.
231
+ - Pickle, JSON, MessagePack, and Protobuf serializers.
232
+ - Label, relationship type, property, composite, and range indexes.
233
+ - Read-only Cypher subset for indexed scans, typed traversal, filtering, ordering, limits, and chained `MATCH` clauses.
234
+ - Bulk and columnar ingestion helpers for Arrow and Polars.
235
+ - Typed path and subgraph sampling.
236
+
237
+ See the full documentation for backend selection, indexing, Cypher syntax, ingestion, sampling, and benchmarks.
238
+
239
+ <details>
240
+ <summary>Name origin</summary>
241
+
242
+ The name GestaltDB is inspired by Gestalt psychology and the idea that the whole is something more than its parts.
243
+
244
+ </details>
@@ -0,0 +1,182 @@
1
+ # GestaltDB
2
+
3
+ ![Coverage](https://raw.githubusercontent.com/mylonasc/gestaltdb/refs/heads/main/assets/coverage_badge.svg)
4
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue.svg)](https://mylonasc.github.io/gestaltdb/)
5
+
6
+ GestaltDB is a pure Python graph database toolkit for attributed graphs. It stores nodes, edges, labels, typed adjacency records, and property indexes on embedded key-value backends.
7
+
8
+ Documentation: https://mylonasc.github.io/gestaltdb/
9
+
10
+ ## Install From PyPI
11
+
12
+ With pip:
13
+
14
+ ```sh
15
+ python -m pip install gestaltdb
16
+ ```
17
+
18
+ With uv:
19
+
20
+ ```sh
21
+ uv add gestaltdb
22
+ ```
23
+
24
+ Install columnar ingestion dependencies:
25
+
26
+ ```sh
27
+ python -m pip install "gestaltdb[arrow,polars]"
28
+ ```
29
+
30
+ Install all optional backends and serializers:
31
+
32
+ ```sh
33
+ python -m pip install "gestaltdb[all]"
34
+ ```
35
+
36
+ Optional extras include `lmdb`, `leveldb`, `rocksdb`, `arrow`, `polars`, `fast-ingest`, `msgpack`, `protobuf`, `bloom`, `docs`, `dev`, and `all`.
37
+
38
+ ## Basic Example
39
+
40
+ ```python
41
+ from tempfile import TemporaryDirectory
42
+
43
+ from gestaltdb.graphdb import Edge, GraphDB, Node
44
+ from gestaltdb.kvstores import LevelDBStore
45
+ from gestaltdb.serializers import PickleSerializer
46
+
47
+ with TemporaryDirectory() as tmpdir:
48
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), PickleSerializer())
49
+
50
+ graph.put_node(Node(node_id="alice", labels=["Person"], properties={"name": "Alice"}))
51
+ graph.put_node(Node(node_id="bob", labels=["Person"], properties={"name": "Bob"}))
52
+ graph.put_edge(Edge(
53
+ edge_id="alice-knows-bob",
54
+ source="alice",
55
+ target="bob",
56
+ properties={"type": "knows", "since": 2024},
57
+ ))
58
+
59
+ result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
60
+ print(result.records)
61
+
62
+ graph.close()
63
+ ```
64
+
65
+ ## Arrow Ingestion Example
66
+
67
+ This example ingests entity columns from PyArrow arrays. `JSONSerializer` lets GestaltDB build node and edge payloads from structured columns.
68
+
69
+ ```python
70
+ from tempfile import TemporaryDirectory
71
+
72
+ import pyarrow as pa
73
+
74
+ from gestaltdb.graphdb import GraphDB
75
+ from gestaltdb.kvstores import LevelDBStore
76
+ from gestaltdb.serializers import JSONSerializer
77
+
78
+ with TemporaryDirectory() as tmpdir:
79
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())
80
+
81
+ graph.ingest_nodes_arrow_entities(
82
+ pa.array(["alice", "bob", "carol"]),
83
+ labels=pa.array([["Person"], ["Person"], ["Person"]]),
84
+ properties={
85
+ "name": pa.array(["Alice", "Bob", "Carol"]),
86
+ "age": pa.array([34, 36, 29]),
87
+ },
88
+ )
89
+
90
+ graph.ingest_edges_arrow_entities(
91
+ pa.array(["alice-knows-bob", "bob-knows-carol"]),
92
+ pa.array(["alice", "bob"]),
93
+ pa.array(["bob", "carol"]),
94
+ pa.array(["knows", "knows"]),
95
+ properties={"since": pa.array([2024, 2025])},
96
+ )
97
+
98
+ result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
99
+ print(result.records)
100
+
101
+ graph.close()
102
+ ```
103
+
104
+ ## Polars Ingestion Example
105
+
106
+ This example ingests the same graph from Polars DataFrames. Property columns are converted into node and edge payloads during ingestion.
107
+
108
+ ```python
109
+ from tempfile import TemporaryDirectory
110
+
111
+ import polars as pl
112
+
113
+ from gestaltdb.graphdb import GraphDB
114
+ from gestaltdb.kvstores import LevelDBStore
115
+ from gestaltdb.serializers import JSONSerializer
116
+
117
+ nodes = pl.DataFrame({
118
+ "node_id": ["alice", "bob", "carol"],
119
+ "labels": [["Person"], ["Person"], ["Person"]],
120
+ "name": ["Alice", "Bob", "Carol"],
121
+ "age": [34, 36, 29],
122
+ })
123
+
124
+ edges = pl.DataFrame({
125
+ "edge_id": ["alice-knows-bob", "bob-knows-carol"],
126
+ "source": ["alice", "bob"],
127
+ "target": ["bob", "carol"],
128
+ "edge_type": ["knows", "knows"],
129
+ "since": [2024, 2025],
130
+ })
131
+
132
+ with TemporaryDirectory() as tmpdir:
133
+ graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())
134
+
135
+ graph.ingest_nodes_polars_entities(nodes)
136
+ graph.ingest_edges_polars_entities(edges)
137
+
138
+ result = graph.query('MATCH (a:Person) MATCH (a)-[:knows]->(b) RETURN a.name, b.name ORDER BY a.name')
139
+ print(result.records)
140
+
141
+ graph.close()
142
+ ```
143
+
144
+ ## Install From A Checkout
145
+
146
+ From a local checkout:
147
+
148
+ ```sh
149
+ uv sync
150
+ ```
151
+
152
+ Install into another project:
153
+
154
+ ```sh
155
+ uv add /path/to/gestaltdb
156
+ ```
157
+
158
+ With pip:
159
+
160
+ ```sh
161
+ python -m pip install /path/to/gestaltdb
162
+ ```
163
+
164
+ ## Features
165
+
166
+ - Attributed `Node` and `Edge` objects with stable IDs.
167
+ - Native node labels and typed edge traversal through `edge.properties["type"]`.
168
+ - LMDB, LevelDB, and RocksDB/PyRex storage backends.
169
+ - Pickle, JSON, MessagePack, and Protobuf serializers.
170
+ - Label, relationship type, property, composite, and range indexes.
171
+ - Read-only Cypher subset for indexed scans, typed traversal, filtering, ordering, limits, and chained `MATCH` clauses.
172
+ - Bulk and columnar ingestion helpers for Arrow and Polars.
173
+ - Typed path and subgraph sampling.
174
+
175
+ See the full documentation for backend selection, indexing, Cypher syntax, ingestion, sampling, and benchmarks.
176
+
177
+ <details>
178
+ <summary>Name origin</summary>
179
+
180
+ The name GestaltDB is inspired by Gestalt psychology and the idea that the whole is something more than its parts.
181
+
182
+ </details>
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gestaltdb"
7
+ version = "0.3.0"
8
+ description = "A pure Python GraphDB for attributed graphs."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9,<3.14"
11
+ dependencies = [
12
+ "google>=3.0.0",
13
+ "msgpack>=1.1.2",
14
+ "plyvel>=1.5.1",
15
+ "protobuf>=6.33.6",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ lmdb = ["lmdb"]
20
+ leveldb = ["plyvel"]
21
+ msgpack = ["msgpack"]
22
+ protobuf = ["protobuf"]
23
+ bloom = ["pybloom-live"]
24
+ coverage = ["coverage", "pytest"]
25
+ docs = ["furo", "myst-parser", "sphinx"]
26
+ rocksdb = ["pyrex-rocksdb>=0.3.0a0"]
27
+ arrow = ["pyarrow"]
28
+ polars = ["polars", "pyarrow"]
29
+ fast-ingest = ["pyarrow", "polars", "pyrex-rocksdb>=0.3.0a0"]
30
+ all = ["lmdb", "msgpack", "plyvel", "protobuf", "pybloom-live", "pyrex-rocksdb>=0.3.0a0", "pyarrow", "polars"]
31
+ dev = ["coverage", "furo", "lmdb", "msgpack", "myst-parser", "plyvel", "protobuf", "pybloom-live", "pyrex-rocksdb>=0.3.0a0", "pyarrow", "polars", "pytest", "sphinx"]
32
+
33
+ [tool.setuptools.packages.find]
34
+ where = ["src"]
35
+
36
+ [tool.pytest.ini_options]
37
+ pythonpath = ["src"]
38
+ testpaths = ["tests"]
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "coverage>=7.10.7",
43
+ "pytest>=8.0",
44
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ """GestaltDB package."""
2
+
3
+ from .sampling import SamplingHop, SamplingPattern
4
+ from .ingestion import EdgeList, NodeList
5
+ from .cypher import QueryResult
6
+
7
+ __all__ = ["EdgeList", "NodeList", "QueryResult", "SamplingHop", "SamplingPattern"]
@@ -0,0 +1,104 @@
1
+ """Minimal read-only Cypher support for GestaltDB.
2
+
3
+ The supported subset maps directly to existing typed adjacency and sampling APIs:
4
+
5
+ MATCH (a {id: "node-id"})-[:TYPE1]->(b)<-[:TYPE2]-(c) RETURN a.name, b LIMIT 10
6
+ CALL pg.sample_typed_paths(["node-id"], [{"edge_type": "TYPE", "sample_size": 2}]) YIELD path RETURN path
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+
13
+ from .cypher_ast import MatchQuery, MultiMatchQuery, NodeScanQuery, RelationshipScanQuery, SampleTypedPathsCall
14
+ from .cypher_plan import LogicalPlan, plan_query
15
+ from .cypher_parser import parse as _parse_query, split_top_level_args as _split_top_level_args
16
+ from .cypher_runtime import QueryContext, execute_match, execute_multi_match, execute_node_scan, execute_relationship_scan
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class QueryResult:
21
+ """Tabular query result returned by ``GraphDB.query``.
22
+
23
+ ``columns`` contains projected column names in return order. ``records`` is
24
+ a list of dictionaries keyed by column name.
25
+
26
+ Examples:
27
+ >>> result = QueryResult(columns=("n",), records=[{"n": "node"}])
28
+ >>> len(result)
29
+ 1
30
+ >>> list(result)[0]["n"]
31
+ 'node'
32
+ """
33
+
34
+ columns: tuple[str, ...]
35
+ records: list[dict[str, object]]
36
+
37
+ def __iter__(self):
38
+ """Iterate over result records."""
39
+ return iter(self.records)
40
+
41
+ def __len__(self):
42
+ """Return the number of result records."""
43
+ return len(self.records)
44
+
45
+
46
+ def parse(query: str) -> MatchQuery | SampleTypedPathsCall | NodeScanQuery | RelationshipScanQuery | MultiMatchQuery:
47
+ """Parse the supported Cypher subset.
48
+
49
+ Args:
50
+ query: Cypher query text.
51
+
52
+ Returns:
53
+ Parsed query object.
54
+
55
+ Raises:
56
+ ValueError: If the query is outside the supported subset.
57
+
58
+ Examples:
59
+ >>> parse('MATCH (n:Drug) RETURN n').label
60
+ 'Drug'
61
+ """
62
+ return _parse_query(query)
63
+
64
+
65
+ def plan(query: str) -> LogicalPlan:
66
+ """Return the logical plan for a supported Cypher query."""
67
+ return plan_query(parse(query))
68
+
69
+
70
+ def execute(graph, query: str, parameters: dict[str, object] | None = None) -> QueryResult:
71
+ """Execute a supported Cypher query against a ``GraphDB`` instance.
72
+
73
+ Args:
74
+ graph: ``GraphDB`` instance used for indexed lookups and traversal.
75
+ query: Cypher query text.
76
+
77
+ Returns:
78
+ ``QueryResult`` with projected records.
79
+
80
+ Examples:
81
+ >>> execute(graph_db, 'MATCH (n:Drug) RETURN n') # doctest: +SKIP
82
+ """
83
+ parsed = parse(query)
84
+ plan_query(parsed)
85
+ parameters = parameters or {}
86
+ if isinstance(parsed, SampleTypedPathsCall):
87
+ paths = graph.sample_typed_paths(parsed.seed_ids, parsed.pattern)
88
+ if parsed.limit is not None:
89
+ paths = paths[:parsed.limit]
90
+ return QueryResult(
91
+ columns=parsed.returns,
92
+ records=[{"path": path} for path in paths],
93
+ )
94
+ if isinstance(parsed, NodeScanQuery):
95
+ records = execute_node_scan(parsed, QueryContext(graph=graph, parameters=parameters))
96
+ return QueryResult(columns=parsed.returns, records=records)
97
+ if isinstance(parsed, RelationshipScanQuery):
98
+ records = execute_relationship_scan(parsed, QueryContext(graph=graph, parameters=parameters))
99
+ return QueryResult(columns=parsed.returns, records=records)
100
+ if isinstance(parsed, MultiMatchQuery):
101
+ records = execute_multi_match(parsed, QueryContext(graph=graph, parameters=parameters))
102
+ return QueryResult(columns=parsed.returns, records=records)
103
+ records = execute_match(parsed, QueryContext(graph=graph, parameters=parameters))
104
+ return QueryResult(columns=parsed.returns, records=records)