Repository navigation
Native find_nearest: a hit loads its vector on first use, not with every search (draft, stacked on #523) - #533
Closed
tae898 wants to merge 1 commit into
Conversation
…ery search The statement behind find_nearest, SELECT expand(vectorNeighbors(..)), sent every hit's whole record twice, nested in the record column and flattened into the row, vector included. The search now drops the indexed vector (and the record column) in SQL, takes rid, type and distance from the row, and builds the hit without the vector. The record loads it once, by RID, when the vector is read or the whole record is needed (get, has_property, to_dict, get_property_names, modify, save). Values, property order and types stay those of lookup_by_rid. A property of the record called record or distance was hidden by the search row's own columns, and a type with a property called record made find_nearest fail with an AttributeError. Such a property now loads with the vector. Tests: test_vector_hit_records.py runs on both wheels (hits equal lookup_by_rid for a four-bucket type with a subtype and properties called record and distance, every first-access order, modify and save, ef_search, allowed_rids, find_nearest_by_key); test_native_vector_hit_lazy.py counts what is shipped and looked up. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 25, 2026
Author
|
Closing here: the bindings moved to ArcadeData/arcadedb-python. This change has no counterpart there yet and no queued A/B. If it is wanted, it goes there as its own PR: it is now a line in the tracking issue ArcadeData/arcadedb-python#19. The branch stays. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Native
find_nearestno longer ships each hit's vector. Part of backlog row B-01 (the native half, N-08); the JVM half is a separate PR. Stacked on #523 (perf/native-find-nearest-ndarray), which edits the same function (the query side); this PR is the result side. Merge #523 first, or retarget this PR tonative-previewonce it is in.The statement behind it,
SELECT expand(vectorNeighbors(..)), returned every hit's whole record twice: nested in therecordcolumn and flattened into the row, vector included both times. The search is nowSELECT *, !, !recordFROM (SELECT expand(vectorNeighbors(?, ?, ?))), with a fourth parameter for the efSearch/filter options as before. The hit is built from the row's@rid,@typeanddistanceand the remaining properties, without the vector. Type and kind come from@typeof the row (a hit of a subtype keeps its own type) and_kind_of.The documented hit contract is unchanged: a hit is
(record, distance)with a full record. The record fills itself once, by onelookup_by_rid, the first time the vector is read (get,has_property) or the whole record is needed (to_dict,get_property_names,modify,save), so values, types and property order are those oflookup_by_rid. A search whose hits never touch the vector ships and looks up no vector. A hit deleted since the search raisesArcadeDBErroron that first read.docs/api/vector.mdsays so.Side fix: the engine's search row hides a record property called
recordordistancebehind its own columns (ScoredRecordRow), and on native a type with a property calledrecordmadefind_nearestfail withAttributeError: 'str' object has no attribute 'get'(the new shared test failed that way before the change). Such properties are now held back with the vector and load with it, anddistanceof the row is always the search score.Untimed counts (dim 128, k 10, four buckets, 300 records): floats in the returned rows 2570 before, 10 after (the distances); lookups after reading
idandtextof all 10 hits 0, after also reading the vector 10 (one per hit). No timing was taken; the microseconds belong to a study.Local checks (native image: a copy of
native-lib/nnext, Python only, no rebuild):test_vector_hit_records.py(8 tests, both wheels: hits equallookup_by_ridfor a four-bucket type with a subtype and properties calledrecordanddistance, every first-access order, modify and save,ef_search,allowed_rids,find_nearest_by_key) andtest_native_vector_hit_lazy.py(3 tests, native only: nothing shipped, one lookup per record, closed database). Written first: the shared file failed 8 of 8 on unchanged native. With the stack: 97 passed, 24 skipped (the usual NOTYET skips) overtest_native_find_nearest_params.py,test_vector.py,test_vector_second_pass.py,test_vector_params_verification.py,test_vector_hit_records.py,test_native_vector_hit_lazy.pyandtest_core.py; the shared tests also pass on the JVM wheel (55 passed withtest_vector.py). A fixed-seed dump of 120find_nearestcalls (rid, repr of score, k 1 to 50, 1, 4 and 8 buckets) is identical before and after, as is the shape of a hit (type, property names and order, vector length). black, isort and the pre-commit hooks pass.Overlap with the open native PRs, by
git merge-treeof this branch against each head: #523 conflicts in_native/schema_objects.pyand is why this PR is stacked on it. #514, #520, #521, #525, #526 and #531 are clean. #522 and #529 each add acollect_ignore.appendline next to mine intests/conftest.py, a trivial adjacent-line conflict for whichever merges second. No native-image rebuild is needed.🤖 Generated with Claude Code