Basic Usage
Consider that we have a LanceDB table namedmy_table, whose string column text we want to index and query via keyword search, the FTS index must be created before you can search via keywords.
Table Setup
First, open or create the table you want to search:Construct FTS Index
Create a full-text search index on your text column:In Python, this page shows the synchronous
create_fts_index(...) form. For the
asynchronous equivalent (await table.create_index("text", config=FTS(...))), see
FTS index.Full-text Search
Perform full-text search and retrieve results:fts_columns="text"
LanceDB automatically searches on the existing FTS index if the input to the search is of type
str. If you provide a vector as input, LanceDB will search the ANN index instead.fts_columns or the query builder’s nearest_to_text(..., columns=...); in TypeScript, use query().nearestToText(..., columns). The newer Lance-native FTS does not accept legacy Tantivy-only index parameters.
Keeping the index up to date
Rows you add after building an FTS index aren’t part of the index until you optimize the table. Until then, queries fall back to a flat scan over the unindexed fragments to keep results complete, which slows them down as the unindexed tail grows. Calltable.optimize() to fold new rows into the existing index — it’s the same operation used for vector indexes:
optimize() after roughly 100,000 row changes or 20 data-modification operations, whichever comes first. For tables with continuous ingest, schedule it on a cadence that keeps num_unindexed_rows (from table.index_stats(...)) close to zero. If you want to skip the flat scan over unindexed rows entirely — for example, on a hot read path where stale results are acceptable — call .fast_search() on the query so the search returns only indexed results.
Advanced Usage
Tokenize Table Data
By default, the text is tokenized by splitting on punctuation and whitespaces, and would filter out words that are longer than 40 characters. All words are converted to lowercase. Stemming is useful for improving search results by reducing words to their root form, e.g. “running” to “run”. LanceDB supports stemming for Arabic, Danish, Dutch, English, Finnish, French, German, Greek, Hungarian, Italian, Norwegian, Portuguese, Romanian, Russian, Spanish, Swedish, Tamil, and Turkish. You should set thebase_tokenizer parameter rather than tokenizer_name because you cannot customize the tokenizer if tokenizer_name is specified.
Tokenization and language filters are separate settings. base_tokenizer controls how text is split into searchable tokens. language controls stemming and stop-word removal when stem=True or remove_stop_words=True; choose the tokenizer for CJK or mixed-language segmentation.
For example, to enable stemming for English:
base_tokenizer:"simple"language: Englishwith_position: falsemax_token_length: 40lower_case: truestem: trueremove_stop_words: trueascii_folding: truecustom_stop_words:None— pass alist[str]to drop additional words beyond the language defaults. Requiresremove_stop_words=True.
base_tokenizer="icu" for unicode-aware word segmentation on mixed-language text. ICU stands for International Components for Unicode. The ICU tokenizer uses bundled ICU4X segmenter data, so it does not require external tokenizer model files. It is a good default when documents mix languages or include scripts where the simple tokenizer would keep an unspaced span as one large token.
The Python API also supports tokenizer implementations that load language model files. Use base_tokenizer="jieba/default" for Jieba tokenization, which segments Chinese text into searchable word tokens when the text is written without spaces between words. Use Lindera-backed tokenizers for dictionary-based East Asian morphological segmentation, such as base_tokenizer="lindera/ipadic" for Japanese or base_tokenizer="lindera/ko-dic" for Korean when you have installed and compiled that Lindera model. These are language-specific tokenizers; ICU is the broader mixed-language option.
lance/language_models, or you can set LANCE_LANGUAGE_MODEL_HOME to point to a different model root:
jieba/default is resolved under <model-home>/jieba/default/..., lindera/ipadic under <model-home>/lindera/ipadic/..., and lindera/ko-dic under <model-home>/lindera/ko-dic/....
Built-in stop-word removal supports Danish, Dutch, English, Finnish, French, German, Hungarian, Italian, Norwegian, Portuguese, Russian, Spanish, and Swedish. If you use another stemming language, such as Arabic, Greek, Romanian, Tamil, or Turkish, set
remove_stop_words=False or pass custom_stop_words.ascii_folding to remove accents, e.g. ‘é’ to ‘e’:
Filtering Options
LanceDB full text search supports to filter the search results by a condition, both pre-filtering and post-filtering are supported. This can be invoked via the familiarwhere syntax.
With pre-filtering:
Phrase vs. Terms Queries
For full-text search you can specify either a phrase query like"the old man and the sea",
or a terms search query like old man sea.
To search for a phrase, the index must be created with with_position=True and remove_stop_words=False:
Fuzzy Search
Fuzzy search allows you to find matches even when the search terms contain typos or slight variations. LanceDB uses the classic Levenshtein distance to find similar terms within a specified edit distance.
For a complete walkthrough that creates a sample table and demonstrates fuzzy search and relevance boosting, see the fuzzy search example.
Search for Substring
LanceDB supports searching for substrings in the text column, you can set thebase_tokenizer parameter to "ngram" to enable this feature, and use the parameters ngram_min_length and ngram_max_length to control the length of the substrings: