> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lancedb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search

> Choose vector, full-text or hybrid search and return the records your application needs.

**Applies to:** Embedded ✅, Enterprise ✅

LanceDB supports vector, full-text and hybrid search. Use vector search to find
similar content, full-text search to match words, or hybrid search to combine
both. Filters narrow the results to records that match your criteria.

## Choose a search method

| You want to find | Use | Prepare |
| :- | :- | :- |
| Items similar to a query, such as related passages or images | [Vector search](https://docs.lancedb.com/oss/v0-41-0/search/vector-search.md) | Stored vectors and a compatible query vector |
| Documents represented by several embeddings | [Multivector search](https://docs.lancedb.com/oss/v0-41-0/search/vector-search.md#multivector) | A multivector column and compatible query vectors |
| Records containing particular words or phrases | [Full-text search](https://docs.lancedb.com/oss/v0-41-0/search/full-text-search.md) | An FTS index on the text column |
| Relevant meaning alongside keyword matches | [Hybrid search](https://docs.lancedb.com/oss/v0-41-0/search/hybrid-search.md) | Vectors and an FTS index |
| Rows matching a condition, without similarity ranking | [Filtering](https://docs.lancedb.com/oss/v0-41-0/search/filtering.md) | A predicate over table columns |

Vector search can run without an index. An approximate nearest-neighbor index
reduces the work needed for larger tables, with a tradeoff between recall and
latency. Full-text search needs an FTS index before you query it.

## Before you search

For vector search, generate stored and query vectors with the same embedding
model and compatible settings. Their dimensions must match. Keep the fields
you want to return, such as document text or an item ID, in the same table.

For full-text search, choose the text column and build its index. Enterprise
builds indexes in the background, so wait for the index to be ready before
querying it. Phrase queries also need token positions in the index. See
[FTS indexes](https://docs.lancedb.com/oss/v0-41-0/indexing/fts-index.md).

> [accordion] Run a complete search example in Python, TypeScript or Rust
>
> These examples use three sample records and hand-written vectors, so no
> embedding service is needed. Each program creates a temporary local table
> and removes it when finished.
>
> * **Python:** Install with `pip install lancedb`, save as `search.py`, then run `python search.py`.
> * **TypeScript:** Install with `npm install @lancedb/lancedb`, save as `search.mts`, then run `node search.mts` with Node.js 22.18 or later.
> * **Rust:** Download and extract the [complete Rust examples](https://raw.githubusercontent.com/lancedb/lancedb/2af85bea8999c8cd23ec01ff62ba110c54652c10/docs/web/static/code/search-rust.zip), then run `cargo run --locked --bin vector_search` from the extracted directory.
>
> ```python Python icon="python" theme={"theme":{"light":"vitesse-light","dark":"catppuccin-mocha"}}
> table = db.create_table(
>     "documents",
>     [
>         {
>             "id": 1,
>             "text": "A puppy playing in the park",
>             "category": "animals",
>             "vector": [1.0, 0.0],
>         },
>         {
>             "id": 2,
>             "text": "A dog resting at home",
>             "category": "animals",
>             "vector": [0.9, 0.1],
>         },
>         {
>             "id": 3,
>             "text": "A guide to growing tomatoes",
>             "category": "gardening",
>             "vector": [0.0, 1.0],
>         },
>     ],
> )
> rows = (
>     table.search([1.0, 0.0])
>     .distance_type("l2")
>     .select(["id", "text", "_distance"])
>     .limit(2)
>     .to_list()
> )
> print(rows)
> ```
>
> ```typescript TypeScript icon="square-js" theme={"theme":{"light":"vitesse-light","dark":"catppuccin-mocha"}}
> const lancedb = await import("@lancedb/lancedb");
> const { mkdtemp, rm } = await import("node:fs/promises");
> const { tmpdir } = await import("node:os");
> const { join } = await import("node:path");
> const uri = await mkdtemp(join(tmpdir(), "lancedb-search-"));
> try {
>   const db = await lancedb.connect(uri);
>   const table = await db.createTable("documents", [
>     {
>       id: 1,
>       text: "A puppy playing in the park",
>       category: "animals",
>       vector: [1.0, 0.0],
>     },
>     {
>       id: 2,
>       text: "A dog resting at home",
>       category: "animals",
>       vector: [0.9, 0.1],
>     },
>     {
>       id: 3,
>       text: "A guide to growing tomatoes",
>       category: "gardening",
>       vector: [0.0, 1.0],
>     },
>   ]);
>   const rows = await table
>     .vectorSearch([1.0, 0.0])
>     .distanceType("l2")
>     .select(["id", "text", "_distance"])
>     .limit(2)
>     .toArray();
>   console.log(rows);
>
> } finally {
>   await rm(uri, { recursive: true, force: true });
> }
> ```
>
> ```rust Rust icon="rust" theme={"theme":{"light":"vitesse-light","dark":"catppuccin-mocha"}}
> use arrow_array::types::Float32Type;
> use arrow_array::{FixedSizeListArray, Int64Array, RecordBatch, RecordBatchIterator, StringArray};
> use arrow_schema::{DataType, Field, Schema};
> use futures::TryStreamExt;
> use lancedb::query::{ExecutableQuery, QueryBase, Select};
> use std::sync::Arc;
>
> #[tokio::main]
> async fn main() -> Result<(), Box<dyn std::error::Error>> {
>     let directory = tempfile::tempdir()?;
>     let db = lancedb::connect(directory.path().to_str().unwrap())
>         .execute()
>         .await?;
>     let schema = Arc::new(Schema::new(vec![
>         Field::new("id", DataType::Int64, false),
>         Field::new("text", DataType::Utf8, false),
>         Field::new("category", DataType::Utf8, false),
>         Field::new(
>             "vector",
>             DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Float32, true)), 2),
>             true,
>         ),
>     ]));
>     let batch = RecordBatch::try_new(
>         schema.clone(),
>         vec![
>             Arc::new(Int64Array::from(vec![1, 2, 3])),
>             Arc::new(StringArray::from(vec![
>                 "A puppy playing in the park",
>                 "A dog resting at home",
>                 "A guide to growing tomatoes",
>             ])),
>             Arc::new(StringArray::from(vec!["animals", "animals", "gardening"])),
>             Arc::new(
>                 FixedSizeListArray::from_iter_primitive::<Float32Type, _, _>(
>                     [[1.0, 0.0], [0.9, 0.1], [0.0, 1.0]]
>                         .into_iter()
>                         .map(|v| Some(v.into_iter().map(Some).collect::<Vec<_>>())),
>                     2,
>                 ),
>             ),
>         ],
>     )?;
>     let reader = RecordBatchIterator::new(vec![Ok(batch)].into_iter(), schema);
>     let table = db
>         .create_table(
>             "documents",
>             Box::new(reader) as Box<dyn arrow_array::RecordBatchReader + Send>,
>         )
>         .execute()
>         .await?;
>     let query = table
>         .vector_search(&[1.0_f32, 0.0])?
>         .distance_type(lancedb::DistanceType::L2)
>         .select(Select::columns(&["id", "text", "_distance"]))
>         .limit(2);
>     let rows: Vec<RecordBatch> = query.execute().await?.try_collect().await?;
>     println!("{rows:?}");
>
>     Ok(())
> }
> ```
>
> This query returns the puppy and dog records, ordered by vector distance.
> It searches the three-row table directly, without an ANN index or an embedding
> service. For connection options, see [Connect](https://docs.lancedb.com/oss/v0-41-0/basics/catalog.md).

## Read the results

Use `select()` to choose the columns your application needs and `limit()` to
set the maximum number of results. The search also returns a ranking value:

| Search method | Ranking column | Order |
| :- | :- | :- |
| Vector | `_distance` | Smaller distance first |
| Full-text | `_score` | Higher text relevance first |
| Hybrid | `_relevance_score` | Higher combined relevance first |

These values have different meanings. Do not compare a vector distance directly
with a full-text score or treat a reranker score as a probability.

## Add filters and reranking

[Filters](https://docs.lancedb.com/oss/v0-41-0/search/filtering.md) restrict which rows qualify, such as documents
from a particular collection. Prefiltering searches within those rows.
Postfiltering removes nonmatching rows from the candidates already retrieved,
so it can leave fewer results than requested.

[Reranking](https://docs.lancedb.com/oss/v0-41-0/reranking.md) reorders the candidates returned by retrieval. Start
with representative queries and expected results, then compare changes to
relevance and latency. A reranker cannot recover a relevant row that retrieval
never returned.

## Read large result sets in batches

Materializing all rows at once can exhaust application memory. For exports and
training reads, use bounded queries or the batch-reading interfaces described
in [Data loading](https://docs.lancedb.com/oss/v0-41-0/training/data-loading.md). The SDK buffers Enterprise
query responses before exposing batches. Set a query limit rather than relying
on batch iteration alone to bound memory.

Direct Lance dataset access through Python's `table.to_lance()` is a LanceDB
Embedded interface. Use the SDK query builders for reads that need to work with
both LanceDB Embedded and LanceDB Enterprise.

## Investigate a slow query

Use [query plans](https://docs.lancedb.com/oss/v0-41-0/basics/sql.md#query-plans) to see the scans, filters and indexes selected
for a query. `explain_plan()` inspects the plan; `analyze_plan()` executes the
query and reports measurements. Compare one change at a time, such as adding
an index, returning fewer columns, or adjusting search parameters.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.