From 62a3ede02504449508826490ce68031f54db45d0 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 13:44:46 +0900 Subject: [PATCH 1/7] Docs: say which query language to use; example 02 leads with openCypher databases-and-queries.adoc gets a short 'Which query language' paragraph: openCypher for graph patterns and traversals, SQL for documents, tables, schema, inserts, filters, aggregates and pagination, the older SQL graph syntax (MATCH {...}, out(), TRAVERSE) supported but not recommended for new code, with links to the main SQL and openCypher references instead of repeating them. Example 02 runs and prints its openCypher section first and describes SQL MATCH as the older syntax; plain SQL stays for documents and tables. Examples 05 and 06 are left for a follow-up after their run on the small dataset (docs audit 2026-11-04, item M3). Closes #263 Co-Authored-By: Claude Opus 5.5 --- .../pages/how-to/databases-and-queries.adoc | 6 +++ examples/02_social_network_graph.py | 42 +++++++++++-------- examples/README.md | 4 +- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc index bde487dd..3bdbd044 100644 --- a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc +++ b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc @@ -88,6 +88,12 @@ Each Python process starts one JVM, on the first database or server it creates. == Write and query +=== Which query language + +Both languages run through the same two calls, so the choice depends on the question you ask. Use openCypher for graph patterns and traversals: who knows whom, friends of friends, paths of a given length. Use SQL for document and table queries, schema, inserts, filters, aggregates, and pagination. The older SQL graph syntax (`MATCH {type: Person, as: p}` patterns, `out()`, `in()`, `outE()`, `inE()`, and `TRAVERSE`) is supported but not recommended for new code: openCypher is a graph pattern language shared by many databases and close to the ISO GQL standard (ISO/IEC 39075), so queries and skills carry over. This page shows both languages on one database. The languages themselves are documented in the main ArcadeDB manual, see the {arcadedb-docs-url}/reference/sql/sql-introduction[SQL reference] and the {arcadedb-docs-url}/reference/cypher/chapter[openCypher reference]; this guide does not repeat them. + +=== Write and read + [source,python,subs="attributes+"] ---- {import-stmt} diff --git a/examples/02_social_network_graph.py b/examples/02_social_network_graph.py index 5db8926d..836da7a2 100644 --- a/examples/02_social_network_graph.py +++ b/examples/02_social_network_graph.py @@ -8,20 +8,21 @@ 1. Creating vertex and edge types (schema definition) 2. Creating vertices (people) with properties 3. Creating edges (friendships) between vertices -4. Querying the graph using SQL MATCH and OpenCypher dialects +4. Querying the graph with openCypher (recommended) and the older SQL MATCH syntax 5. Finding friends, friends of friends, and mutual connections -6. Comparing SQL vs OpenCypher syntax and performance +6. Comparing openCypher with the older SQL MATCH syntax, in syntax and timing Key Concepts: - Vertices represent entities (Person) - Edges represent relationships (FRIEND_OF) - Properties store data on both vertices and edges - Graph traversal allows complex relationship queries -- SQL MATCH and OpenCypher provide different graph querying capabilities +- openCypher and SQL MATCH are two ways to ask the same graph questions ArcadeDB Query Languages: -- SQL MATCH: ArcadeDB's extended SQL syntax for graph traversal -- OpenCypher: openCypher query language for declarative graph patterns +- OpenCypher: the openCypher language for graph patterns and traversals (use it for new code) +- SQL MATCH: ArcadeDB's older SQL graph syntax, supported but not recommended for new code +- Plain SQL stays the language for document and table queries (inserts, aggregates, IS NULL filters) ⚔ Performance Findings (This Example): - SQL MATCH and OpenCypher performance varies by dataset and runtime configuration @@ -374,15 +375,15 @@ def create_sample_data(db): def demonstrate_graph_queries(db): - """Demonstrate various graph queries using SQL and OpenCypher""" + """Demonstrate various graph queries using OpenCypher and SQL""" print("\nšŸ” Demonstrating graph queries...") - # SQL-based queries - demonstrate_sql_queries(db) - - # OpenCypher-based queries + # OpenCypher-based queries (the recommended language for graph patterns) demonstrate_opencypher_queries(db) + # SQL-based queries (the older SQL MATCH syntax, plus plain SQL on the same data) + demonstrate_sql_queries(db) + def demonstrate_sql_queries(db): """Demonstrate graph queries using ArcadeDB's SQL MATCH syntax""" @@ -793,22 +794,27 @@ def compare_query_languages(db): # Show syntax differences print("\n šŸ” Key Syntax Differences:") print(" " + "-" * 40) - print(" SQL MATCH:") - print(" • Nodes: {type: Person, as: alias, where: (condition)}") - print(" • Edges: -EDGE_TYPE-> or -EDGE_TYPE->{as: alias}") - print(" • Conditions: where: (property = 'value')") - print(" • More verbose but explicit about types") - print() print(" OpenCypher:") print(" • Pattern matching with MATCH and relationships") print(" • Filters: WHERE clauses") print(" • Aggregations: count(), distinct") print(" • Declarative graph patterns") + print() + print(" SQL MATCH:") + print(" • Nodes: {type: Person, as: alias, where: (condition)}") + print(" • Edges: -EDGE_TYPE-> or -EDGE_TYPE->{as: alias}") + print(" • Conditions: where: (property = 'value')") + print(" • More verbose but explicit about types") print("\n šŸ’” When to use each:") print(" " + "-" * 30) - print(" • SQL MATCH: Mixing graph and relational queries") - print(" • OpenCypher: Declarative graph patterns") + print( + " • OpenCypher: graph patterns and traversals (recommended for new code)" + ) + print( + " • SQL MATCH: older graph syntax, supported but not recommended for new code" + ) + print(" • Plain SQL: document and table queries") print(" • Both support the same underlying graph operations") print(f" ā±ļø Comparison section: {time.time() - section_start:.3f}s") diff --git a/examples/README.md b/examples/README.md index eb99c851..46303d35 100644 --- a/examples/README.md +++ b/examples/README.md @@ -35,14 +35,14 @@ Perfect introduction to ArcadeDB basics: --- ### šŸ”— [02_social_network_graph.py](./02_social_network_graph.py) -**Vertex Types | Edge Types | Graph Traversal | SQL MATCH vs OpenCypher | NULL Handling** +**Vertex Types | Edge Types | Graph Traversal | OpenCypher vs SQL MATCH | NULL Handling** Complete social network modeling with graph database: - Creating vertex types (Person) and edge types (FRIEND_OF) with rich properties - NULL value handling for optional fields (email, phone, reputation) - Bidirectional relationships with metadata (since, closeness) - Graph traversal patterns (friends, friends-of-friends, mutual connections) -- Comparing SQL MATCH vs OpenCypher query languages +- Comparing OpenCypher (recommended for graph patterns) with the older SQL MATCH syntax - Variable-length path queries (`*1..3`) and graph aggregations - NULL filtering (IS NULL queries for missing contact info) - Relationship property queries (closeness filtering) From b42429fa3733cffecd141bfe41f3c419ea087e6f Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:38:42 +0900 Subject: [PATCH 2/7] Docs: what find_nearest's ef_search=None does, and the 26.11.1 change of the default width With ef_search=None the width comes from the index's efSearch metadata, else from the engine: max(k, 100) under 10,000 vectors, max(2k, 100) from 10,000 on (ArcadeData/arcadedb#10704, in 26.11.1; before max(2k, 20), SIFT 100k recall@10 0.929 vs 0.998). A search on a large index is more accurate and slower than on earlier releases; ef_search trades it back. Found while answering ArcadeData/arcadedb#11323. Co-Authored-By: Claude Opus 5.5 --- docs/modules/ROOT/pages/how-to/vectors.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/ROOT/pages/how-to/vectors.adoc b/docs/modules/ROOT/pages/how-to/vectors.adoc index 2b56a7d3..b36e0ee5 100644 --- a/docs/modules/ROOT/pages/how-to/vectors.adoc +++ b/docs/modules/ROOT/pages/how-to/vectors.adoc @@ -164,6 +164,8 @@ print([row["name"] for row in rows]) `VectorIndex.find_nearest(query_vector, k=10, ef_search=None, allowed_rids=None, *, include_vector=True)` searches without a statement. It takes a list or a NumPy array and returns a list of `(record, distance)` tuples, nearest first. Use it when you already hold the index object, or when you have a list of record ids to search within. +With `ef_search=None` the search width comes from the index's `efSearch` metadata when it is set, otherwise from the engine: `max(k, 100)` candidates on an index of under 10,000 vectors and `max(2k, 100)` from 10,000 vectors on. Before ArcadeDB 26.11.1 the larger indexes searched `max(2k, 20)`, which gave recall@10 0.929 on SIFT 100k against 0.998 now (https://github.com/ArcadeData/arcadedb/issues/10704[ArcadeData/arcadedb#10704]), so a search on a large index is more accurate and slower than on earlier releases. Pass a smaller `ef_search` to trade recall for speed, or a larger one for more recall. + On the native package the query vector crosses as one `float32` buffer whatever its form (a list, a `float64` array, or a `float32` array), so you do not need to convert it first. A vector index stores `float32`, so the numbers are rounded the same way as before. The table gives the median `find_nearest` latency with this change over the median without it (95% interval, below 1 is faster), measured on the native wheel `26.11.1.dev0` on one Linux x86_64 laptop with 11 to 12 clean pairs per figure. The wheel built before this change was the baseline. [cols="3,2,2",options="header"] From f7504e2706b1a2ee8cd87e2b62360b6ff1b3f73c Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:41:38 +0900 Subject: [PATCH 3/7] Docs: read-only open (open_database(path, read_only=True)), what it refuses, no speed claim Both builds implement and test it (tests/test_open_read_only.py: reads, INSERT, CREATE DOCUMENT TYPE and save() refused with 'read only', commit/rollback of a read transaction returns from 26.11.1, DatabaseFactory.open(read_only=True)); no page described it. Co-Authored-By: Claude Opus 5.5 --- docs/modules/ROOT/pages/how-to/databases-and-queries.adoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc index 3bdbd044..e8d1bf0f 100644 --- a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc +++ b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc @@ -76,6 +76,12 @@ Notes on the script: * `db.drop()` deletes all files of the database. It cannot be undone. * `db.is_transaction_active()` is `True` only inside `with db.transaction():`. +=== Read-only open + +`open_database(path, read_only=True)` opens an existing database for reading only; `DatabaseFactory(path).open(read_only=True)` does the same. Queries, counts and record reads work as usual and `db.is_read_only()` is `True`. Every write raises `ArcadeDBError` with "read only" in the message: an `INSERT`, a schema change such as `CREATE DOCUMENT TYPE`, or `save()` on a record. Reads inside `with db.transaction():` work too; on ArcadeDB 26.11.1 and later the commit or rollback of such a transaction returns, while earlier engines raise the same read-only error there. + +Use it when a process only reads a finished database, so that a stray write fails instead of changing the data. It is a safety setting, not a speed setting: no measurement here shows reads getting faster. + === Locking The open database holds an operating-system lock on `database.lck` in its directory. A second process cannot open the same directory while the first holds it; open it in one process and share the handle between that process's threads. From dc986dfe696b767d2f43554d8e2ac88c2124f4ad Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:55:45 +0900 Subject: [PATCH 4/7] Docs: fix statements the code contradicts (#267) - jvm_kwargs on the native build: create_database and open_database accept it and ignore it with a logged warning (_native/database.py NativeDatabaseFactory), they do not 'not exist'. installation.adoc and which-package.adoc said so. - Studio: the native pom has no arcadedb-studio, so the native server has none; installation.adoc's shared 'What is included' list now says Studio is JVM only. - Docstrings pointed to pages that do not exist (docs/api/graph_batch.md, 'the Python functions guide', 'the performance guide'). graph_batch.py now points to the bulk-loads page, the other two drop the pointer. - README: 29 example scripts (was 27), Python range per package (native tested on 3.11 to 3.14 and installs on 3.15, JVM is >=3.11,<3.15), the polars extra of arcadedb-jvm. - bulk-loads.adoc: append_samples exists on both packages; the address path is the native fast path. - concurrency.adoc: the native row said 'alpha quality'; the package is beta (README, classifiers). Sample outputs that need a real rerun (installation.adoc, quickstart.adoc, concurrency.adoc) are left as they are; the pre-release notes change on release day. Co-Authored-By: Claude Opus 5.5 --- README.md | 6 +++--- docs/modules/ROOT/pages/how-to/bulk-loads.adoc | 6 +++--- docs/modules/ROOT/pages/how-to/concurrency.adoc | 2 +- docs/modules/ROOT/pages/installation.adoc | 6 +++--- docs/modules/ROOT/pages/which-package.adoc | 2 +- src/arcadedb/core.py | 2 +- src/arcadedb/graph_batch.py | 5 +++-- src/arcadedb/results.py | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a4186b96..a5169c05 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,10 @@ uv add arcadedb-jvm # JVM, for heavy long-running loads > Old names: `arcadedb-jvm` was formerly `arcadedb-embedded`, and `arcadedb` was formerly `arcadedb-embedded-native` (archived on PyPI). A last `arcadedb-embedded` release will be a deprecation shim that installs `arcadedb`, the native build. -- Python 3.11 to 3.14. CI builds and tests all four versions on every platform below for `main`, every night and each release. A pull request runs a smaller selection. +- Python: `arcadedb` (native) is tested on 3.11 to 3.14 and also installs on 3.15 (`requires-python` is `>=3.11` with no upper bound; the CI runs a subset of the tests on 3.15). `arcadedb-jvm` needs 3.11 to 3.14 (`requires-python` is `>=3.11,<3.15`; its dependency JPype has no 3.15 wheel yet). CI builds and tests the four versions 3.11 to 3.14 on every platform below for `main`, every night and each release. A pull request runs a smaller selection. - Linux x86_64 and ARM64, macOS on Apple Silicon, Windows x86_64, for both packages. The Windows wheel of `arcadedb` is the newest. - Import name: `arcadedb`. Check which package you have with `python -c "import arcadedb; print(arcadedb.__backend__)"`. It prints `native` or `jvm`. -- Optional extras: `arrow`, `pandas`, `vector` for `arcadedb-jvm`; `numpy`, `arrow`, `pandas`, `polars` for `arcadedb`. +- Optional extras: `arrow`, `pandas`, `polars`, `vector` for `arcadedb-jvm`; `numpy`, `arrow`, `pandas`, `polars` for `arcadedb`. - `arcadedb-jvm` bundles its own Java 25 runtime, so you install no Java. `arcadedb` needs no Java at all. ## Quick start @@ -88,7 +88,7 @@ Releases from this repository start with 26.11.1. The 26.10.x wheels were built ## Examples -[`examples/`](https://github.com/ArcadeData/arcadedb-python/tree/main/examples) holds 27 runnable scripts, from a first document store to vector search, time series, graph algorithms and server mode. CI runs them ([`test-python-examples.yml`](https://github.com/ArcadeData/arcadedb-python/blob/main/.github/workflows/test-python-examples.yml)). +[`examples/`](https://github.com/ArcadeData/arcadedb-python/tree/main/examples) holds 29 runnable scripts, from a first document store to vector search, time series, graph algorithms and server mode. CI runs them ([`test-python-examples.yml`](https://github.com/ArcadeData/arcadedb-python/blob/main/.github/workflows/test-python-examples.yml)). ## Build and test from source diff --git a/docs/modules/ROOT/pages/how-to/bulk-loads.adoc b/docs/modules/ROOT/pages/how-to/bulk-loads.adoc index 9febc7f6..3ba0d541 100644 --- a/docs/modules/ROOT/pages/how-to/bulk-loads.adoc +++ b/docs/modules/ROOT/pages/how-to/bulk-loads.adoc @@ -1,5 +1,5 @@ = Bulk loads and commit size -:description: The default commit size of insert_many, graph_batch and create_vertices on the arcadedb (native) and arcadedb-jvm packages, what it means for a failed load, what to do after an interrupted graph batch, how to change it, how insert_many treats Python's cyclic garbage collector on arcadedb-jvm, which property shapes a graph batch loads in bulk on the JVM build, how to pass time-series columns to append_samples on arcadedb (native), and a known limitation: a graph batch does not reuse the space of deleted edges. +:description: The default commit size of insert_many, graph_batch and create_vertices on the arcadedb (native) and arcadedb-jvm packages, what it means for a failed load, what to do after an interrupted graph batch, how to change it, how insert_many treats Python's cyclic garbage collector on arcadedb-jvm, which property shapes a graph batch loads in bulk on the JVM build, how to pass time-series columns to append_samples (the address path is native only), and a known limitation: a graph batch does not reuse the space of deleted edges. `insert_many`, `graph_batch` and `GraphBatch.create_vertices` load data in transactions. The commit size is the number of rows in one transaction. It decides how much work the engine holds in memory before it commits, and how many rows stay in the database when a load fails. @@ -111,9 +111,9 @@ The table is one laptop study of the `arcadedb-jvm` package: per-row time of thi |=== -== Time-series samples with `append_samples` (`arcadedb`, native) +== Time-series samples with `append_samples` -`db.async_executor().append_samples(type_name, timestamps, *columns)` loads a columnar batch into a TIMESERIES type: the timestamps, then one column for each tag and field in the order of the type's declaration. Call `wait_completion()` before you read the samples back. How to declare the type is in the main ArcadeDB docs: see {arcadedb-docs-url}/concepts/timeseries[Time series]. +`db.async_executor().append_samples(type_name, timestamps, *columns)` loads a columnar batch into a TIMESERIES type: the timestamps, then one column for each tag and field in the order of the type's declaration. Call `wait_completion()` before you read the samples back. How to declare the type is in the main ArcadeDB docs: see {arcadedb-docs-url}/concepts/timeseries[Time series]. The call exists on both packages. The rest of this section describes the fast path of the `arcadedb` (native) package. On `arcadedb-jvm`, a NumPy array for the timestamps or for a numeric column crosses as one buffer copy, and any other sequence converts value by value. On the `arcadedb` (native) package, a NumPy column crosses to the engine by its address, with no JSON text, when it is one of these: diff --git a/docs/modules/ROOT/pages/how-to/concurrency.adoc b/docs/modules/ROOT/pages/how-to/concurrency.adoc index 60282061..7d1d35a6 100644 --- a/docs/modules/ROOT/pages/how-to/concurrency.adoc +++ b/docs/modules/ROOT/pages/how-to/concurrency.adoc @@ -17,7 +17,7 @@ Every number on this page, except the ratios in <> to change it. -* *No Java objects, no JVM options.* `get_java_database`, `get_java_document` and the other `get_java_*` calls raise `NotOnNativeYet`. `start_jvm`, `jvm_kwargs` and the flags in <> do not exist. +* *No Java objects, no JVM options.* `get_java_database`, `get_java_document` and the other `get_java_*` calls raise `NotOnNativeYet`. `start_jvm` and the flags in <> do not exist, and `jvm_kwargs` is ignored with a logged warning. * *No console log on Windows by default.* The log goes to the file only. See xref:how-to/logs.adoc[Where the logs go] for the reason and for `ARCADEDB_LOG_CONSOLE`. * *Engine modules left out.* The gRPC and MongoDB wire protocols, Raft HA, Gremlin and the polyglot languages (JavaScript, Python as a query language) are not in the image. See <>. * *Platforms.* Intel Macs, Alpine Linux (musl) and Windows on ARM have no wheel. See <>. diff --git a/docs/modules/ROOT/pages/which-package.adoc b/docs/modules/ROOT/pages/which-package.adoc index 4688e525..8f6f5cff 100644 --- a/docs/modules/ROOT/pages/which-package.adoc +++ b/docs/modules/ROOT/pages/which-package.adoc @@ -93,7 +93,7 @@ The API is the same. The runtime is not. These are the differences you can plan | Available. | JVM options (`jvm_kwargs`, `start_jvm`, `-X` and `-D` flags) -| Do not exist. There is no JVM. +| `start_jvm` and the `-X` and `-D` flags do not exist, because there is no JVM. `jvm_kwargs` is accepted and ignored, with a logged warning. | Available. See xref:installation.adoc#jvm-options[JVM options]. | A few API calls diff --git a/src/arcadedb/core.py b/src/arcadedb/core.py index d24a780f..c1777814 100644 --- a/src/arcadedb/core.py +++ b/src/arcadedb/core.py @@ -1974,7 +1974,7 @@ def register_function( is the one exception to that: the statement is aborted and the caller gets a ``KeyboardInterrupt``, chained from the ``ArcadeDBError``, wherever the signal landed. A numeric argument costs about 30 bytes of Python memory per call that - JPype 1.7.1 does not give back (see the Python functions guide). + JPype 1.7.1 does not give back. The engine registers functions for the whole process, not for one database: every database in this process sees the function, and a function stays registered until diff --git a/src/arcadedb/graph_batch.py b/src/arcadedb/graph_batch.py index 938d179d..03495844 100644 --- a/src/arcadedb/graph_batch.py +++ b/src/arcadedb/graph_batch.py @@ -31,8 +31,9 @@ class GraphBatch: buffer fills, manage their own transactions. From engine 26.10.1 they refuse to run inside a transaction you opened (an ArcadeDBError) and leave it untouched; on 26.9.1 and earlier they commit it (ArcadeData/arcadedb#9242). - Call them outside your own transactions either way; see - docs/api/graph_batch.md. + Call them outside your own transactions either way. Commit sizes and what + an interrupted batch leaves behind are in + docs/modules/ROOT/pages/how-to/bulk-loads.adoc. """ _VALID_WAL_FLUSH_MODES = { diff --git a/src/arcadedb/results.py b/src/arcadedb/results.py index ece0e9db..ebb344d0 100644 --- a/src/arcadedb/results.py +++ b/src/arcadedb/results.py @@ -419,7 +419,7 @@ def to_list(self, convert_types: bool = True) -> List[Dict[str, Any]]: (RIDs, embedded documents, ``float[]``, ...) over as the engine's own object, so each value has the Python type it always had and no value crosses the JVM boundary on its own. That is within a few percent of - ``to_json_list()`` on a wide scan (see the performance guide). + ``to_json_list()`` on a wide scan. For large results, ``to_columns()``, ``to_dataframe()`` or ``to_arrow()`` move the data as columns and are faster still. From 1b7cdcb0243f72349eb973ac2c5dd3cb71972dcc Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:58:16 +0900 Subject: [PATCH 5/7] Docs: a result set is read once; server mode host, settings and plugins (#267) databases-and-queries.adoc, 'A result set is read once': a second pass over a set read to its end returns nothing; first(), one(), close() and leaving a with block close it, and a set closed before its end raises ArcadeDBError when read again; count() consumes it; statement errors can surface while rows are read; an unclosed set keeps the parallel scan threads parked for arcadedb.parallelScanAbandonedTimeout; to_list() when the rows are needed twice. Rests on results.py (class docstring, count, first, one, close, __enter__) and the same states in _native/database.py (NativeResultSet._readable, close, first, one, count). quickstart.adoc, server mode: host defaults to localhost and covers the HTTP server and every enabled wire protocol (server.py and _native/server.py, ArcadeNative.serverStart), http_port and mode are read by the bindings, any other key becomes arcadedb., Postgres/Redis/Bolt plugins are opt-in through server_plugins and are in both builds (native/pom.xml), the native server has no Studio. Co-Authored-By: Claude Opus 5.5 --- .../pages/how-to/databases-and-queries.adoc | 13 ++++++++++++ docs/modules/ROOT/pages/quickstart.adoc | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc index e8d1bf0f..a225015d 100644 --- a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc +++ b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc @@ -275,6 +275,19 @@ On the JVM build, `Document.to_dict()`, `Result.to_dict()` and `get_property_nam The control times `json.dumps` of rows already fetched, which uses no query and no JPype call. Its ratios on the four workloads are 0.996, 0.987, 0.993 and 1.000, and the same build measured twice gave ratios whose intervals contain 1. The figures come from one Linux x86_64 laptop with two cores for the process, Python 3.12, the median of 21 timed passes per run, and builds alternated in both orders. They cover the JVM build and these four workloads only, not wider rows, other value types, `to_list()`, or the native build. +[[result-set-single-pass]] +=== A result set is read once + +A result set is a lazy cursor, and you read it once. The engine computes the rows as you read them, not in `db.query(...)`. Both packages behave the same way: + +* A second pass over a set that you read to its end returns nothing. The set closes itself at its end, and it reads as empty afterwards. +* `first()` and `one()` close the set after they take their row. `close()` closes it too. A set closed before its end raises `ArcadeDBError` when you read it again, because the rows it had not returned are gone. Leaving a `with` block closes the set too. +* `count()` reads and counts the remaining rows, so the set is empty after it. +* An error in the statement can surface while you read the rows, not in `db.query(...)`. Every way of reading raises it as `ArcadeDBError`. +* If you stop reading early, close the set, or use it in a `with` block, so the engine releases its scan. An unclosed set keeps the threads of the engine's parallel scan parked for up to `arcadedb.parallelScanAbandonedTimeout` (10 minutes by default), and a few of them can stall the next query. Reading to the end also closes it. + +Use `to_list()` when you need the rows twice. It reads the whole result into a list of dicts, which you can loop over as often as you like. + === Record ids in rows A record id inside a row value (`SELECT @rid AS r`, a `LINK` property, a list of links such as `out('Knows').@rid`) comes back as an RID object, not as text. `str(rid)` is the text (`"#10:5"`), so is an f-string, and `get_rid()` always returns text or `None`. Two RIDs of the same record are equal and hash alike, so they work in sets and as dict keys, and RIDs sort by bucket, then position. `getBucketId()`, `getPosition()`, `toString()` and `isValid()` work on the object. It goes back in as a query parameter (`WHERE @rid = ?`, or a list passed by name) and to `db.lookup_by_rid()`, as itself or as its text. `to_json_list()` is the exception: a record id in its rows is the text (`"#10:5"`), on both builds, and so is a map key that is not a string, because a JSON object has no other kind of key. diff --git a/docs/modules/ROOT/pages/quickstart.adoc b/docs/modules/ROOT/pages/quickstart.adoc index e339a5e2..d3ce1e15 100644 --- a/docs/modules/ROOT/pages/quickstart.adoc +++ b/docs/modules/ROOT/pages/quickstart.adoc @@ -156,6 +156,27 @@ HTTP port: 2480 The server runs only while your Python process runs. See xref:index.adoc#embedded-vs-http[Embedded bindings or the HTTP driver] for when to run the regular ArcadeDB server (the official Docker image `arcadedata/arcadedb`) instead. +The server takes a `config` dictionary. These keys matter first: + +* `host` defaults to `localhost`, so only clients on the same machine connect. A client in a Docker container or on another machine needs `config={"host": "0.0.0.0"}` (all IPv4 interfaces, or `"::"` for IPv6). `host` applies to the HTTP server and to every wire protocol that you enable. A per-protocol key such as `postgres_host` overrides it for that protocol. +* `http_port` (default 2480) and `mode` (default `development`) are read by the bindings. +* Any other key becomes the engine setting `arcadedb.`. For example, `postgres_port` sets `arcadedb.postgres.port`. + +Both packages carry the Postgres, Redis and Bolt wire protocol plugins, and none starts unless you name it in `server_plugins`: + +[source,python] +---- +config = { + "host": "0.0.0.0", + "server_plugins": "Postgres:com.arcadedb.postgres.PostgresProtocolPlugin", + "postgres_port": 5432, # sets arcadedb.postgres.port +} +---- + +The class names of the other two are `com.arcadedb.redis.RedisProtocolPlugin` (port key `redis_port`) and `com.arcadedb.bolt.BoltProtocolPlugin` (port key `bolt_port`). + +The native server has no Studio web UI. + // REMOVE WHEN: the old arcadedb-embedded* shims are retired and two releases have shipped with the two-package layout allow-old-name In the `{jvm-pkg-name}` wheels from 26.11.1 the server also serves the Studio web UI, and `get_studio_url()` returns its address. The earlier 26.10.x wheels do not contain Studio: see the note in xref:installation.adoc#what-is-included[What is included]. From 08edf45d6bdd0b59161446417ce759aca9f1557d Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:58:16 +0900 Subject: [PATCH 6/7] Docs: Cypher vector indexes in 26.11.1; find_nearest distance; find_nearest_by_key (#267) vectors.adoc: openCypher creates (CREATE VECTOR INDEX) and searches (SEARCH ... IN (VECTOR INDEX ...)) vector indexes from 26.11.1, with links to the main manual's cypher-clauses page (anchors cypher-create-vector-index, cypher-search, checked on docs.arcadedb.com). The second element of a find_nearest tuple is a distance, lower is closer, for every similarity function (VectorIndex._sort_results; the native index reads the distance column of vectorNeighbors); find_nearest_by_key(key, k, ef_search, allowed_rids) uses the stored vector of the record whose id property equals key (vector.py _lookup_query_vector_by_key). Co-Authored-By: Claude Opus 5.5 --- docs/modules/ROOT/pages/how-to/vectors.adoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/modules/ROOT/pages/how-to/vectors.adoc b/docs/modules/ROOT/pages/how-to/vectors.adoc index b36e0ee5..84beb2db 100644 --- a/docs/modules/ROOT/pages/how-to/vectors.adoc +++ b/docs/modules/ROOT/pages/how-to/vectors.adoc @@ -159,6 +159,8 @@ print([row["name"] for row in rows]) ['python-ish', 'python'] ---- +On ArcadeDB 26.11.1 and later, openCypher also creates and searches vector indexes: `CREATE VECTOR INDEX ... FOR (n:Label) ON n.property OPTIONS {...}` creates an `LSM_VECTOR` index, and `SEARCH ... IN (VECTOR INDEX ...)` queries it. The syntax is in the main manual: {arcadedb-docs-url}/reference/cypher/cypher-clauses#cypher-create-vector-index[CREATE VECTOR INDEX] and {arcadedb-docs-url}/reference/cypher/cypher-clauses#cypher-search[SEARCH]. + [[find-nearest]] === find_nearest @@ -166,6 +168,10 @@ print([row["name"] for row in rows]) With `ef_search=None` the search width comes from the index's `efSearch` metadata when it is set, otherwise from the engine: `max(k, 100)` candidates on an index of under 10,000 vectors and `max(2k, 100)` from 10,000 vectors on. Before ArcadeDB 26.11.1 the larger indexes searched `max(2k, 20)`, which gave recall@10 0.929 on SIFT 100k against 0.998 now (https://github.com/ArcadeData/arcadedb/issues/10704[ArcadeData/arcadedb#10704]), so a search on a large index is more accurate and slower than on earlier releases. Pass a smaller `ef_search` to trade recall for speed, or a larger one for more recall. +The second element of each tuple is a distance, and lower is closer, for every similarity function (`COSINE`, `EUCLIDEAN` and `DOT_PRODUCT`) and on both packages. `find_nearest` sorts the hits by it, ascending. It is the `distance` column of `vectorNeighbors`, not the `score` of the Cypher procedure above, where higher is closer. + +`VectorIndex.find_nearest_by_key(key, k=10, ef_search=None, allowed_rids=None)` searches with the stored vector of an existing record as the query, so you do not fetch the vector first. `key` is the value of the index's id property for that record. `get_metadata()["id_property"]` names that property, and `create_vector_index(..., id_property=...)` sets it. The other arguments and the result are those of `find_nearest`. The call raises `ArcadeDBError` when no record has that key, or when the record's vector property is null. + On the native package the query vector crosses as one `float32` buffer whatever its form (a list, a `float64` array, or a `float32` array), so you do not need to convert it first. A vector index stores `float32`, so the numbers are rounded the same way as before. The table gives the median `find_nearest` latency with this change over the median without it (95% interval, below 1 is faster), measured on the native wheel `26.11.1.dev0` on one Linux x86_64 laptop with 11 to 12 clean pairs per figure. The wheel built before this change was the baseline. [cols="3,2,2",options="header"] From 1bf35c1849921e26829ff002c2973676785f1e93 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 14:59:50 +0900 Subject: [PATCH 7/7] Docs: one install command per package; README CI sentence matches the matrices (#267) The install table had each uv add line twice (left over from the pip-to-uv replacement). The README said CI tests 3.11 to 3.14 on every platform for main, nightly and releases; per scripts/select_native_matrix.py and select_matrix.py, main and nightly run native 3.12 and 3.14 on every platform (pyver subset on 3.11, 3.13, 3.15) and the JVM build 3.11 to 3.14 on Linux x86-64 plus 3.11 and 3.14 elsewhere. A release runs 3.11 to 3.14 on all four platforms for both builds once the JVM release matrix is full (ci/jvm-full-matrix-on-release, to merge before this PR). Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docs/modules/ROOT/pages/installation.adoc | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a5169c05..baaaccf1 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ uv add arcadedb-jvm # JVM, for heavy long-running loads > Old names: `arcadedb-jvm` was formerly `arcadedb-embedded`, and `arcadedb` was formerly `arcadedb-embedded-native` (archived on PyPI). A last `arcadedb-embedded` release will be a deprecation shim that installs `arcadedb`, the native build. -- Python: `arcadedb` (native) is tested on 3.11 to 3.14 and also installs on 3.15 (`requires-python` is `>=3.11` with no upper bound; the CI runs a subset of the tests on 3.15). `arcadedb-jvm` needs 3.11 to 3.14 (`requires-python` is `>=3.11,<3.15`; its dependency JPype has no 3.15 wheel yet). CI builds and tests the four versions 3.11 to 3.14 on every platform below for `main`, every night and each release. A pull request runs a smaller selection. +- Python: `arcadedb` (native) is tested on 3.11 to 3.14 and also installs on 3.15 (`requires-python` is `>=3.11` with no upper bound; the CI runs a subset of the tests on 3.15). `arcadedb-jvm` needs 3.11 to 3.14 (`requires-python` is `>=3.11,<3.15`; its dependency JPype has no 3.15 wheel yet). Each release builds and tests both packages on Python 3.11 to 3.14 on all four platforms below. Between releases, `main` and the nightly run test `arcadedb` on 3.12 and 3.14 on every platform (with a short subset on 3.11, 3.13 and 3.15), and `arcadedb-jvm` on 3.11 to 3.14 on Linux x86-64 and on 3.11 and 3.14 on the other three. A pull request runs a smaller selection. - Linux x86_64 and ARM64, macOS on Apple Silicon, Windows x86_64, for both packages. The Windows wheel of `arcadedb` is the newest. - Import name: `arcadedb`. Check which package you have with `python -c "import arcadedb; print(arcadedb.__backend__)"`. It prints `native` or `jvm`. - Optional extras: `arrow`, `pandas`, `polars`, `vector` for `arcadedb-jvm`; `numpy`, `arrow`, `pandas`, `polars` for `arcadedb`. diff --git a/docs/modules/ROOT/pages/installation.adoc b/docs/modules/ROOT/pages/installation.adoc index 06f259e0..45e29999 100644 --- a/docs/modules/ROOT/pages/installation.adoc +++ b/docs/modules/ROOT/pages/installation.adoc @@ -13,13 +13,11 @@ Pick one of the two packages. Both import as `{import-name}`. If you are not sur | Package | Install | Pick this if | `{native-pkg-name}` (native, the default) -| `uv add {native-pkg-name}` + -`uv add {native-pkg-name}` +| `uv add {native-pkg-name}` | You write scripts, notebooks, tests, CLIs, serverless functions or agents. You want a fast start and a small footprint, and you need no JVM. | `{jvm-pkg-name}` (JVM) -| `uv add {jvm-pkg-name}` + -`uv add {jvm-pkg-name}` +| `uv add {jvm-pkg-name}` | Your load is heavy and long-running, so the JIT compiler pays off. Or you need the Java-object APIs, JVM options, `jvm_kwargs` or `start_jvm`. |===