Repository navigation
Conversation
Tenant pre-filtered vector.neighbors fused with a full-text match by vector.fuse (RRF), then graph hops kept inside the tenant. The where form needs 26.11.1; the filter form runs on 26.10.2. Test runs both forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What
Example 29,
examples/29_hybrid_memory_retrieval.py(27 and 28 are taken by #30 and #29, so 29 is the next free number; the three README entries will conflict in the same spot and need a trivial merge). For an agent-memory or GraphRAG builder who today glues a vector store, a keyword index and a graph store together: the whole retrieval is one SQL statement, and everything goes throughdb.query/db.command(no new Python wrapper). Synthetic data only: six tenants, 900Factvertices (text plus a fixed-seed numpy embedding), 96Entityvertices,MENTIONSandRELATESedges.The point is the tenant pre-filter. A global top-k followed by a tenant test returns fewer rows than asked, or none, whenever the nearest neighbours belong to another tenant (Cypher
db.index.vector.queryNodes(index, k, vector)has no filter, soWHERE node.tenant = ...runs on its k results: ArcadeData/arcadedb#10685).vector.neighborstakes the restriction as an option, so the index search only considers the tenant's vectors. The example runs the failing post-filter once for contrast (0 rows) and then the pre-filtered forms.The statement
Setup is DDL only:
CREATE INDEX ON Fact (embedding) LSM_VECTOR METADATA {...}andCREATE INDEX ON Fact (text) FULL_TEXT. One statement was enough, nothing is split. The[tenant = :tenant]guard on the hops matters: the memory holds one strayRELATESedge from an acme entity to a globex entity, and without the guard the hops of the acme facts also return globex facts (the example prints this, and a test asserts it).related_fidsis a sample of 3 in no particular order, not a ranking.What needs 26.11.1
Only the
whereoption ofvector.neighbors(ArcadeData/arcadedb#10169, #10185); it is the default--form.--form filterreplaces it withfilter: (SELECT @rid FROM Fact WHERE tenant = :tenant)and runs on 26.10.2 as well (checked on the publishedarcadedb-embedded==26.10.2.post1:vector.fuse,SEARCH_INDEXas a fuse source and the rest of the statement work there;whereis rejected there withUnknown option 'where' for function 'vector.neighbors'). The CI examples job builds againstarcadedata/arcadedb:latest, so the default form runs there; a release build pinned to 26.10.2 would need--form filter.At the index defaults the search beam narrows from 10,000 vectors up (ArcadeData/arcadedb#10704); this memory has 900, so the defaults are fine, and the docstring says to pass
efSearchin the options map for a larger one.Measured
python 29_hybrid_memory_retrieval.pyon the 26.11.1 build (arcadedb_embedded 26.11.1.dev0, this branch's Python over the 26.11.1-SNAPSHOT jars), copied from the run:With
--form filteronarcadedb-embedded==26.10.2.post1the output after the engine line is identical, row for row.tests/test_example29_hybrid_memory.py(5 tests: both forms stay in the tenant, the post-filter loses the rows, the forms agree, the hop guard matters) passes on the 26.11.1 build. black,isort --profile blackand bandit (the CI invocations) are clean. Nothing here is timed and no performance claim is made.🤖 Generated with Claude Code