This section documents search capabilities in Spice SQL, including vector search, full-text search, and lexical filtering methods. These features help retrieve relevant data using semantic similarity, keyword matching, and pattern-based filtering.
vector_search) (opens in a new tab)
text_search) (opens in a new tab)
rrf) (opens in a new tab)
rerank) (opens in a new tab)vector_search)Vector search retrieves records by semantic similarity using embeddings. It is ideal for finding related content even when exact keywords differ.
table: Dataset name (required)query: Search text, or an array of strings for multi-query (opens in a new tab) search (required)column: Column name (optional if only one embedding column; required when the table has multiple embedded columns)limit: Maximum results (optional). When omitted, the engine-defined maximum is used.include_score: Include relevance scores (optional, default TRUE)distance_metric: Similarity metric used to rank candidate vectors (optional, named argument). Supported values: 'cosine' (default) and 'l2' (negated Euclidean distance). 'dot' is parsed but not yet wired through the scan path.rank_weight: Per-query ranking weight (optional, named argument). Only meaningful when vector_search is passed as a subquery to rrf (opens in a new tab).WHERE predicates on base table columns (e.g., created_at, product_category) are pushed down as pre-filters — they are applied before the similarity ranking, so only matching rows are scored and returned. This means results reflect the top-K within the filtered set, not the top-K of the entire table filtered afterward.
Predicates on computed columns like score are applied as post-filters after ranking.
To override the similarity metric, pass distance_metric as a named argument:
See Vector-Based Search (opens in a new tab) for configuration and advanced usage.
When the target column is a multi-vector column (opens in a new tab), vector_search also accepts an array of query strings. Each query is embedded independently and the per-row score is Σ_q max_e cos(q, e) — ColBERT-style late interaction. Passing an array to a scalar or chunked column returns an error. At most 32 query strings are accepted per call.
text_search)Full-text search uses BM25 scoring to retrieve records matching keywords in indexed columns.
table: Dataset name (required)query: Keyword or phrase (required)column: Column to search (optional if the table has a single full-text index; required when multiple columns are indexed)limit: Maximum results (optional). Defaults to 1000, which is the maximum supported.include_score: Include relevance scores (optional, default TRUE)rank_weight: Per-query ranking weight (optional, named argument). Only meaningful when text_search is passed as a subquery to rrf (opens in a new tab).By default, text_search retrieves up to 1000 results. To request fewer, specify a smaller limit.
With the built-in Tantivy engine, WHERE predicates on columns carried in the full-text index are pushed into the index scan as pre-filters — they are applied before the top-K limit, so the results are the top-K within the filtered set rather than the filtered remainder of an unfiltered top-K. A predicate the index cannot apply is left to Spice's query engine above the scan, and one the index applies only approximately is re-checked there, so results are the same either way; only how many rows survive the limit changes.
Filterable columns are the dataset's primary key (or full_text_search.row_id (opens in a new tab)) and any column declared with metadata.vectors (opens in a new tab), whose values are carried into the index alongside the searched text. Columns of a type the index cannot represent — dates and timestamps — are skipped, with a warning logged at startup naming the column.
Predicates that push down: =, !=, <, <=, >, >=, BETWEEN, IN, a prefix LIKE 'x%' on a string column, and AND / OR / NOT combinations of them. Predicates that do not, and are applied above the scan instead: anything on the searched text column itself (it is tokenized), on a floating-point column, or on a binary column; a case-insensitive or negated LIKE; an IN list containing NULL; and any comparison whose operands are not a column and a literal.
The Tantivy warm tier used with engine: elasticsearch (opens in a new tab) is built without those extra columns — its schema is the primary key and _score alone — so only primary-key predicates push into it.
See Full-Text Search (opens in a new tab) for configuration and details.
rrf)Reciprocal Rank Fusion (RRF) combines results from multiple search queries to improve relevance by merging rankings from different search methods. Advanced features include per-query ranking weights, recency boosting, and flexible decay functions.
rrf is variadic and takes two or more search UDTF calls as arguments. Named parameters provide advanced control over ranking, recency, and fusion behavior.
:::info
The rrf function automatically adds a fused_score column to the result set, which contains the combined relevance score from all input search queries. Results are sorted by fused_score DESC by default when no explicit ORDER BY clause is specified.
:::
Arguments:
Note that rank_weight is specified as the last argument to either a text_search or vector_search UDTF call (as shown above). All other arguments can be specified in any order after the search calls (within an rrf invocation).
| Parameter | Type | Required | Description |
|---|---|---|---|
query_1 | Search UDTF call | Yes | First search query (e.g., vector_search, text_search) |
query_2 | Search UDTF call | Yes | Second search query. rrf requires at least two subqueries. |
... | Search UDTF call | No | Additional search queries (variadic) |
join_key | String | No | Column name to use for joining subquery results. If omitted, the primary key is inferred from the underlying tables; otherwise rows are auto-hashed. |
k | Float | No | Smoothing parameter for RRF scoring (default: 60.0) |
limit | Integer | No | Upper bound on the fused result set. Also propagated as a default limit to any nested search subquery that does not specify its own. |
time_column | String | No | Column name containing timestamps for recency boosting |
recency_decay | String | No | Decay function: 'linear' or 'exponential' (default: 'exponential') |
decay_constant | Float | No | Decay rate for exponential decay (default: 0.01) |
decay_scale_secs | Float | No | Time scale in seconds for decay (default: 86400) |
decay_window_secs | Float | No | Window size for linear decay in seconds (default: 86400) |
rank_weight | Float | No | Per-query ranking weight (specified within the individual search subquery call) |
WHERE predicates on base table columns (e.g., review_date, product_category) are pushed down into each nested search subquery as pre-filters — they are applied before ranking and fusion, so each subquery only considers matching rows. This means the fused results reflect the top-K within the filtered set, not a post-filtered slice of unfiltered rankings.
Predicates on computed columns like fused_score are applied as post-filters after fusion.
Basic Hybrid Search:
Weighted Ranking:
Recency-Boosted Search:
Linear Decay:
How RRF works:
RRF Score = Σ(rank_weight / (k + rank))k parameter controls ranking sensitivity (lower = more sensitive to rank position)Advanced query tuning:
rank_weight parametertime_column is specified, scores are multiplied by a decay factor
e^(-decay_constant * age_in_units) where age is in decay_scale_secsmax(0, 1 - (age_in_units / decay_window_secs))join_key is specified, rrf infers the primary key from the underlying tables; if none is available, rows are joined by an auto-generated row identifierrerank)Reranking reorders candidate results using a dedicated reranker model or an LLM-as-reranker for improved relevance. The input can be any search UDTF (vector_search, text_search, rrf) or a plain table.
Arguments:
| Parameter | Type | Required | Description |
|---|---|---|---|
input | Table or UDTF | Yes | Input rows to rerank. Can be a search UDTF call (vector_search, text_search, rrf) or a table name. |
model | String | Yes | Name of a registered reranker or chat model. |
document | String | Yes | Column containing the text to send to the reranker for scoring. |
query | String | No | Query string for relevance scoring. Auto-extracted from nested search UDTFs when omitted; required for bare-table inputs. |
limit | Integer | No | Maximum number of results to return. |
strategy | String | No | LLM reranking strategy: 'listwise' (default) or 'pointwise'. Only applies when the model resolves to a chat model. |
prompt_template | String | No | Custom prompt template for LLM-as-reranker. Use {query} and {document} placeholders. Only applies when the model resolves to a chat model. |
When the input is a search UDTF (vector_search, text_search, or rrf wrapping search UDTFs), the query string is automatically extracted from the nested call. Single-string, make_array(...), and ARRAY[...] query forms are all supported. For multi-query inputs, the first query string is used.
For bare-table inputs, query must be provided explicitly.
Rerank hybrid search results:
Rerank a plain table with an explicit query:
LLM-as-reranker with custom prompt:
See Reranking (opens in a new tab) for configuration, provider setup, and additional examples.
Spice SQL supports traditional filtering for exact and pattern-based matches:
% matches any sequence of characters._ matches a single character.Returns rows where the column exactly matches the value.
Spice SQL supports the PostgreSQL regex operators ~ (match), ~* (case-insensitive match), !~ (not match), and !~* (case-insensitive not match) — see Operators (opens in a new tab). Alternatively, use scalar functions such as regexp_like, regexp_match, and regexp_replace. For details and examples, see the Scalar Functions documentation (opens in a new tab).
For more on hybrid and advanced search, see Search Functionality (opens in a new tab) and Vector-Based Search (opens in a new tab)