Skip to content

Add semantic (vector) search support via Solr's text-to-vector module - #3707

Open
luis100 wants to merge 2 commits into
developmentfrom
feature/solr-semantic-search
Open

Add semantic (vector) search support via Solr's text-to-vector module#3707
luis100 wants to merge 2 commits into
developmentfrom
feature/solr-semantic-search

Conversation

@luis100

@luis100 luis100 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds a knn_vector Solr field type and query-time vectorization via Solr's language-models module, so free-text queries can be matched by meaning rather than only by keyword overlap.
  • New TextToVectorFilterParameter (field, query, model, topK) - composes with every other FilterParameter type via the existing AND/OR/nesting mechanism.
  • AIP/File/Representation collections each get an embedding_vector field (knn_vector, not stored) and a vectorized_b flag (default false) that an external enrichment service polls to find documents still awaiting vectorization. RODA itself never populates embedding_vector - the second-pass enrichment is a deliberately separate service, since synchronous embedding calls on every index write would be far too slow for ingest.
  • SolrBootstrapUtils registers the embedding model with Solr's text-to-vector-model-store from new core.index.embedding.* config (enabled flag, base URL, model name, dimensions, Solr model name, default top-K) - an OpenAI-compatible /embeddings endpoint contract, so any external enrichment service stays consistent with query-time vectorization.
  • appendTextToVector passes the query text through the quoted v= local param rather than as trailing text after the local params block, fixing a bug where any multi-word query broke with 400 undefined field _text_ (trailing text is only unambiguous for a single word once combined with other filters into one larger boolean query string).

Test plan

  • Compiles cleanly (mvn -pl roda-common/roda-common-data,roda-core/roda-core -am install -DskipTests)
  • Verified live against a real Solr instance + a real external embedding server: model registration, knn_vector field, and TextToVectorFilterParameter all confirmed working, including a 12-word natural-language query correctly ranking the semantically relevant document first (proving the multi-word fix)
  • SolrUtilsTest unit tests included for the new filter parameter (not re-run against the full TestNG integration harness in this session - relying on the live end-to-end verification above plus a clean compile)

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Adds a knn_vector Solr field type and query-time vectorization via
Solr's language-models module, so free-text queries can be matched by
meaning rather than only by keyword overlap.

- New TextToVectorFilterParameter FilterParameter subtype (field, query,
  model, topK) - vectorizes the query at search time via Solr's
  {!knn_text_to_vector} query parser and restricts results to the topK
  nearest neighbours of a knn_vector field. Composes with every other
  filter type the same way (AND/OR/nesting).
- AIP/File/Representation collections each get an embedding_vector
  field (knn_vector, not stored - vectors are large and only useful for
  the KNN search itself) and a vectorized_b flag (boolean, default
  false) that an external enrichment service can poll
  (fq=vectorized_b:false) to find documents still awaiting
  vectorization. RODA itself never populates embedding_vector - the
  enrichment pass is a separate service by design, since running
  embedding calls synchronously on every index write would be far too
  slow for ingest.
- SolrBootstrapUtils registers the embedding model with Solr's
  text-to-vector-model-store from new core.index.embedding.* config
  (enabled flag, base URL, model name, dimensions, Solr model name,
  default top-K) - an OpenAI-compatible /embeddings endpoint, the same
  contract any external enrichment service should call so both sides of
  the vector space stay consistent.
- appendTextToVector passes the query text through the quoted `v=`
  local param rather than as trailing text after the local params
  block. Trailing text is only unambiguous for a single word - a
  multi-word query gets combined with other filters into one larger
  boolean query string, and the outer parser can split on the
  whitespace and try to resolve the extra words against Solr's default
  search field, which RODA doesn't define. Verified live against a real
  embedding server with a 12-word natural-language query correctly
  ranking the semantically relevant document first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01263fsB7QYevDFgwAY8okcq
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement labels Jul 29, 2026
Without SOLR_MODULES: language-models, Solr never loads the
knn_text_to_vector query parser this PR's TextToVectorFilterParameter
depends on - semantic search would silently fail with an "unknown
query parser" error in both the dev and production-like standalone
deploys. Also adds a minimal docker-compose-vector-test.yaml (just
ZooKeeper + Solr + PostgreSQL) for quickly verifying the embedding
model registration and schema wiring without the full dev stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01263fsB7QYevDFgwAY8okcq
@luis100
luis100 requested a review from hmiguim July 29, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants