Skip to Content

Search

search() queries one or more fields in a single request. The server computes one ranked list per queried field, and search() always returns those lists keyed by field name. There is no single-field special case. To collapse the per-field lists into one ranked list, fuse them with the standalone rerank() function.

This page covers the field query shape, single- and multi-field search, hybrid fusion with RRF, and the structure of results.

Search Parameters

search() takes the fields map plus optional top-level parameters. limit and ef_search are set per field (inside each field’s query config), not at the top level.

ParameterDescriptionDefault
fieldsMap of field name → query config {query, limit?, ef_search?}. query is required.Required
filterFilter conditions to restrict results (see Filtering)None
prefilter_cardinality_threshold(filtered search only) Match-count below which Endee brute-forces the prefilter instead of HNSW. Range 1,0001,000,000.10,000
filter_boost_percentage(filtered search only) Expand the HNSW candidate pool before filtering, 0100.0

Per-field limit and ef_search. Each field draws results independently. limit is the max number of hits to return for that field (default 10, max 4096); ef_search tunes that field’s HNSW recall/latency (default 128, max 1024). Both live inside the field’s query config. There is no overall result limit.

The Field Query Shape

Every field is queried with the same config object: {query, limit?, ef_search?}. query is required (there is no bare-value shorthand in all SDKs), and its value matches the field’s type:

Field typequery shape
vector[0.2, 0.2, ...]
sparse{"indices": [3, 17], "values": [0.8, 0.4]}
multi_vector[[...], [...]]

The Python and TypeScript clients share one model: the per-field config form (query required), per-field limit/ef_search, results always keyed by field name, and a standalone rerank() for fusion. The only surface difference is naming: Python uses ef_search/snake_case keys, TypeScript uses ef_search inside the field config with camelCase for top-level options.

Query one field and get that field’s ranked list under results[field_name].

collection = client.get_collection("my_collection") res = collection.search( fields={"embedding": {"query": [0.12, -0.34, 0.89, ...], "limit": 5, "ef_search": 128}}, ) # results is always keyed by field name. for hit in res["results"]["embedding"]: print(hit["id"], round(hit["similarity"], 4), hit["meta"])

Increasing ef_search improves recall at the cost of latency. For most use cases the default 128 is a good starting point.

Multi-Field Search (per-field results)

Query several fields and you get each field’s own ranked list, keyed by field name. limit is per field, so each field draws independently. Scores are in each field’s own scale (cosine for dense, sparse dot product for keywords) and are not comparable across fields, which is why fusion is a separate, opt-in step.

res = collection.search( fields={ "embedding": {"query": [0.2, ...], "limit": 50}, "keywords": {"query": {"indices": [3, 17], "values": [0.8, 0.4]}, "limit": 50}, }, ) res["results"]["embedding"] # → [ {id, similarity, meta, filter}, ... ] res["results"]["keywords"] # → [ {id, similarity, meta, filter}, ... ]

Hybrid Search: Fusing with rerank()

To collapse the per-field lists into a single ranked list, fuse them with Reciprocal Rank Fusion (RRF). In all SDKs rerank() is a standalone function: run search(), then pass its result into rerank().

from endee import rerank res = collection.search( fields={ "embedding": {"query": [0.2, ...], "limit": 50}, "keywords": {"query": {"indices": [3, 17], "values": [0.8, 0.4]}, "limit": 50}, }, ) fused = rerank( res, limit=10, field_weights={"embedding": 0.6, "keywords": 0.4}, # optional, must sum to 1.0 rrf_k=60, # optional RRF constant (default 60) ) # Fused → results is a single ranked list. for hit in fused["results"]: print(hit["id"], round(hit["similarity"], 4), hit["meta"])

Each fused hit keeps the meta/filter from its per-field hit; similarity is replaced with the RRF score, the sum of weight / (rrf_k + rank) across the fields it appeared in (rank is 1-based). A larger rrf_k flattens the contribution gap between ranks; a smaller one sharpens it (top ranks dominate). Only "rrf" is supported currently.

Return Shapes

Callresults shape
search(...) (1 or N fields){ field: [hit, ...], ... } (per-field, keyed by field name)
rerank(search_result, ...)[hit, ...] (a single fused list)

search() always returns results keyed by field name ({ results: { field: [...] } }) in all SDKs, even for a single field. Fusion into one flat list ({ results: [hit, ...] }) is done only by the separate rerank() call.

Result Fields

Each hit in a search response contains:

FieldDescription
idUnique identifier of the matched object
similaritySimilarity score (higher means more similar). For RRF results this is the fused score.
metaMetadata object attached to the object
filterFilter tags attached to the object

Search results carry meta/filter but not the stored vectors. To retrieve the vectors, use get_objects.