Skip to main content
Feature Search stage showing multi-vector semantic search with fusion
The Feature Search stage is the primary search stage for retrieval pipelines. It performs vector similarity search across one or more embedding features, supporting single-modal, multimodal, and hybrid search patterns. Results from multiple searches are fused using configurable strategies (RRF, DBSF, weighted, max, or learned).
Stage Category: FILTER (Retrieves documents)Transformation: 0 documents → N documents (retrieves from collection based on vector similarity)

When to Use

When NOT to Use

Core Concepts

Feature URIs

Feature URIs identify which embedding index to search. They follow the pattern:
Examples:
  • mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding - Multimodal text/image/video embeddings
  • mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1 - Text-only embeddings
  • mixpeek://image_extractor@v1/google_siglip_base_v1 - Image embeddings
  • mixpeek://multimodal_extractor@v1/multilingual_e5_large_instruct_v1 - Speech/transcript embeddings (from video/audio)

Fusion Strategies

When searching multiple features, results are combined using fusion:

Learned Fusion Configuration

When fusion is set to "learned", you can provide a learning_config object to control how the bandit adapts. See Auto-Tune for a full walkthrough.

learning_config Fields

See Auto-Tune for the full concept overview, Reward Signals for reward map customization, and Rollout & Safety for traffic splitting and kill switch details.

Parameters

Search Object Parameters

Each item in the searches array supports:

Query Input Modes

The query field on each search object accepts either a plain string (shorthand for text mode) or an object with an explicit input_mode: Text — embed a string and search:
Content — fetch a URL and embed it:
Vector — use a pre-computed embedding directly (no inference at query time):
Multi-content — embed multiple files together in one API call. Only valid when the feature_uri points to an extractor whose vector index has supports_multi_query=True (currently: gemini_multifile_extractor). Attempting this with any other feature URI returns a 400 error.
Each item in values is auto-detected: URLs (http://, https://, s3://) are fetched and embedded as files; all other strings are embedded as text. All items are passed to the underlying model in one call, producing a single query vector that mirrors how objects were indexed. Set lexical: true on a search to run keyword/BM25 matching instead of vector similarity. The query text is matched against the namespace’s full-text index — it is not embedded into a vector. BM25 catches exact tokens that dense embeddings routinely miss: brand names, SKUs, prices like $9.99, promo codes, error strings, and CTAs.
Searching only one field (e.g. OCR text). BM25 matches across all text-indexed string fields — it can’t be scoped to a single field like ocr_text. To make one field independently searchable, give it its own dense index by running a text_extractor over it (map the extractor’s input to ocr_text), then feature_search that feature URI directly. For coarse exact-substring filtering on a single field, an attribute_filter with the contains operator works but is not relevance-ranked.
The real power is hybrid retrieval — fuse a dense (vector) search with a lexical (BM25) search under rrf so semantic recall and exact-keyword precision reinforce each other:
Dense + Lexical Hybrid (RRF)
Use rrf fusion for dense+lexical hybrid — it ranks by position, so it is immune to the score-scale mismatch between cosine similarity and BM25. Avoid weighted/max here unless you have a specific reason.

Query Preprocessing

When searching with large files (videos, PDFs, long documents) as input, query_preprocessing decomposes the file into chunks using the same extractor pipeline that indexed your data, runs parallel searches for each chunk, and fuses the results. This is ingestion applied to the query — same decomposition and embedding, but vectors are used for search instead of storage.
params uses the extractor’s own parameter schema. Whatever parameters the extractor accepts during ingestion (e.g. split_method, time_split_interval for video; chunk_size, chunk_overlap for text) are the same parameters you pass here. There is no separate preprocessing-specific schema — the extractor drives the decomposition exactly as it would during collection processing. Refer to the extractor’s own documentation for valid parameter names.
You can also set query_preprocessing at the stage level (on parameters) to apply it to all searches as a default. Per-search settings override the stage default.
Aggregation strategies:

Configuration Examples

Query Preprocessing Examples

Search with a large video — decompose it into 10-second segments, search each, and fuse:
Preprocessing uses the same extractor pipeline that indexed your data. The params accept the same fields you configured on your collection’s feature extractor (e.g., split_method, chunk_size). If you don’t specify params, extractor defaults are used.
The response includes preprocessing metadata showing what happened:
Each result also includes query_chunks showing which parts of your query matched:

Grouping (Decompose/Recompose)

When documents are decomposed into chunks (e.g., video frames, document pages), use group_by to recompose results by parent:
Use cases:
  • Video search: Group frames by video, return top 3 frames per video
  • Document search: Group chunks by document, return best chunks per doc
  • Product search: Group variants by product family
Get counts of results by field values for building filter UIs:
Response includes:

Filter Syntax

Filtered fields must have payload indexes on your namespace. Without indexes, filtering is slow and the response includes warnings about unindexed fields.
Pre-filters use boolean logic with AND/OR/NOT:

Supported Operators

Performance

For best performance, use pre-filters to reduce the search space. Filtering at the vector index level is much faster than post-filtering in later stages.

Common Pipeline Patterns

Basic Search + Rerank

Multimodal Search + Filter + Limit

Video Search with Frame Grouping

E-commerce Search with Facets

Output Schema

Each result includes: Example output:

Comparison: feature_search vs attribute_filter

Error Handling

The following is a complete working example of creating a retriever that uses the feature_search stage, then executing it. Pay close attention to the field names — several are easy to confuse.
Common mistakes:
  • Use collection_identifiers (not collection_ids) in the retriever body.
  • input_schema is a flat map keyed by field name ({"query": {"type": "text"}}) — do not wrap it in a JSON Schema object ({"properties": {...}, "type": "object"}).
  • Use type: "text" (not "string") in input_schema values.
  • stage_type at the outer level must be "filter" — passing stage_type: "feature_search" is rejected (feature_search is a stage_id, not a stage_type).
  • stage_id: "feature_search" lives inside the config object, not at the outer stage_id.
  • Inside each search, the query value uses {"input_mode": "text", "value": "..."} — the value key, not a bare text key.
  • final_top_k lives inside config.parameters, not at the top level.
  • If a feature_uri is wrong, the error lists the available_feature_uris for your target collections — copy the exact URI (e.g. multilingual_e5_large_instruct_v1, not embedding).

Step 1 — Create the Retriever

Step 2 — Execute the Retriever

Finding the Right feature_uri

The feature_uri must match an embedding index that exists in your namespace. To discover available feature URIs, list the vector indexes in a collection:
Each item in the vector_indexes array has a feature_uri field — use that value directly in your retriever stage.

Embedding Task Conditioning

Feature search automatically applies task-aware embedding conditioning to instruction-aware models (E5, Gemini) at query time. This means query embeddings are optimized for asymmetric retrieval without any configuration. How it works:
  • Index time: Extractors embed documents with retrieval_document task (configurable via embedding_task on the extractor — see Text Extractor)
  • Query time: Feature search automatically uses retrieval_query task for all query embeddings
This asymmetric pairing (document vs. query) improves retrieval quality by ~10% for instruction-aware models like E5-Large. The embedding_task used is included in the stage response metadata:
Task-aware models: