Neo4jVectorStore: filtered similarity search bypasses the vector index — causes brute-force scan at scale
Bug description
When users pass a FilterExpression, the filter runs as a WHERE clause AFTER db.index.vector.queryNodes() fetches candidates. This means:
- Vector index fetches top K by similarity
- Filter is applied post-fetch in Cypher
- Final results may be fewer than requested topK
- At large scale this causes full collection scans
Real User Impact
A user with 10M documents filtering by category='science' (where 1% match) gets:
- topK=10 requested
- 10 candidates fetched from index
- ~9 filtered out post-fetch
- returns only ~1 document instead of 10
They have to set topK=1000 to compensate — fetching 1000 vectors just to get 10 results.
Root cause
The current query uses the deprecated procedure: CALL db.index.vector.queryNodes(...) which does not support in-index filtering.
Fix
Neo4j 2026.02 GA'd the SEARCH clause which runs
filters INSIDE the index:
MATCH (node:Label) SEARCH node IN ( VECTOR INDEX $indexName FOR $embeddingValue WHERE node.category = 'science' ← inside index, fast LIMIT $topK ) SCORE AS score WHERE score >= $threshold RETURN node, score
Side Effect
db.index.vector.queryNodes is also deprecated as of Neo4j 2026.04 — so this fix addresses both performance and deprecation in one change.
Source: spring-projects/spring-ai