Search records
Database includes three search modes for relational flat tables:
| Mode | Best for | Query interfaces |
|---|---|---|
| Full text | Words and phrases across selected text fields | JavaScript, Python, Refine, REST |
| Vector | Semantic similarity against one embedding field | Python, REST |
| Hybrid | Combining semantic and keyword relevance | Python, REST |
Whole-table JSONB storage does not provide vector or hybrid search. Review storage compatibility before applying a search workflow to an existing table.
Prepare a searchable table
Prepare the table before your application uses search:
- Choose the text fields or embedding field that represents the searchable content.
- Add the matching search configuration to the table's schema — see Plan schema changes.
- Apply and verify the generated search or vector index.
- Store representative records and run a bounded test query.
- Confirm the expected results, stable ordering, and application policy with production-equivalent roles.
Configure full-text search
A table with full-text search stores a generated search_vector column and a
GIN index. Its search configuration chooses the searchable text fields and
language configuration. Application clients send only the search term and
normal query controls.
search_fields contains between one and ten distinct fields. Each field must
have the Frictionless type string or text and may carry a weight from A
(highest) through D (lowest):
{
"search_fields": [
{"field": "title", "weight": "A"},
{"field": "summary", "weight": "B"}
],
"search_config": "english"
}
When a weight is omitted, Database assigns A, B, C, then D to
successive unweighted entries and uses D for any remaining entries. The
weights affect the text rank consumed by hybrid search; the ordinary full-text
query below still uses the explicit application ordering.
Query the table through the interface your application already uses.
- JavaScript SDK
- Python SDK
- Refine
- REST API
await database
.from('documents')
.search('release planning')
.sort('id', 'asc')
.page(1)
.pageSize(20)
.execute();
(
client.database
.from_("documents")
.search("release planning")
.sort("id", "asc")
.page(1)
.page_size(20)
.execute()
)
useList({
resource: 'documents',
sorters: [{field: 'id', order: 'asc'}],
pagination: {currentPage: 1, pageSize: 20, mode: 'server'},
meta: {search: 'release planning'},
});
/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/curl "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/?search=release%20planning&ordering=id&page=1&page_size=20" \
-H "Authorization: Api-Key $TARUVI_API_KEY"
200Returns text matches in stable ID order when the table has a generated search vector.
The search convenience parameter is applied only when the table contains the
generated search vector. On a table without search configured it has no effect, so only show search in
your UI for tables that have it.
Full-text search on this route filters matching records; it does not expose a
relevance score or apply relevance ordering. Supply a stable order whenever
you paginate. The examples use the unique id field.
Vector search
A vector field uses Frictionless type: "any" with vector
metadata. TaruviBase derives its HNSW index from the same metadata:
{
"name": "embedding",
"type": "any",
"x-vector-metadata": {
"dimensions": 1536,
"distance": "cosine",
"index_type": "hnsw",
"storage_type": "vector"
}
}
A table supports one vector field. Its distance must be cosine, l2, or
ip, and its storage must be vector or halfvec. HNSW vector fields
allow up to 2,000 dimensions; halfvec allows up to 4,000.
The application supplies query_embedding with exactly the configured number
of dimensions. Database stores and searches embeddings; it does not generate
them from source text. In the REST examples, query-embedding.json contains
one JSON array with exactly 1,536 numeric values for this example schema.
- Python SDK
- REST API
(
client.database
.from_("documents")
.vector_search(
"embedding",
query_embedding,
topk=10,
metric="cosine",
)
.sort("id", "asc")
.page(1)
.page_size(10)
.execute()
)
/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/QUERY_EMBEDDING="$(tr -d '\n' < query-embedding.json)"
curl -G "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--data-urlencode "embedding__vector_near=$QUERY_EMBEDDING" \
--data-urlencode "_topk=10" \
--data-urlencode "_vector_metric=cosine" \
--data-urlencode "ordering=id"
200Returns vector-ranked rows with a stable ID tie-breaker and metric-specific similarity scores.
Pure vector rows include _vector_score, the raw pgvector comparison value,
and _similarity_score, a derived higher-is-better value. The transformation
depends on the selected metric:
| Metric | _vector_score | _similarity_score |
|---|---|---|
cosine | Cosine distance; lower ranks first | 1 - distance, in the range [-1, 1] |
l2 | Euclidean distance; lower ranks first | 1 / (1 + distance), in the range (0, 1] |
ip | Negated inner product; lower ranks first | Inner product (-distance), which is unbounded |
These values are ranking signals, not probabilities, and are not comparable
across different metrics. The id ordering in the example breaks equal-score
ties without replacing vector rank.
Hybrid search
Hybrid search runs vector and full-text retrieval and combines their ranks with reciprocal-rank fusion. It requires both a query embedding and a keyword query.
- Python SDK
- REST API
(
client.database
.from_("documents")
.vector_search(
"embedding",
query_embedding,
topk=20,
metric="cosine",
)
.search("release planning")
.hybrid(strategy="rrf", alpha=0.5)
.sort("id", "asc")
.page(1)
.page_size(10)
.execute()
)
/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/QUERY_EMBEDDING="$(tr -d '\n' < query-embedding.json)"
curl -G "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/documents/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--data-urlencode "embedding__vector_near=$QUERY_EMBEDDING" \
--data-urlencode "search_vector__search=release planning" \
--data-urlencode "_hybrid_strategy=rrf" \
--data-urlencode "_hybrid_alpha=0.5" \
--data-urlencode "_topk=20" \
--data-urlencode "ordering=id"
200Returns fused rankings with a stable ID tie-breaker and hybrid component scores.
Only rrf is implemented. alpha=0 uses only the full-text contribution;
alpha=1 uses only the vector contribution. Hybrid rows include
_hybrid_score, _vector_score, and _fts_score. The two component scores
are reciprocal-rank terms for their respective result lists, or zero when that
leg does not contribute. _hybrid_score applies alpha to the vector term and
1 - alpha to the full-text term. None of these hybrid values is a raw vector
distance, text rank, or probability. The id ordering breaks equal fused-score
ties.
Query controls
| Control | Behavior |
|---|---|
_topk | Defines the candidate window and defaults to 10; keep it bounded for the query and page size |
_vector_metric | Must match the field's cosine, l2, or ip definition |
_vector_threshold | Applies a metric-dependent cutoff |
_vector_ef_search | Sets HNSW search breadth; larger values trade query work for recall |
_hybrid_alpha | Balances the vector and full-text contributions from 0 to 1 |
Pagination slices the top-k candidate window. An offset at or beyond _topk
is rejected. Secondary ordering can break equal-score ties but does not
replace score order.
Embedding fields are omitted from default query results. Request an embedding
field explicitly with field selection only when the application needs it. The
generated search_vector field is managed by Database and is never returned.
Validate the embedding dimension before the request, avoid logging embeddings
that may encode sensitive content, and measure latency and relevance with
application data before a launch decision.
Continue with query field selection or index planning.