# `QuackDB.FTS`
[🔗](https://github.com/elixir-vibe/quackdb/blob/main/lib/quack_db/fts.ex#L1)

SQL helpers for DuckDB's full-text search extension.

These helpers return iodata for DuckDB FTS statements and expressions. DuckDB
autoloads the `fts` extension on first use in many configurations, but you can
install/load it explicitly:

    alias QuackDB.FTS

    QuackDB.query!(conn, FTS.install())
    QuackDB.query!(conn, FTS.load())
    QuackDB.query!(conn, FTS.create_index("documents", :id, [:title, :body]))

    score = FTS.match_bm25(~s|"id"|, "duckdb analytics", schema: FTS.schema_name("main.documents"))
    QuackDB.query!(conn, ["SELECT id, ", score, " AS score FROM documents ORDER BY score DESC"])

`bm25/3` and `search_score/3` are aliases for `match_bm25/3`. Use `stem/2`
for DuckDB's stemming helper.

# `create_option`

```elixir
@type create_option() ::
  {:stemmer, atom() | String.t()}
  | {:stopwords, atom() | String.t()}
  | {:ignore, String.t()}
  | {:strip_accents, boolean()}
  | {:lower, boolean()}
  | {:overwrite, boolean()}
```

# `match_option`

```elixir
@type match_option() ::
  {:fields, [atom() | String.t()] | atom() | String.t()}
  | {:k, number()}
  | {:b, number()}
  | {:conjunctive, boolean()}
  | {:schema, atom() | String.t()}
```

# `bm25`

```elixir
@spec bm25(iodata(), String.t(), [match_option()]) :: iodata()
```

Alias for `match_bm25/3`.

# `create_index`

```elixir
@spec create_index(
  atom() | String.t(),
  atom() | String.t(),
  [atom() | String.t()] | :all,
  [
    create_option()
  ]
) :: iodata()
```

Builds `PRAGMA create_fts_index(...)`.

# `drop_index`

```elixir
@spec drop_index(atom() | String.t()) :: iodata()
```

Builds `PRAGMA drop_fts_index(...)`.

# `install`

```elixir
@spec install() :: iodata()
```

Builds an `INSTALL fts;` statement.

# `load`

```elixir
@spec load() :: iodata()
```

Builds a `LOAD fts;` statement.

# `match_bm25`

```elixir
@spec match_bm25(iodata(), String.t(), [match_option()]) :: iodata()
```

Builds `match_bm25(id, query, ...)`.

# `schema_name`

```elixir
@spec schema_name(atom() | String.t()) :: String.t()
```

Returns DuckDB's generated FTS schema name for a table.

DuckDB names the schema after the fully qualified table, so an unqualified
table resolves to `main` the same way `create_index/4` does:
`"documents"` and `"main.documents"` both give `"fts_main_documents"`.

# `search_score`

```elixir
@spec search_score(iodata(), String.t(), [match_option()]) :: iodata()
```

Alias for `match_bm25/3` when using the expression as a score/rank.

# `stem`

```elixir
@spec stem(iodata(), atom() | String.t()) :: iodata()
```

Builds `stem(text, stemmer)`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
