Skip to content

Native lookup_by_key raises without an index, as the JVM build does - #96

Merged
tae898 merged 2 commits into
mainfrom
lookup-by-key-unindexed
Oct 26, 2026
Merged

tae898 merged 2 commits into
mainfrom
lookup-by-key-unindexed

Conversation

@tae898

@tae898 tae898 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Database.lookup_by_key on an unindexed key scanned the type on the native wheel (arcadedb) and raised on the JVM wheel (arcadedb-jvm). Measured on the CI wheels of main 662851c (26.11.1.dev0), same script, same database.

Before, per backend (type D with an integer k, no index; then an index on k; a composite index on (a, b); a FULL_TEXT index on t):

call native (before) JVM
["k"], no index, hit record raises No index has been created on type 'D' properties [k]
["k"], no index, miss None raises (same)
["k"], UNIQUE index, hit / miss record / None record / None
["a"] and ["b", "a"] on index (a, b) record raises
["t"] on a FULL_TEXT index None raises No key index has been created ... FULL_TEXT
unknown type raises was not found raises was not found

The contract is the JVM one, because that is the engine: LocalDatabase.lookupByKey throws IllegalArgumentException unless the type or a supertype has a ready LSM_TREE or HASH index on exactly that property list (getPolymorphicIndexByProperties, isExactKeyLookup). The docstring already says the keys "must be indexed" and the user docs do not mention the method, so no document promises a scan.

The fix: the native lookup_by_key no longer runs a SELECT ... WHERE. A new entry point of the native library, arcadedb_n_lookup in NativeExtras.java, calls the engine's own Database.lookupByKey(type, keys, values) inside the image and replies with the first hit (none or one row) in the shape of a statement reply, so Python reads it with the reader of a SELECT and wraps it as a Document, Vertex or Edge. The engine's exceptions become the error reply and so the same ArcadeDBError text as on the JVM build, with no extra query on the hot path (one crossing, as before). This is the case where the bindings wrap a Java call instead of a statement: no SQL or Cypher statement raises when no index serves the keys. The docstring of the JVM method gains a Raises section and the exact-index rule. vector.py already catches ArcadeDBError from this call and falls back to a query, so find_nearest by key behaves as before.

Tests: tests/test_lookup_by_key_index.py (15 tests) is shared, no conftest entry. Unindexed hit and miss raise, UNIQUE/NOTUNIQUE/UNIQUE_HASH/NOTUNIQUE_HASH work once created, a dropped index raises again, an index on another property does not serve, a composite index serves exactly its list (prefix and reordered raise), FULL_TEXT raises, an index on a supertype serves subtypes two levels down but a subtype index does not serve the supertype, vertex and edge types follow the same rule, an unknown type names the type. On the CI wheels of main, 14 of the 15 fail on the native wheel and all 15 pass on the JVM wheel (the 15th is the one that already holds). With this change: JVM 15 of 15 (local, the core.py docstring is the only change there). Native: not run yet at the time of writing. Test native in CI builds the image from this commit, but it skips a draft PR, so it starts when the PR is marked ready for review. A local image build from this commit (native/scripts/build-image.sh through quiet_build.sh, then the new tests and the existing lookup tests on the native backend) is queued; its result will be added here. The Java compiles (GraalVM CE 25.4.4.1.1, mvn -o compile in native/).

Separate observation, not part of this change: on the native wheel SELECT FROM schema:indexes reports typeName D for the bucket index of a subtype Sub of D (Sub_0_...), where the JVM wheel reports Sub. Same engine rows, different typeName; worth its own look.

black and isort are clean, scripts/check_no_embedded_name.py --root . passes.

🤖 Generated with Claude Code

Database.lookup_by_key on the native build ran a SELECT ... WHERE, which scans the type when no index serves it, so the same call returned a record on the arcadedb wheel and raised on the arcadedb-jvm wheel (the engine's lookupByKey needs an index and throws IllegalArgumentException). The native build now checks schema:indexes first, with the engine's rule: an LSM_TREE or HASH index on exactly the given properties, in the index's order, on the type or a supertype. Otherwise it raises ArcadeDBError with the engine's message (No index has been created on type ... properties [...], or No key index ... for a FULL_TEXT index). An unknown type still raises the not-found error of the SELECT. The docstring of the JVM method now states the contract. tests/test_lookup_by_key_index.py runs on both backends.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tae898
tae898 marked this pull request as draft October 26, 2026 12:04
The first version checked schema:indexes before the SELECT, one more query on a hot point-lookup path. The native glue now has an entry point, arcadedb_n_lookup, that calls Database.lookupByKey(type, keys, values) in the image and replies with the first hit as the rows of a statement reply, so Python reads it as it reads a SELECT. The engine's IllegalArgumentException (no LSM_TREE or HASH index on exactly those properties of the type or a supertype) and SchemaException (unknown type) are the error reply, and become the ArcadeDBError that the JVM build raises. No statement has that raise-on-unindexed rule, so this is the case where a Java call is wrapped. _require_key_index and its two schema queries are gone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tae898
tae898 marked this pull request as ready for review October 26, 2026 12:09
@tae898
tae898 merged commit 5d69e04 into main Oct 26, 2026
45 checks passed
@tae898
tae898 deleted the lookup-by-key-unindexed branch October 26, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant