Skip to content

Commit dd0bbd9

Browse files
committed
feat: support named parameters in SQL queries and enhance type conversion
1 parent ef67091 commit dd0bbd9

5 files changed

Lines changed: 60 additions & 5 deletions

File tree

bindings/python/docs/api/database.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Execute a query and return results. Queries are read-only and don't require a tr
143143

144144
- `language` (str): Query language - `"sql"`, `"opencypher"`, `"mongo"`, `"graphql"`
145145
- `command` (str): Query string
146-
- `*args`: Optional parameters to bind to the query
146+
- `*args`: Optional positional parameters, or one mapping for named parameters
147147

148148
**Returns:**
149149

@@ -164,6 +164,13 @@ for record in result:
164164
# Parameterized query
165165
result = db.query("sql", "SELECT FROM Person WHERE age > ?", 25)
166166

167+
# Named parameters
168+
result = db.query(
169+
"sql",
170+
"SELECT FROM Person WHERE age IN :ages ORDER BY age",
171+
{"ages": [25, 30, 35]},
172+
)
173+
167174
# OpenCypher query
168175
result = db.query("opencypher", """
169176
MATCH (p:Person)-[:Knows]->(friend)
@@ -195,7 +202,7 @@ Execute a command (write operation). Commands modify data and **require a transa
195202

196203
- `language` (str): Command language (usually `"sql"` or `"opencypher"`)
197204
- `command` (str): Command string
198-
- `*args`: Optional parameters
205+
- `*args`: Optional positional parameters, or one mapping for named parameters
199206

200207
**Returns:**
201208

@@ -223,7 +230,11 @@ db.command(
223230
# Data operations must be in a transaction
224231
with db.transaction():
225232
db.command("sql", "INSERT INTO Person SET name = ?, age = ?", "Alice", 30)
226-
db.command("sql", "UPDATE Person SET age = 31 WHERE name = 'Alice'")
233+
db.command(
234+
"sql",
235+
"UPDATE Person SET age = :age WHERE name = :name",
236+
{"age": 31, "name": "Alice"},
237+
)
227238
db.command("sql", "DELETE FROM Person WHERE name = 'Alice'")
228239
```
229240

bindings/python/docs/api/type_conversion.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ results = db.query(
302302
25, datetime(2024, 1, 1) # Python types converted to Java
303303
)
304304

305+
results = db.query(
306+
"sql",
307+
"SELECT FROM User WHERE age IN :ages",
308+
{"ages": [25, 30, 35]} # dict/list converted to Java Map/List
309+
)
310+
305311
# Reading results (automatically converted back to Python)
306312
for result in results:
307313
name = result.get("name") # str

bindings/python/docs/guide/core/queries.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,19 @@ result = db.query(
137137
{"name": "Alice", "min_age": 25}
138138
)
139139

140+
# Named collection parameter
141+
result = db.query(
142+
"sql",
143+
"SELECT FROM Person WHERE age IN :ages ORDER BY age",
144+
{"ages": [25, 30, 35]}
145+
)
146+
140147
# Positional parameters
141148
result = db.query(
142149
"sql",
143150
"SELECT FROM Person WHERE name = ? AND age > ?",
144-
["Alice", 25]
151+
"Alice",
152+
25,
145153
)
146154
```
147155

bindings/python/src/arcadedb_embedded/core.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
Database and DatabaseFactory classes for embedded database access.
55
"""
66

7+
from collections.abc import Mapping
78
from os import PathLike
8-
from typing import Any, List, Mapping, Optional
9+
from typing import Any, List, Optional
910

1011
from .exceptions import ArcadeDBError
1112
from .graph import Document, Edge, Vertex
@@ -14,6 +15,7 @@
1415
from .importer import import_documents as run_document_import
1516
from .jvm import start_jvm
1617
from .results import ResultSet
18+
from .type_conversion import convert_python_to_java
1719
from .transactions import TransactionContext
1820
from .vector import to_java_float_array
1921

@@ -50,6 +52,8 @@ def _convert_args(args):
5052
for arg in args:
5153
if np is not None and isinstance(arg, np.ndarray):
5254
converted_args.append(to_java_float_array(arg))
55+
elif isinstance(arg, Mapping):
56+
converted_args.append(convert_python_to_java(arg))
5357
else:
5458
converted_args.append(arg)
5559

bindings/python/tests/test_hash_index_schema.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,29 @@ def test_hash_index_schema_get_or_create_and_force_drop(temp_db_path):
8282

8383
schema.drop_index(index_name, force=True)
8484
assert not schema.exists_index(index_name)
85+
86+
87+
def test_indexed_in_named_list_parameter_returns_rows(temp_db_path):
88+
"""Indexed SQL IN parameters should expand Python list values in named args."""
89+
with arcadedb.create_database(temp_db_path) as db:
90+
schema = db.schema
91+
schema.create_document_type("IssueItem")
92+
schema.create_property("IssueItem", "code", arcadedb.PropertyType.INTEGER)
93+
schema.create_index(
94+
"IssueItem", ["code"], index_type=arcadedb.IndexType.LSM_TREE
95+
)
96+
97+
with db.transaction():
98+
db.command("sql", "INSERT INTO IssueItem SET code = 1, name = 'one'")
99+
db.command("sql", "INSERT INTO IssueItem SET code = 2, name = 'two'")
100+
db.command("sql", "INSERT INTO IssueItem SET code = 3, name = 'three'")
101+
102+
rows = list(
103+
db.query(
104+
"sql",
105+
"SELECT code FROM IssueItem WHERE code IN :codes ORDER BY code",
106+
{"codes": [1, 2, 3]},
107+
)
108+
)
109+
110+
assert [row.get("code") for row in rows] == [1, 2, 3]

0 commit comments

Comments
 (0)