---
title: "Full-Text Search"
sidebarTitle: "Full-Text Search"
description: "The fts() table function — match, fuzzy, phrase, boolean, boost and multi-match queries from SQL."
icon: magnifying-glass
keywords: ["fts", "sql", "full text search", "fuzzy", "phrase", "boolean", "boost", "multi-match"]
---
Enterprise [SQL runs on a deployment.](/sql)
Full-text search is a **table function**, `fts(table, query)`, rather than a
predicate -- it produces a relevance score alongside the matching rows, and a
`WHERE` clause has nowhere to put one. The two string-literal arguments are
the table name and a JSON-encoded query, built with the same query-builder
classes used from the SDK and serialized with `.to_json()`.
## Setup {#setup}
```sql
CREATE TABLE my_docs (id INT, text STRING, category STRING, author STRING)
INSERT INTO my_docs VALUES
(1, 'The happy puppy runs merrily in the park', 'animals', 'Alice'),
(2, 'A curious kitten jumps quickly over the fence', 'animals', 'Bob'),
(3, 'The puppy catches a ball with great enthusiasm', 'sports', 'Alice'),
(5, 'Puppy training requires patience and dedication', 'training', 'Alice')
CREATE INDEX ON my_docs USING fts (text)
```
Phrase queries need position information, which costs extra index size --
add `WITH (with_position = true)` to `CREATE INDEX`. Without it,
[phrase queries](#phrase) find nothing. See
[FTS parameters](/indexing/fts-index#fts-parameters).
## Basic search {#basic}
```python
from lancedb.query import MatchQuery
query = MatchQuery("puppy", "text")
json_query = query.to_json()
```
```sql
SELECT id, text, category FROM fts('my_docs', '')
LIMIT 5
```
By default the top 5 matches come back in **arbitrary order** -- select
`_score` and order by it for relevance ranking:
```sql
SELECT id, text, _score FROM fts('my_docs', '')
ORDER BY _score DESC
LIMIT 5
```
`_score` is a pseudo-column carrying the query's BM25 relevance for the row;
it behaves like any other column, but exists only in a relation that came
from `fts()`. See [Data types](/sql/data-types#pseudo-columns).
## Fuzzy {#fuzzy}
Tolerates typos, up to a character-edit budget:
```python
MatchQuery("pupy", "text", fuzziness=2)
```
## Phrase {#phrase}
Exact phrase, or with `slop` to allow words between terms:
```python
PhraseQuery("happy puppy", "text")
PhraseQuery("puppy park", "text", slop=2) # up to 2 words between them
```
## Boolean {#boolean}
Combine queries with `&` (AND) or `|` (OR):
```python
MatchQuery("puppy", "text") & MatchQuery("happy", "text")
MatchQuery("puppy", "text") | MatchQuery("kitten", "text")
```
## Boost {#boost}
Weight a query up or down relative to another:
```python
from lancedb.query import BoostQuery
BoostQuery(
positive=MatchQuery("puppy", "text"),
negative=MatchQuery("kitten", "text"),
negative_boost=0.2,
)
```
## Multi-match {#multi-match}
Search several columns at once, optionally weighting them differently:
```python
from lancedb.query import MultiMatchQuery
MultiMatchQuery("puppy", ["text", "category"])
MultiMatchQuery("puppy", ["text", "category"], boosts=[2.0, 1.0]) # text weighted 2x
```
## Combining with SQL {#combining}
`fts()` is a relation, so it composes with the rest of the SQL surface --
filter, join, or aggregate over it same as any table:
```sql
SELECT id, text, category FROM fts('my_docs', '')
WHERE category = 'animals'
LIMIT 5
```
## What to read next {#next}
The query-builder equivalent, and every query form in full.
Tokenizers, and the position information phrase queries need.