Compare commits

..

7 Commits

Author SHA1 Message Date
Will Jones 011def461c docs(python): fix cross-references that resolved to the wrong page
`mkdocs build --strict` only catches references it cannot resolve. A bare
anchor such as `[limit][]` or `[vector search][search]` is matched by
autorefs against any heading on the site, so six of them silently linked
into the JavaScript reference instead. The relative links in
`permutation.py` and `remote/errors.py` pointed at in-page anchors and
paths that do not exist.

Targets that still exist here or in an imported inventory now use
mkdocstrings references; the guide pages deleted in #2770 use their
lancedb.com URLs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:19:38 -07:00
Will Jones ed6be12ad6 docs: clear the mkdocs warning backlog so --strict passes
`mkdocs build` emitted 61 warnings on main, and rendering the previously
undocumented classes in this PR pushed that to 158. That backlog is what
blocks turning on strict mode (#3707), so clear it here rather than leave
it worse than we found it.

Most of it was one systematic false positive: griffe cannot see the
generated `__init__` of a pydantic dataclass, so every documented
parameter looked unknown. `warn_unknown_params` turns that check off.

The rest were real docstring bugs, in 15 docstrings:

* Prose trailing a `Parameters` section is read as parameter names, which
  invented parameters called `The`, `you` and `To`. Moved into `Notes` or
  the summary.
* numpydoc only reads a type when the colon has spaces around it. Where
  the documented name is a pydantic attribute rather than a signature
  parameter, griffe has no signature to fall back on and the type was
  dropped. Affects nine embedding classes.
* `num_partitions, default sqrt(num_rows)` and friends parse as a list of
  names, rendering a bogus `default` parameter.
* One parameter indented five spaces instead of four.

`nodejs/CONTRIBUTING.md` links to the repo-root CONTRIBUTING.md, which
does not resolve once typedoc copies the file into `docs/src/js/_media/`;
an absolute URL works from both places.

`mkdocs build --strict` now exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:15:20 -07:00
Will Jones ac2b689cdb docs(python): render index/embeddings/remote/rerankers from __all__
Four packages are now rendered by a single mkdocstrings directive each,
driven by the module's `__all__`, instead of a hand-maintained list of
symbols. These were where most of the drift was: 7 of 12 rerankers and
14 of 17 embedding functions had never been listed.

`lancedb.embeddings` had no `__all__`; without one mkdocstrings renders
no members at all for a re-export package, so one is added.

AGENTS.md gains a section describing how the reference page is wired up
and how to check a docs build locally, plus a step in the "adding a new
method on Table" checklist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:59:23 -07:00
Will Jones 4fc8114871 docs(python): add missing public APIs to the Python reference
The Python API reference page had drifted from the public API. Branch
management (`Branches` / `AsyncBranches`, which own `diff` and `merge`),
structured full-text query classes, take queries, blob helpers,
namespace connections, most rerankers and embedding functions, the
PyTorch dataloader, and several other public symbols were never listed,
so they did not appear in the rendered docs.

Also fixes docstring cross-references that pointed at guide pages which
have since moved off this site, and at unresolvable relative targets
(`[Table](Table)`, `[PyArrow Table](pyarrow.Table)`).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:46:45 -07:00
Will Jones 03b26d585b fix: deflake test_read_consistency_interval (#3713)
`test_read_consistency_interval` asserted that a table opened with a
100ms `read_consistency_interval` still read stale data immediately
after a concurrent write. The cache timestamp is set when the table is
opened and reads within the interval do not refresh it, so that
assertion only held if the intervening open/count/commit/count sequence
finished within 100ms of real wall-clock time. On a loaded CI runner it
did not: the TTL expired, `count_rows` refreshed synchronously, and the
test failed with `left: 1, right: 0`. This broke the Rust workflow on
`main` at 0bc08160 (a Python-only commit).

This pins the `background_cache` mock clock once `table2` has seeded its
cache, and advances it explicitly in place of `tokio::time::sleep`, so
the test controls when the interval elapses. Same approach as #3547.
With the clock pinned there is no real sleep left to be imprecise, so
the `cfg(not(target_os = "windows"))` guard is dropped and the test now
runs on Windows too.

Verified by inserting a stall before the write: 120ms reproduces the
original failure deterministically, and with this change the test still
passes with a 500ms stall.

Fixes #3712
2026-07-29 13:06:41 -07:00
Yang Cen f7feed48c3 feat(fts): support custom stop-word lists (#3734)
## What

Expose custom FTS stop-word lists in the Python and TypeScript public
APIs, including their standalone tokenize helpers and remote index
creation.

This PR supports concrete string lists only. It does not add file or
LanceDB-table stop-word sources.

## Why

Rust already exposes Lance's custom stop-word list option. The Python
and TypeScript APIs did not pass it through, and local index details did
not retain the full tokenizer parameters needed by index-backed
tokenization after reopening a table.

## How

- Add `custom_stop_words` / `customStopWords` to the Python and
TypeScript FTS and tokenize options.
- Preserve `None` / `undefined`, empty lists, and list contents without
normalization.
- Load the persisted FTS segment parameters when returning local index
details.
- Serialize the concrete list in remote create-index requests.
- Keep Python and TypeScript tests thin; behavior, persistence, query
tokenization, and remote JSON coverage live primarily in Rust.

## Validation

- `cargo check --quiet --features remote --tests --examples`
- `cargo clippy --quiet --features remote --tests --examples`
- `cargo test --quiet --features remote --tests`
- Python extension rebuild with `uv` and `maturin`
- Targeted Python tests: 4 passed
- Python `ruff format --check` and `ruff check`
- TypeScript build, typecheck, Biome lint, generated docs, and targeted
tests

---------

Co-authored-by: Yang Cen <yangcen@Yangs-Mac-mini.local>
2026-07-29 17:40:12 +08:00
Lance Release e5f489818b Bump version: 0.37.0-beta.0 → 0.37.1-beta.0 2026-07-29 07:12:34 +00:00
45 changed files with 576 additions and 182 deletions
+29
View File
@@ -92,6 +92,8 @@ Python bindings changes:
* Should use `LOOP.run()` to call the corresponding `AsyncTable` method. * Should use `LOOP.run()` to call the corresponding `AsyncTable` method.
6. Add concrete sync method to `RemoteTable` class in `python/python/lancedb/remote/table.py`. 6. Add concrete sync method to `RemoteTable` class in `python/python/lancedb/remote/table.py`.
7. Add unit test in `python/tests/test_table.py`. 7. Add unit test in `python/tests/test_table.py`.
8. If you added a new public class or module-level function (not just a method on an
existing class), expose it in the API reference. See "Python API reference" below.
TypeScript bindings changes: TypeScript bindings changes:
@@ -103,6 +105,33 @@ TypeScript bindings changes:
5. Add test in `nodejs/__test__/table.test.ts`. 5. Add test in `nodejs/__test__/table.test.ts`.
6. Run `npm run docs` to generate TypeScript documentation. 6. Run `npm run docs` to generate TypeScript documentation.
## Python API reference
`docs/src/python/python.md` is the entire Python API reference. It is maintained by
hand, and anything not listed there is not rendered at all, so new public classes and
module-level functions have to be added explicitly. How depends on the module:
* `lancedb.index`, `lancedb.embeddings`, `lancedb.remote`, and `lancedb.rerankers` are
rendered by a single directive each, driven by the module's `__all__`. Add the new
name to `__all__` and it appears; forget, and it is silently omitted.
* Everything else (`lancedb`, `lancedb.table`, `lancedb.query`, `lancedb.db`, ...) is
listed symbol by symbol. Add a `::: lancedb.<module>.<Name>` line to the matching
section, and remember that the page separates synchronous and asynchronous APIs.
Deliberately undocumented: concrete implementations reached through an abstract base
(`LanceTable`, `LanceDBConnection`, `RemoteDBConnection`), query base classes already
covered by `inherited_members`, and internal helpers.
Cross-references in docstrings use mkdocstrings syntax, `[text][lancedb.table.Table]`.
Plain relative links such as `[Table](Table)` do not resolve. To check your work:
```shell
pip install -r docs/requirements.txt
cd docs && PYTHONPATH=. mkdocs build
```
The docs site only builds on pushes to `main`, so this is not covered by PR CI.
## Review Guidelines ## Review Guidelines
Please consider the following when reviewing code contributions. Please consider the following when reviewing code contributions.
Generated
+3 -3
View File
@@ -5379,7 +5379,7 @@ dependencies = [
[[package]] [[package]]
name = "lancedb" name = "lancedb"
version = "0.37.0-beta.0" version = "0.37.1-beta.0"
dependencies = [ dependencies = [
"ahash", "ahash",
"anyhow", "anyhow",
@@ -5467,7 +5467,7 @@ dependencies = [
[[package]] [[package]]
name = "lancedb-nodejs" name = "lancedb-nodejs"
version = "0.37.0-beta.0" version = "0.37.1-beta.0"
dependencies = [ dependencies = [
"arrow-array", "arrow-array",
"arrow-buffer", "arrow-buffer",
@@ -5492,7 +5492,7 @@ dependencies = [
[[package]] [[package]]
name = "lancedb-python" name = "lancedb-python"
version = "0.37.0-beta.0" version = "0.37.1-beta.0"
dependencies = [ dependencies = [
"arrow", "arrow",
"async-trait", "async-trait",
+5
View File
@@ -51,6 +51,11 @@ plugins:
paths: [../python/python] paths: [../python/python]
options: options:
docstring_style: numpy docstring_style: numpy
docstring_options:
# Attributes documented in a `Parameters` section, and pydantic
# dataclasses whose `__init__` griffe cannot see statically, both
# trip this check. It reports nothing actionable here.
warn_unknown_params: false
heading_level: 3 heading_level: 3
show_signature_annotations: true show_signature_annotations: true
show_root_heading: true show_root_heading: true
+10
View File
@@ -453,6 +453,16 @@ paths:
The metric type to use for the index. l2, Cosine, Dot are supported. The metric type to use for the index. l2, Cosine, Dot are supported.
index_type: index_type:
type: string type: string
custom_stop_words:
type: [array, "null"]
items:
type: string
description: |
The custom stop-word list for an FTS index. A non-null
array replaces the language's built-in stop-word list and is only
applied when remove_stop_words is enabled. Null uses the built-in
language list, while an empty array explicitly replaces it with no
stop words.
responses: responses:
"200": "200":
description: Index successfully created description: Index successfully created
+1 -1
View File
@@ -1,7 +1,7 @@
# Contributing to LanceDB Typescript # Contributing to LanceDB Typescript
This document outlines the process for contributing to LanceDB Typescript. This document outlines the process for contributing to LanceDB Typescript.
For general contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md). For general contribution guidelines, see [CONTRIBUTING.md](https://github.com/lancedb/lancedb/blob/main/CONTRIBUTING.md).
## Project layout ## Project layout
+15
View File
@@ -56,6 +56,21 @@ the experimental FTS V3 format and may introduce breaking changes.
*** ***
### customStopWords?
```ts
optional customStopWords: string[];
```
Custom stop words that replace the built-in list for `language`.
This option only affects tokenization when `removeStopWords` is true.
`undefined` keeps the built-in language list. An empty array explicitly
replaces it with no stop words.
***
### language? ### language?
```ts ```ts
+15
View File
@@ -30,6 +30,21 @@ The tokenizer to use. The default is "simple".
*** ***
### customStopWords?
```ts
optional customStopWords: string[];
```
Custom stop words that replace the built-in list for `language`.
This option only affects tokenization when `removeStopWords` is true.
`undefined` keeps the built-in language list. An empty array explicitly
replaces it with no stop words.
***
### language? ### language?
```ts ```ts
+141 -52
View File
@@ -26,6 +26,18 @@ is also an [asynchronous API client](#connections-asynchronous).
::: lancedb.db.DBConnection ::: lancedb.db.DBConnection
::: lancedb.Session
## Namespaces (Synchronous)
A namespace-backed connection resolves tables through a
[Lance namespace](https://lancedb.github.io/lance-namespace/) service instead of
listing a storage directory.
::: lancedb.connect_namespace
::: lancedb.namespace.LanceNamespaceDBConnection
## Tables (Synchronous) ## Tables (Synchronous)
::: lancedb.table.Table ::: lancedb.table.Table
@@ -34,8 +46,12 @@ is also an [asynchronous API client](#connections-asynchronous).
::: lancedb.table.FragmentSummaryStats ::: lancedb.table.FragmentSummaryStats
::: lancedb.table.TableStatistics
::: lancedb.table.Tags ::: lancedb.table.Tags
::: lancedb.table.Branches
## Expressions ## Expressions
Type-safe expression builder for filters and projections. Use these instead Type-safe expression builder for filters and projections. Use these instead
@@ -62,29 +78,46 @@ of raw SQL strings with [where][lancedb.query.LanceQueryBuilder.where] and
::: lancedb.query.LanceHybridQueryBuilder ::: lancedb.query.LanceHybridQueryBuilder
::: lancedb.query.LanceEmptyQueryBuilder
::: lancedb.query.LanceTakeQueryBuilder
## Full text queries
Structured full text queries can be passed to
[Table.search][lancedb.table.Table.search] or
[AsyncTable.search][lancedb.table.AsyncTable.search] in place of a query string,
and combined with [BooleanQuery][lancedb.query.BooleanQuery].
::: lancedb.query.FullTextQuery
::: lancedb.query.MatchQuery
::: lancedb.query.PhraseQuery
::: lancedb.query.BoostQuery
::: lancedb.query.MultiMatchQuery
::: lancedb.query.BooleanQuery
::: lancedb.query.FullTextOperator
::: lancedb.query.Occur
## Embeddings ## Embeddings
::: lancedb.embeddings.registry.EmbeddingFunctionRegistry ::: lancedb.embeddings
options:
::: lancedb.embeddings.base.EmbeddingFunctionConfig show_root_heading: false
show_root_toc_entry: false
::: lancedb.embeddings.base.EmbeddingFunction
::: lancedb.embeddings.base.TextEmbeddingFunction
::: lancedb.embeddings.sentence_transformers.SentenceTransformerEmbeddings
::: lancedb.embeddings.openai.OpenAIEmbeddings
::: lancedb.embeddings.open_clip.OpenClipEmbeddings
## Remote configuration ## Remote configuration
::: lancedb.remote.ClientConfig ::: lancedb.remote
options:
::: lancedb.remote.TimeoutConfig show_root_heading: false
show_root_toc_entry: false
::: lancedb.remote.RetryConfig
## Context ## Context
@@ -94,11 +127,50 @@ of raw SQL strings with [where][lancedb.query.LanceQueryBuilder.where] and
## Full text search ## Full text search
Use [lancedb.table.Table.create_fts_index][] for the synchronous API or Pass `custom_stop_words` to [lancedb.index.FTS][]:
[lancedb.table.AsyncTable.create_index][] with [lancedb.index.FTS][] for the
asynchronous API.
::: lancedb.index.FTS ```python
from lancedb.index import FTS
table.create_index(
"text",
config=FTS(remove_stop_words=True, custom_stop_words=["acme", "internal"]),
)
```
The list replaces the built-in stop words and is used only when
`remove_stop_words=True`:
- `custom_stop_words=None` uses the built-in list for `language`.
- `custom_stop_words=[]` removes no words.
- Values are passed through without trimming, lowercasing, or other rewriting.
The same option is available on `lancedb.tokenize(...)` and the deprecated
[lancedb.table.Table.create_fts_index][] compatibility helper:
```python
import lancedb
tokens = list(lancedb.tokenize("acme makes searchable data",
custom_stop_words=["acme"]))
```
::: lancedb.tokenize
::: lancedb.FtsToken
## Blobs
Blob columns store large binary values out of line so they can be read lazily
instead of being materialized with the rest of the row.
::: lancedb.blob
::: lancedb.BlobType
::: lancedb._blob.BlobFile
options:
show_root_full_path: false
## Utilities ## Utilities
@@ -106,6 +178,14 @@ asynchronous API.
::: lancedb.merge.LanceMergeInsertBuilder ::: lancedb.merge.LanceMergeInsertBuilder
::: lancedb.otel.instrument_lancedb_metrics
## Exceptions
::: lancedb.exceptions.MissingValueError
::: lancedb.exceptions.MissingColumnError
## Integrations ## Integrations
## Pydantic ## Pydantic
@@ -114,19 +194,30 @@ asynchronous API.
::: lancedb.pydantic.vector ::: lancedb.pydantic.vector
::: lancedb.pydantic.Vector
::: lancedb.pydantic.MultiVector
::: lancedb.pydantic.LanceModel ::: lancedb.pydantic.LanceModel
## PyTorch
::: lancedb.streaming.StreamingDataset
::: lancedb.permutation.permutation_builder
::: lancedb.permutation.PermutationBuilder
::: lancedb.permutation.Permutation
::: lancedb.permutation.Transforms
## Reranking ## Reranking
::: lancedb.rerankers.linear_combination.LinearCombinationReranker ::: lancedb.rerankers
options:
::: lancedb.rerankers.cohere.CohereReranker show_root_heading: false
show_root_toc_entry: false
::: lancedb.rerankers.colbert.ColbertReranker
::: lancedb.rerankers.cross_encoder.CrossEncoderReranker
::: lancedb.rerankers.openai.OpenaiReranker
## Connections (Asynchronous) ## Connections (Asynchronous)
@@ -137,6 +228,12 @@ can be used to create, list, or open tables.
::: lancedb.db.AsyncConnection ::: lancedb.db.AsyncConnection
## Namespaces (Asynchronous)
::: lancedb.connect_namespace_async
::: lancedb.namespace.AsyncLanceNamespaceDBConnection
## Tables (Asynchronous) ## Tables (Asynchronous)
Table hold your actual data as a collection of records / rows. Table hold your actual data as a collection of records / rows.
@@ -145,32 +242,20 @@ Table hold your actual data as a collection of records / rows.
::: lancedb.table.AsyncTags ::: lancedb.table.AsyncTags
::: lancedb.table.AsyncBranches
## Indices (Asynchronous) ## Indices (Asynchronous)
Indices can be created on a table to speed up queries. This section Indices can be created on a table to speed up queries. This section
lists the indices that LanceDb supports. lists the indices that LanceDb supports.
::: lancedb.index.BTree ::: lancedb.index
options:
::: lancedb.index.Bitmap show_root_heading: false
show_root_toc_entry: false
::: lancedb.index.LabelList # `lang_mapping` is defined in the module rather than imported, so it is
# picked up despite not being in `__all__`. It is an internal lookup table.
::: lancedb.index.FTS filters: ["!^_", "!^lang_mapping$"]
::: lancedb.index.IvfPq
::: lancedb.index.HnswPq
::: lancedb.index.HnswSq
::: lancedb.index.IvfFlat
::: lancedb.index.IvfSq
::: lancedb.index.IvfRq
::: lancedb.index.HnswFlat
::: lancedb.table.IndexStatistics ::: lancedb.table.IndexStatistics
@@ -198,3 +283,7 @@ rows nearest to a query vector and can be created with the
::: lancedb.query.AsyncHybridQuery ::: lancedb.query.AsyncHybridQuery
options: options:
inherited_members: true inherited_members: true
::: lancedb.query.AsyncTakeQuery
options:
inherited_members: true
+1 -1
View File
@@ -1,7 +1,7 @@
# Contributing to LanceDB Typescript # Contributing to LanceDB Typescript
This document outlines the process for contributing to LanceDB Typescript. This document outlines the process for contributing to LanceDB Typescript.
For general contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md). For general contribution guidelines, see [CONTRIBUTING.md](https://github.com/lancedb/lancedb/blob/main/CONTRIBUTING.md).
## Project layout ## Project layout
+7 -2
View File
@@ -226,7 +226,7 @@ describe("remote connection", () => {
); );
}); });
it("sends the FTS posting block size to remote tables", async () => { it("sends FTS options to remote tables", async () => {
let createIndexBody: Record<string, unknown> | undefined; let createIndexBody: Record<string, unknown> | undefined;
await withMockDatabase( await withMockDatabase(
@@ -264,7 +264,11 @@ describe("remote connection", () => {
async (db) => { async (db) => {
const table = await db.openTable("t"); const table = await db.openTable("t");
await table.createIndex("text", { await table.createIndex("text", {
config: Index.fts({ blockSize: 256 }), config: Index.fts({
blockSize: 256,
removeStopWords: true,
customStopWords: ["the"],
}),
}); });
}, },
); );
@@ -272,6 +276,7 @@ describe("remote connection", () => {
expect(createIndexBody?.["column"]).toBe("text"); expect(createIndexBody?.["column"]).toBe("text");
expect(createIndexBody?.["index_type"]).toBe("FTS"); expect(createIndexBody?.["index_type"]).toBe("FTS");
expect(createIndexBody?.["block_size"]).toBe(256); expect(createIndexBody?.["block_size"]).toBe(256);
expect(createIndexBody?.["custom_stop_words"]).toEqual(["the"]);
}); });
it("diffs and merges remote branches", async () => { it("diffs and merges remote branches", async () => {
+9
View File
@@ -2769,6 +2769,15 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
}, },
); );
test("tokenize supports custom stop words", async () => {
const tokens = await tokenize("the lance data", {
stem: false,
removeStopWords: true,
customStopWords: ["lance"],
});
expect(tokens.map((token) => token.text)).toEqual(["the", "data"]);
});
describe("when calling explainPlan", () => { describe("when calling explainPlan", () => {
let tmpDir: tmp.DirResult; let tmpDir: tmp.DirResult;
let table: Table; let table: Table;
+7 -1
View File
@@ -29,8 +29,14 @@ test("full text search", async () => {
const tbl = await db.createTable("myVectors", data, { mode: "overwrite" }); const tbl = await db.createTable("myVectors", data, { mode: "overwrite" });
await tbl.createIndex("doc", { await tbl.createIndex("doc", {
config: lancedb.Index.fts(), config: lancedb.Index.fts({
stem: false,
removeStopWords: true,
customStopWords: ["banana"],
}),
}); });
const tokens = await tbl.tokenize("apple banana", { column: "doc" });
expect(tokens.map((token) => token.text)).toEqual(["apple"]);
// --8<-- [start:full_text_search] // --8<-- [start:full_text_search]
const result = await tbl const result = await tbl
+11
View File
@@ -194,6 +194,16 @@ export interface TokenizeOptions {
/** Whether to remove stop words. */ /** Whether to remove stop words. */
removeStopWords?: boolean; removeStopWords?: boolean;
/**
* Custom stop words that replace the built-in list for `language`.
*
* This option only affects tokenization when `removeStopWords` is true.
*
* `undefined` keeps the built-in language list. An empty array explicitly
* replaces it with no stop words.
*/
customStopWords?: string[];
/** Whether to fold ASCII characters. */ /** Whether to fold ASCII characters. */
asciiFolding?: boolean; asciiFolding?: boolean;
@@ -225,6 +235,7 @@ export async function tokenize(
options?.lowercase, options?.lowercase,
options?.stem, options?.stem,
options?.removeStopWords, options?.removeStopWords,
options?.customStopWords,
options?.asciiFolding, options?.asciiFolding,
options?.ngramMinLength, options?.ngramMinLength,
options?.ngramMaxLength, options?.ngramMaxLength,
+11
View File
@@ -553,6 +553,16 @@ export interface FtsOptions {
*/ */
removeStopWords?: boolean; removeStopWords?: boolean;
/**
* Custom stop words that replace the built-in list for `language`.
*
* This option only affects tokenization when `removeStopWords` is true.
*
* `undefined` keeps the built-in language list. An empty array explicitly
* replaces it with no stop words.
*/
customStopWords?: string[];
/** /**
* whether to remove punctuation * whether to remove punctuation
*/ */
@@ -755,6 +765,7 @@ export class Index {
options?.lowercase, options?.lowercase,
options?.stem, options?.stem,
options?.removeStopWords, options?.removeStopWords,
options?.customStopWords,
options?.asciiFolding, options?.asciiFolding,
options?.ngramMinLength, options?.ngramMinLength,
options?.ngramMaxLength, options?.ngramMaxLength,
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@lancedb/lancedb", "name": "@lancedb/lancedb",
"version": "0.37.0-beta.0", "version": "0.37.1-beta.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@lancedb/lancedb", "name": "@lancedb/lancedb",
"version": "0.37.0-beta.0", "version": "0.37.1-beta.0",
"cpu": [ "cpu": [
"x64", "x64",
"arm64" "arm64"
+4
View File
@@ -43,6 +43,7 @@ pub fn tokenize(
lower_case: Option<bool>, lower_case: Option<bool>,
stem: Option<bool>, stem: Option<bool>,
remove_stop_words: Option<bool>, remove_stop_words: Option<bool>,
custom_stop_words: Option<Vec<String>>,
ascii_folding: Option<bool>, ascii_folding: Option<bool>,
ngram_min_length: Option<u32>, ngram_min_length: Option<u32>,
ngram_max_length: Option<u32>, ngram_max_length: Option<u32>,
@@ -72,6 +73,7 @@ pub fn tokenize(
if let Some(remove_stop_words) = remove_stop_words { if let Some(remove_stop_words) = remove_stop_words {
opts = opts.remove_stop_words(remove_stop_words); opts = opts.remove_stop_words(remove_stop_words);
} }
opts = opts.custom_stop_words(custom_stop_words);
if let Some(ascii_folding) = ascii_folding { if let Some(ascii_folding) = ascii_folding {
opts = opts.ascii_folding(ascii_folding); opts = opts.ascii_folding(ascii_folding);
} }
@@ -222,6 +224,7 @@ impl Index {
lower_case: Option<bool>, lower_case: Option<bool>,
stem: Option<bool>, stem: Option<bool>,
remove_stop_words: Option<bool>, remove_stop_words: Option<bool>,
custom_stop_words: Option<Vec<String>>,
ascii_folding: Option<bool>, ascii_folding: Option<bool>,
ngram_min_length: Option<u32>, ngram_min_length: Option<u32>,
ngram_max_length: Option<u32>, ngram_max_length: Option<u32>,
@@ -250,6 +253,7 @@ impl Index {
if let Some(remove_stop_words) = remove_stop_words { if let Some(remove_stop_words) = remove_stop_words {
opts = opts.remove_stop_words(remove_stop_words); opts = opts.remove_stop_words(remove_stop_words);
} }
opts = opts.custom_stop_words(custom_stop_words);
if let Some(ascii_folding) = ascii_folding { if let Some(ascii_folding) = ascii_folding {
opts = opts.ascii_folding(ascii_folding); opts = opts.ascii_folding(ascii_folding);
} }
+5 -2
View File
@@ -258,6 +258,7 @@ def tokenize(
lower_case: bool = True, lower_case: bool = True,
stem: bool = True, stem: bool = True,
remove_stop_words: bool = True, remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True, ascii_folding: bool = True,
ngram_min_length: int = 3, ngram_min_length: int = 3,
ngram_max_length: int = 3, ngram_max_length: int = 3,
@@ -265,9 +266,10 @@ def tokenize(
) -> Iterable[FtsToken]: ) -> Iterable[FtsToken]:
"""Tokenize a full-text search query using an explicit tokenizer. """Tokenize a full-text search query using an explicit tokenizer.
This does not require a table or FTS index. The tokenizer options match This does not require an FTS index. The tokenizer options match
:class:`lancedb.index.FTS`. :class:`lancedb.index.FTS`. ``custom_stop_words`` accepts a list of strings.
""" """
return _tokenize( return _tokenize(
query, query,
base_tokenizer=base_tokenizer, base_tokenizer=base_tokenizer,
@@ -276,6 +278,7 @@ def tokenize(
lower_case=lower_case, lower_case=lower_case,
stem=stem, stem=stem,
remove_stop_words=remove_stop_words, remove_stop_words=remove_stop_words,
custom_stop_words=custom_stop_words,
ascii_folding=ascii_folding, ascii_folding=ascii_folding,
ngram_min_length=ngram_min_length, ngram_min_length=ngram_min_length,
ngram_max_length=ngram_max_length, ngram_max_length=ngram_max_length,
+1
View File
@@ -59,6 +59,7 @@ def tokenize(
lower_case: bool = True, lower_case: bool = True,
stem: bool = True, stem: bool = True,
remove_stop_words: bool = True, remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True, ascii_folding: bool = True,
ngram_min_length: int = 3, ngram_min_length: int = 3,
ngram_max_length: int = 3, ngram_max_length: int = 3,
+2 -2
View File
@@ -359,7 +359,7 @@ class DBConnection(EnforceOverrides):
Data is converted to Arrow before being written to disk. For maximum Data is converted to Arrow before being written to disk. For maximum
control over how data is saved, either provide the PyArrow schema to control over how data is saved, either provide the PyArrow schema to
convert to or else provide a [PyArrow Table](pyarrow.Table) directly. convert to or else provide a [PyArrow Table][pyarrow.Table] directly.
>>> import pyarrow as pa >>> import pyarrow as pa
>>> custom_schema = pa.schema([ >>> custom_schema = pa.schema([
@@ -1529,7 +1529,7 @@ class AsyncConnection(object):
Data is converted to Arrow before being written to disk. For maximum Data is converted to Arrow before being written to disk. For maximum
control over how data is saved, either provide the PyArrow schema to control over how data is saved, either provide the PyArrow schema to
convert to or else provide a [PyArrow Table](pyarrow.Table) directly. convert to or else provide a [PyArrow Table][pyarrow.Table] directly.
>>> import pyarrow as pa >>> import pyarrow as pa
>>> custom_schema = pa.schema([ >>> custom_schema = pa.schema([
@@ -21,3 +21,32 @@ from .watsonx import WatsonxEmbeddings
from .voyageai import VoyageAIEmbeddingFunction from .voyageai import VoyageAIEmbeddingFunction
from .colpali import ColPaliEmbeddings from .colpali import ColPaliEmbeddings
from .siglip import SigLipEmbeddings from .siglip import SigLipEmbeddings
# The API reference renders this package with a single mkdocstrings directive,
# which only picks up names listed here. New embedding functions must be added
# to both the imports above and this list, or they will silently go undocumented.
__all__ = [
"EmbeddingFunction",
"EmbeddingFunctionConfig",
"TextEmbeddingFunction",
"EmbeddingFunctionRegistry",
"get_registry",
"register",
"SentenceTransformerEmbeddings",
"OpenAIEmbeddings",
"OpenClipEmbeddings",
"BedRockText",
"CohereEmbeddingFunction",
"GeminiText",
"GteEmbeddings",
"InstructorEmbeddingFunction",
"JinaEmbeddings",
"OllamaEmbeddings",
"TransformersEmbeddingFunction",
"ColbertEmbeddings",
"VoyageAIEmbeddingFunction",
"WatsonxEmbeddings",
"ColPaliEmbeddings",
"ImageBindEmbeddings",
"SigLipEmbeddings",
]
@@ -39,6 +39,8 @@ class CohereEmbeddingFunction(TextEmbeddingFunction):
query_input_type : str, default "search_query" query_input_type : str, default "search_query"
The input type for the query column in the database The input type for the query column in the database
Notes
-----
Cohere supports following input types: Cohere supports following input types:
| Input Type | Description | | Input Type | Description |
+32 -23
View File
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: Copyright The LanceDB Authors # SPDX-FileCopyrightText: Copyright The LanceDB Authors
from dataclasses import dataclass from dataclasses import dataclass
from typing import Literal, Optional from typing import List, Literal, Optional
from ._lancedb import ( from ._lancedb import (
IndexConfig, IndexConfig,
@@ -151,6 +151,11 @@ class FTS:
remove_stop_words : bool, default True remove_stop_words : bool, default True
Whether to remove stop words. Stop words are common words that are often Whether to remove stop words. Stop words are common words that are often
removed from text before indexing. For example, in English "the" and "and". removed from text before indexing. For example, in English "the" and "and".
custom_stop_words : list of str, optional
Custom words replace the built-in language stop words
and only take effect when ``remove_stop_words`` is True. ``None`` uses
the built-in language list, while an empty list explicitly uses no
stop words.
ascii_folding : bool, default True ascii_folding : bool, default True
Whether to fold ASCII characters. This converts accented characters to Whether to fold ASCII characters. This converts accented characters to
their ASCII equivalent. For example, "café" would be converted to "cafe". their ASCII equivalent. For example, "café" would be converted to "cafe".
@@ -179,6 +184,7 @@ class FTS:
ngram_max_length: int = 3 ngram_max_length: int = 3
prefix_only: bool = False prefix_only: bool = False
block_size: int = 128 block_size: int = 128
custom_stop_words: Optional[List[str]] = None
@dataclass @dataclass
@@ -213,7 +219,7 @@ class HnswPq:
distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their
l2 norm is 1), then dot distance is equivalent to the cosine distance. l2 norm is 1), then dot distance is equivalent to the cosine distance.
num_partitions, default sqrt(num_rows) num_partitions: int, default sqrt(num_rows)
The number of IVF partitions to create. The number of IVF partitions to create.
@@ -222,7 +228,7 @@ class HnswPq:
will require too much memory. Each partition becomes its own HNSW graph, so will require too much memory. Each partition becomes its own HNSW graph, so
setting this value higher reduces the peak memory use of training. setting this value higher reduces the peak memory use of training.
num_sub_vectors, default is vector dimension / 16 num_sub_vectors: int, default is vector dimension / 16
Number of sub-vectors of PQ. Number of sub-vectors of PQ.
@@ -244,7 +250,7 @@ class HnswPq:
This value controls how much the sub-vectors are compressed. The more bits This value controls how much the sub-vectors are compressed. The more bits
the more accurate the index but the slower search. Only 4 and 8 are supported. the more accurate the index but the slower search. Only 4 and 8 are supported.
max_iterations, default 50 max_iterations: int, default 50
Max iterations to train kmeans. Max iterations to train kmeans.
@@ -257,7 +263,7 @@ class HnswPq:
those cases it is unlikely that setting this larger will lead to the index those cases it is unlikely that setting this larger will lead to the index
converging anyways. converging anyways.
sample_rate, default 256 sample_rate: int, default 256
The rate used to calculate the number of training vectors for kmeans. The rate used to calculate the number of training vectors for kmeans.
@@ -273,14 +279,14 @@ class HnswPq:
Increasing this value might improve the quality of the index but in Increasing this value might improve the quality of the index but in
most cases the default should be sufficient. most cases the default should be sufficient.
m, default 20 m: int, default 20
The number of neighbors to select for each vector in the HNSW graph. The number of neighbors to select for each vector in the HNSW graph.
This value controls the tradeoff between search speed and accuracy. This value controls the tradeoff between search speed and accuracy.
The higher the value the more accurate the search but the slower it will be. The higher the value the more accurate the search but the slower it will be.
ef_construction, default 300 ef_construction: int, default 300
The number of candidates to evaluate during the construction of the HNSW graph. The number of candidates to evaluate during the construction of the HNSW graph.
@@ -291,7 +297,7 @@ class HnswPq:
This value should be set to a value that is not less than `ef` in the This value should be set to a value that is not less than `ef` in the
search phase. search phase.
target_partition_size, default is 1,048,576 target_partition_size: int, default is 1,048,576
The target size of each partition. The target size of each partition.
@@ -345,7 +351,7 @@ class HnswSq:
distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their
l2 norm is 1), then dot distance is equivalent to the cosine distance. l2 norm is 1), then dot distance is equivalent to the cosine distance.
num_partitions, default sqrt(num_rows) num_partitions: int, default sqrt(num_rows)
The number of IVF partitions to create. The number of IVF partitions to create.
@@ -354,7 +360,7 @@ class HnswSq:
will require too much memory. Each partition becomes its own HNSW graph, so will require too much memory. Each partition becomes its own HNSW graph, so
setting this value higher reduces the peak memory use of training. setting this value higher reduces the peak memory use of training.
max_iterations, default 50 max_iterations: int, default 50
Max iterations to train kmeans. Max iterations to train kmeans.
@@ -367,7 +373,7 @@ class HnswSq:
In those cases it is unlikely that setting this larger will lead to In those cases it is unlikely that setting this larger will lead to
the index converging anyways. the index converging anyways.
sample_rate, default 256 sample_rate: int, default 256
The rate used to calculate the number of training vectors for kmeans. The rate used to calculate the number of training vectors for kmeans.
@@ -383,14 +389,14 @@ class HnswSq:
Increasing this value might improve the quality of the index but in Increasing this value might improve the quality of the index but in
most cases the default should be sufficient. most cases the default should be sufficient.
m, default 20 m: int, default 20
The number of neighbors to select for each vector in the HNSW graph. The number of neighbors to select for each vector in the HNSW graph.
This value controls the tradeoff between search speed and accuracy. This value controls the tradeoff between search speed and accuracy.
The higher the value the more accurate the search but the slower it will be. The higher the value the more accurate the search but the slower it will be.
ef_construction, default 300 ef_construction: int, default 300
The number of candidates to evaluate during the construction of the HNSW graph. The number of candidates to evaluate during the construction of the HNSW graph.
@@ -401,7 +407,7 @@ class HnswSq:
This value should be set to a value that is not less than `ef` in the search This value should be set to a value that is not less than `ef` in the search
phase. phase.
target_partition_size, default is 1,048,576 target_partition_size: int, default is 1,048,576
The target size of each partition. The target size of each partition.
@@ -454,7 +460,7 @@ class HnswFlat:
distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their
l2 norm is 1), then dot distance is equivalent to the cosine distance. l2 norm is 1), then dot distance is equivalent to the cosine distance.
num_partitions, default sqrt(num_rows) num_partitions: int, default sqrt(num_rows)
The number of IVF partitions to create. The number of IVF partitions to create.
@@ -464,18 +470,18 @@ class HnswFlat:
graph, so setting this value higher reduces the peak memory use of graph, so setting this value higher reduces the peak memory use of
training. training.
max_iterations, default 50 max_iterations: int, default 50
Max iterations to train kmeans. Max iterations to train kmeans.
When training an IVF index we use kmeans to calculate the partitions. When training an IVF index we use kmeans to calculate the partitions.
This parameter controls how many iterations of kmeans to run. This parameter controls how many iterations of kmeans to run.
sample_rate, default 256 sample_rate: int, default 256
The rate used to calculate the number of training vectors for kmeans. The rate used to calculate the number of training vectors for kmeans.
m, default 20 m: int, default 20
The number of neighbors to select for each vector in the HNSW graph. The number of neighbors to select for each vector in the HNSW graph.
@@ -483,7 +489,7 @@ class HnswFlat:
The higher the value the more accurate the search but the slower it The higher the value the more accurate the search but the slower it
will be. will be.
ef_construction, default 300 ef_construction: int, default 300
The number of candidates to evaluate during the construction of the HNSW The number of candidates to evaluate during the construction of the HNSW
graph. graph.
@@ -495,7 +501,7 @@ class HnswFlat:
than 500. This value should be set to a value that is not less than `ef` than 500. This value should be set to a value that is not less than `ef`
in the search phase. in the search phase.
target_partition_size, default is 1,048,576 target_partition_size: int, default is 1,048,576
The target size of each partition. The target size of each partition.
""" """
@@ -599,7 +605,7 @@ class IvfFlat:
The default value is 256. The default value is 256.
target_partition_size, default is 8192 target_partition_size: int, default is 8192
The target size of each partition. The target size of each partition.
@@ -763,7 +769,7 @@ class IvfPq:
The default value is 256. The default value is 256.
target_partition_size, default is 8192 target_partition_size: int, default is 8192
The target size of each partition. The target size of each partition.
@@ -824,7 +830,7 @@ class IvfRq:
sample_rate: int, default 256 sample_rate: int, default 256
Controls the number of training vectors: sample_rate * num_partitions. Controls the number of training vectors: sample_rate * num_partitions.
target_partition_size, default is 8192 target_partition_size: int, default is 8192
Target size of each partition. Target size of each partition.
""" """
@@ -839,6 +845,9 @@ class IvfRq:
accelerator: Optional[str] = None accelerator: Optional[str] = None
# The API reference renders this module with a single mkdocstrings directive,
# which only picks up names listed here. New public names must be added to this
# list, or they will silently go undocumented.
__all__ = [ __all__ = [
"BTree", "BTree",
"IvfPq", "IvfPq",
+11 -6
View File
@@ -438,7 +438,8 @@ class Permutation:
_reader: Optional[PermutationReader] = None, _reader: Optional[PermutationReader] = None,
): ):
""" """
Internal constructor. Use [from_tables](#from_tables) instead. Internal constructor. Use
[from_tables][lancedb.permutation.Permutation.from_tables] instead.
""" """
assert base_table is not None, "base_table is required" assert base_table is not None, "base_table is required"
assert selection is not None, "selection is required" assert selection is not None, "selection is required"
@@ -985,8 +986,9 @@ class Permutation:
types. Conversion of strings, lists, and structs will require creating python types. Conversion of strings, lists, and structs will require creating python
objects and this is not zero-copy. objects and this is not zero-copy.
For custom formatting, use [with_transform](#with_transform) which overrides For custom formatting, use
this method. [with_transform][lancedb.permutation.Permutation.with_transform] which
overrides this method.
""" """
assert format is not None, "format is required" assert format is not None, "format is required"
if format == "python": if format == "python":
@@ -1061,7 +1063,8 @@ class Permutation:
Note: this method returns a new permutation and does not modify `self` Note: this method returns a new permutation and does not modify `self`
It is provided for compatibility with the huggingface Dataset API. It is provided for compatibility with the huggingface Dataset API.
Use [with_skip](#with_skip) instead to avoid confusion. Use [with_skip][lancedb.permutation.Permutation.with_skip] instead to
avoid confusion.
""" """
return self.with_skip(skip) return self.with_skip(skip)
@@ -1084,7 +1087,8 @@ class Permutation:
Note: this method returns a new permutation and does not modify `self` Note: this method returns a new permutation and does not modify `self`
It is provided for compatibility with the huggingface Dataset API. It is provided for compatibility with the huggingface Dataset API.
Use [with_take](#with_take) instead to avoid confusion. Use [with_take][lancedb.permutation.Permutation.with_take] instead to
avoid confusion.
""" """
return self.with_take(limit) return self.with_take(limit)
@@ -1107,7 +1111,8 @@ class Permutation:
Note: this method returns a new permutation and does not modify `self` Note: this method returns a new permutation and does not modify `self`
It is provided for compatibility with the huggingface Dataset API. It is provided for compatibility with the huggingface Dataset API.
Use [with_repeat](#with_repeat) instead to avoid confusion. Use [with_repeat][lancedb.permutation.Permutation.with_repeat] instead
to avoid confusion.
""" """
return self.with_repeat(times) return self.with_repeat(times)
+18 -13
View File
@@ -651,7 +651,8 @@ class Query(pydantic.BaseModel):
distance_type : Optional[str] distance_type : Optional[str]
the distance type to use for vector search the distance type to use for vector search
This can be l2 (default), cosine and dot. See [metric definitions][search] for This can be l2 (default), cosine and dot. See
[metric definitions](https://lancedb.com/docs/search/vector-search/) for
more details. more details.
If this is not a vector search this will be None. If this is not a vector search this will be None.
@@ -664,8 +665,9 @@ class Query(pydantic.BaseModel):
- A higher number makes search more accurate but also slower. - A higher number makes search more accurate but also slower.
- See discussion in [Querying an ANN Index][querying-an-ann-index] for - See discussion in
tuning advice. [Querying an ANN Index](https://lancedb.com/docs/indexing/)
for tuning advice.
Will be None if this is not a vector search. Will be None if this is not a vector search.
refine_factor : Optional[int] refine_factor : Optional[int]
@@ -673,8 +675,9 @@ class Query(pydantic.BaseModel):
- A higher number makes search more accurate but also slower. - A higher number makes search more accurate but also slower.
- See discussion in [Querying an ANN Index][querying-an-ann-index] for - See discussion in
tuning advice. [Querying an ANN Index](https://lancedb.com/docs/indexing/)
for tuning advice.
Will be None if this is not a vector search. Will be None if this is not a vector search.
lower_bound : Optional[float] lower_bound : Optional[float]
@@ -1651,8 +1654,8 @@ class LanceVectorQueryBuilder(LanceQueryBuilder):
Higher values will yield better recall (more likely to find vectors if Higher values will yield better recall (more likely to find vectors if
they exist) at the expense of latency. they exist) at the expense of latency.
See discussion in [Querying an ANN Index][querying-an-ann-index] for See discussion in [Querying an ANN Index](https://lancedb.com/docs/indexing/)
tuning advice. for tuning advice.
This method sets both the minimum and maximum number of probes to the same This method sets both the minimum and maximum number of probes to the same
value. See `minimum_nprobes` and `maximum_nprobes` for more fine-grained value. See `minimum_nprobes` and `maximum_nprobes` for more fine-grained
@@ -1752,8 +1755,8 @@ class LanceVectorQueryBuilder(LanceQueryBuilder):
As an example, a refine factor of 2 will sample 2x as many vectors as As an example, a refine factor of 2 will sample 2x as many vectors as
requested, re-ranks them, and returns the top half most relevant results. requested, re-ranks them, and returns the top half most relevant results.
See discussion in [Querying an ANN Index][querying-an-ann-index] for See discussion in [Querying an ANN Index](https://lancedb.com/docs/indexing/)
tuning advice. for tuning advice.
Parameters Parameters
---------- ----------
@@ -3379,8 +3382,9 @@ class AsyncQuery(AsyncStandardQuery):
are various ANN search parameters that will let you fine tune your recall are various ANN search parameters that will let you fine tune your recall
accuracy vs search latency. accuracy vs search latency.
Vector searches always have a [limit][]. If `limit` has not been called then Vector searches always have a
a default `limit` of 10 will be used. [limit][lancedb.query.AsyncVectorQuery.limit]. If `limit` has not been
called then a default `limit` of 10 will be used.
Typically, a single vector is passed in as the query. However, you can also Typically, a single vector is passed in as the query. However, you can also
pass in multiple vectors. When multiple vectors are passed in, if the vector pass in multiple vectors. When multiple vectors are passed in, if the vector
@@ -3511,8 +3515,9 @@ class AsyncFTSQuery(AsyncStandardQuery):
are various ANN search parameters that will let you fine tune your recall are various ANN search parameters that will let you fine tune your recall
accuracy vs search latency. accuracy vs search latency.
Hybrid searches always have a [limit][]. If `limit` has not been called then Hybrid searches always have a
a default `limit` of 10 will be used. [limit][lancedb.query.AsyncHybridQuery.limit]. If `limit` has not been
called then a default `limit` of 10 will be used.
Typically, a single vector is passed in as the query. However, you can also Typically, a single vector is passed in as the query. However, you can also
pass in multiple vectors. This can be useful if you want to find the nearest pass in multiple vectors. This can be useful if you want to find the nearest
+3
View File
@@ -11,6 +11,9 @@ from lancedb import __version__
from .header import HeaderProvider from .header import HeaderProvider
from .oauth import OAuthConfig, OAuthFlowType from .oauth import OAuthConfig, OAuthFlowType
# The API reference renders this module with a single mkdocstrings directive,
# which only picks up names listed here. New public names must be added to this
# list, or they will silently go undocumented.
__all__ = [ __all__ = [
"TimeoutConfig", "TimeoutConfig",
"RetryConfig", "RetryConfig",
+2 -2
View File
@@ -53,9 +53,9 @@ class RetryError(LanceDBClientError):
"""An error that occurs when the client has exceeded the maximum number of retries. """An error that occurs when the client has exceeded the maximum number of retries.
The retry strategy can be adjusted by setting the The retry strategy can be adjusted by setting the
[retry_config](lancedb.remote.ClientConfig.retry_config) in the client [retry_config][lancedb.remote.ClientConfig.retry_config] in the client
configuration. This is passed in the `client_config` argument of configuration. This is passed in the `client_config` argument of
[connect](lancedb.connect) and [connect_async](lancedb.connect_async). [connect][lancedb.connect] and [connect_async][lancedb.connect_async].
The __cause__ attribute of this exception will be the last exception that The __cause__ attribute of this exception will be the last exception that
caused the retry to fail. It will be an caused the retry to fail. It will be an
+7 -3
View File
@@ -340,6 +340,7 @@ class RemoteTable(Table):
lower_case: bool = True, lower_case: bool = True,
stem: bool = True, stem: bool = True,
remove_stop_words: bool = True, remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True, ascii_folding: bool = True,
ngram_min_length: int = 3, ngram_min_length: int = 3,
ngram_max_length: int = 3, ngram_max_length: int = 3,
@@ -361,6 +362,7 @@ class RemoteTable(Table):
lower_case=lower_case, lower_case=lower_case,
stem=stem, stem=stem,
remove_stop_words=remove_stop_words, remove_stop_words=remove_stop_words,
custom_stop_words=custom_stop_words,
ascii_folding=ascii_folding, ascii_folding=ascii_folding,
ngram_min_length=ngram_min_length, ngram_min_length=ngram_min_length,
ngram_max_length=ngram_max_length, ngram_max_length=ngram_max_length,
@@ -578,8 +580,9 @@ class RemoteTable(Table):
progress: Optional[Union[bool, Callable, Any]] = None, progress: Optional[Union[bool, Callable, Any]] = None,
write_parallelism: Optional[int] = None, write_parallelism: Optional[int] = None,
) -> AddResult: ) -> AddResult:
"""Add more data to the [Table](Table). It has the same API signature as """Add more data to the [Table][lancedb.table.Table].
the OSS version.
It has the same API signature as the OSS version.
Parameters Parameters
---------- ----------
@@ -639,7 +642,8 @@ class RemoteTable(Table):
fast_search: bool = False, fast_search: bool = False,
) -> LanceVectorQueryBuilder: ) -> LanceVectorQueryBuilder:
"""Create a search query to find the nearest neighbors """Create a search query to find the nearest neighbors
of the given query vector. We currently support [vector search][search] of the given query vector. We currently support
[vector search](https://lancedb.com/docs/search/vector-search/)
All query options are defined in All query options are defined in
[LanceVectorQueryBuilder][lancedb.query.LanceVectorQueryBuilder]. [LanceVectorQueryBuilder][lancedb.query.LanceVectorQueryBuilder].
@@ -14,6 +14,9 @@ from .answerdotai import AnswerdotaiRerankers
from .voyageai import VoyageAIReranker from .voyageai import VoyageAIReranker
from .watsonx import WatsonxReranker from .watsonx import WatsonxReranker
# The API reference renders this module with a single mkdocstrings directive,
# which only picks up names listed here. New public names must be added to this
# list, or they will silently go undocumented.
__all__ = [ __all__ = [
"Reranker", "Reranker",
"CrossEncoderReranker", "CrossEncoderReranker",
+32 -18
View File
@@ -1103,6 +1103,7 @@ class Table(ABC):
lower_case: bool = True, lower_case: bool = True,
stem: bool = True, stem: bool = True,
remove_stop_words: bool = True, remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True, ascii_folding: bool = True,
ngram_min_length: int = 3, ngram_min_length: int = 3,
ngram_max_length: int = 3, ngram_max_length: int = 3,
@@ -1170,6 +1171,9 @@ class Table(ABC):
remove_stop_words : bool, default True remove_stop_words : bool, default True
Whether to remove stop words. Stop words are common words that are often Whether to remove stop words. Stop words are common words that are often
removed from text before indexing. For example, in English "the" and "and". removed from text before indexing. For example, in English "the" and "and".
custom_stop_words : list of str, optional
Custom words that replace the built-in language stop words. ``None``
uses the built-in list; an empty list explicitly uses no stop words.
ascii_folding : bool, default True ascii_folding : bool, default True
Whether to fold ASCII characters. This converts accented characters to Whether to fold ASCII characters. This converts accented characters to
their ASCII equivalent. For example, "café" would be converted to "cafe". their ASCII equivalent. For example, "café" would be converted to "cafe".
@@ -1207,7 +1211,7 @@ class Table(ABC):
progress: Optional[Union[bool, Callable, Any]] = None, progress: Optional[Union[bool, Callable, Any]] = None,
write_parallelism: Optional[int] = None, write_parallelism: Optional[int] = None,
) -> AddResult: ) -> AddResult:
"""Add more data to the [Table](Table). """Add more data to the [Table][lancedb.table.Table].
Parameters Parameters
---------- ----------
@@ -1339,8 +1343,8 @@ class Table(ABC):
fts_columns: Optional[Union[str, List[str]]] = None, fts_columns: Optional[Union[str, List[str]]] = None,
) -> LanceQueryBuilder: ) -> LanceQueryBuilder:
"""Create a search query to find the nearest neighbors """Create a search query to find the nearest neighbors
of the given query vector. We currently support [vector search][search] of the given query vector. We currently support [vector search](https://lancedb.com/docs/search/vector-search/)
and [full-text search][experimental-full-text-search]. and [full-text search](https://lancedb.com/docs/search/full-text-search/).
All query options are defined in All query options are defined in
[LanceQueryBuilder][lancedb.query.LanceQueryBuilder]. [LanceQueryBuilder][lancedb.query.LanceQueryBuilder].
@@ -1774,7 +1778,7 @@ class Table(ABC):
for faster reads. for faster reads.
Arguments are passed onto Lance's Arguments are passed onto Lance's
[compact_files][lance.dataset.DatasetOptimizer.compact_files]. `lance.dataset.DatasetOptimizer.compact_files`.
For most cases, the default should be fine. For most cases, the default should be fine.
See Also See Also
@@ -1828,6 +1832,8 @@ class Table(ABC):
retrain: bool, default False retrain: bool, default False
This parameter is no longer used and is deprecated. This parameter is no longer used and is deprecated.
Notes
-----
The frequency an application should call optimize is based on the frequency of The frequency an application should call optimize is based on the frequency of
data modifications. If data is frequently added, deleted, or updated then data modifications. If data is frequently added, deleted, or updated then
optimize should be run frequently. A good rule of thumb is to run optimize if optimize should be run frequently. A good rule of thumb is to run optimize if
@@ -1982,15 +1988,14 @@ class Table(ABC):
change permanent you can use the `[Self::restore]` method. change permanent you can use the `[Self::restore]` method.
Any operation that modifies the table will fail while the table is in a checked Any operation that modifies the table will fail while the table is in a checked
out state. out state. To return the table to a normal state use
`[Self::checkout_latest]`.
Parameters Parameters
---------- ----------
version: int | str, version: int | str,
The version to check out. A version number (`int`) or a tag The version to check out. A version number (`int`) or a tag
(`str`) can be provided. (`str`) can be provided.
To return the table to a normal state use `[Self::checkout_latest]`
""" """
@abstractmethod @abstractmethod
@@ -3055,6 +3060,7 @@ class LanceTable(Table):
lower_case: bool = True, lower_case: bool = True,
stem: bool = True, stem: bool = True,
remove_stop_words: bool = True, remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True, ascii_folding: bool = True,
ngram_min_length: int = 3, ngram_min_length: int = 3,
ngram_max_length: int = 3, ngram_max_length: int = 3,
@@ -3101,6 +3107,7 @@ class LanceTable(Table):
"lower_case": lower_case, "lower_case": lower_case,
"stem": stem, "stem": stem,
"remove_stop_words": remove_stop_words, "remove_stop_words": remove_stop_words,
"custom_stop_words": custom_stop_words,
"ascii_folding": ascii_folding, "ascii_folding": ascii_folding,
"ngram_min_length": ngram_min_length, "ngram_min_length": ngram_min_length,
"ngram_max_length": ngram_max_length, "ngram_max_length": ngram_max_length,
@@ -3108,6 +3115,7 @@ class LanceTable(Table):
} }
else: else:
tokenizer_configs = self.infer_tokenizer_configs(tokenizer_name) tokenizer_configs = self.infer_tokenizer_configs(tokenizer_name)
tokenizer_configs["custom_stop_words"] = custom_stop_words
config = FTS(block_size=block_size, **tokenizer_configs) config = FTS(block_size=block_size, **tokenizer_configs)
@@ -3380,8 +3388,8 @@ class LanceTable(Table):
fts_columns: Optional[Union[str, List[str]]] = None, fts_columns: Optional[Union[str, List[str]]] = None,
) -> LanceQueryBuilder: ) -> LanceQueryBuilder:
"""Create a search query to find the nearest neighbors """Create a search query to find the nearest neighbors
of the given query vector. We currently support [vector search][search] of the given query vector. We currently support [vector search](https://lancedb.com/docs/search/vector-search/)
and [full-text search][search]. and [full-text search](https://lancedb.com/docs/search/full-text-search/).
Examples Examples
-------- --------
@@ -3411,8 +3419,9 @@ class LanceTable(Table):
- *default None*. - *default None*.
Acceptable types are: list, np.ndarray, PIL.Image.Image Acceptable types are: list, np.ndarray, PIL.Image.Image
- If None then the select/[where][sql]/limit clauses are applied - If None then the
to filter the table select/[where][lancedb.query.LanceQueryBuilder.where]/limit clauses
are applied to filter the table
vector_column_name: str, optional vector_column_name: str, optional
The name of the vector column to search. The name of the vector column to search.
@@ -3806,6 +3815,8 @@ class LanceTable(Table):
retrain: bool, default False retrain: bool, default False
This parameter is no longer used and is deprecated. This parameter is no longer used and is deprecated.
Notes
-----
The frequency an application should call optimize is based on the frequency of The frequency an application should call optimize is based on the frequency of
data modifications. If data is frequently added, deleted, or updated then data modifications. If data is frequently added, deleted, or updated then
optimize should be run frequently. A good rule of thumb is to run optimize if optimize should be run frequently. A good rule of thumb is to run optimize if
@@ -4684,7 +4695,7 @@ class AsyncTable:
Parameters Parameters
---------- ----------
**kwargs **kwargs
Forwarded to [`lance.dataset`][lance.dataset]. Forwarded to `lance.dataset`.
Returns Returns
------- -------
@@ -5003,7 +5014,7 @@ class AsyncTable:
progress: Optional[Union[bool, Callable, Any]] = None, progress: Optional[Union[bool, Callable, Any]] = None,
write_parallelism: Optional[int] = None, write_parallelism: Optional[int] = None,
) -> AddResult: ) -> AddResult:
"""Add more data to the [Table](Table). """Add more data to the [AsyncTable][lancedb.table.AsyncTable].
Parameters Parameters
---------- ----------
@@ -5205,8 +5216,8 @@ class AsyncTable:
fts_columns: Optional[Union[str, List[str]]] = None, fts_columns: Optional[Union[str, List[str]]] = None,
) -> Union[AsyncHybridQuery, AsyncFTSQuery, AsyncVectorQuery]: ) -> Union[AsyncHybridQuery, AsyncFTSQuery, AsyncVectorQuery]:
"""Create a search query to find the nearest neighbors """Create a search query to find the nearest neighbors
of the given query vector. We currently support [vector search][search] of the given query vector. We currently support [vector search](https://lancedb.com/docs/search/vector-search/)
and [full-text search][experimental-full-text-search]. and [full-text search](https://lancedb.com/docs/search/full-text-search/).
All query options are defined in [AsyncQuery][lancedb.query.AsyncQuery]. All query options are defined in [AsyncQuery][lancedb.query.AsyncQuery].
@@ -5767,15 +5778,14 @@ class AsyncTable:
change permanent you can use the `[Self::restore]` method. change permanent you can use the `[Self::restore]` method.
Any operation that modifies the table will fail while the table is in a checked Any operation that modifies the table will fail while the table is in a checked
out state. out state. To return the table to a normal state use
`[Self::checkout_latest]`.
Parameters Parameters
---------- ----------
version: int | str, version: int | str,
The version to check out. A version number (`int`) or a tag The version to check out. A version number (`int`) or a tag
(`str`) can be provided. (`str`) can be provided.
To return the table to a normal state use `[Self::checkout_latest]`
""" """
try: try:
await self._inner.checkout(version) await self._inner.checkout(version)
@@ -5959,6 +5969,8 @@ class AsyncTable:
retrain: bool, default False retrain: bool, default False
This parameter is no longer used and is deprecated. This parameter is no longer used and is deprecated.
Notes
-----
The frequency an application should call optimize is based on the frequency of The frequency an application should call optimize is based on the frequency of
data modifications. If data is frequently added, deleted, or updated then data modifications. If data is frequently added, deleted, or updated then
optimize should be run frequently. A good rule of thumb is to run optimize if optimize should be run frequently. A good rule of thumb is to run optimize if
@@ -6339,6 +6351,8 @@ class Branches:
dry_run: bool, default False dry_run: bool, default False
When True, only preview. When False, attempt the merge. When True, only preview. When False, attempt the merge.
Notes
-----
A rejected merge returns ``status="rejected"`` instead of raising. A rejected merge returns ``status="rejected"`` instead of raising.
""" """
return LOOP.run(self._table.branches.merge(from_branch, dry_run)) return LOOP.run(self._table.branches.merge(from_branch, dry_run))
+20
View File
@@ -219,11 +219,13 @@ def test_create_inverted_index(table, with_position):
table.create_fts_index( table.create_fts_index(
"text", "text",
with_position=with_position, with_position=with_position,
custom_stop_words=["puppy"],
name="custom_fts_index", name="custom_fts_index",
) )
indices = table.list_indices() indices = table.list_indices()
fts_indices = [i for i in indices if i.index_type == "FTS"] fts_indices = [i for i in indices if i.index_type == "FTS"]
assert any(i.name == "custom_fts_index" for i in fts_indices) assert any(i.name == "custom_fts_index" for i in fts_indices)
assert fts_indices[0].index_details["custom_stop_words"] == ["puppy"]
@pytest.mark.parametrize("block_size", [128, 256]) @pytest.mark.parametrize("block_size", [128, 256])
@@ -243,6 +245,24 @@ def test_create_inverted_index_rejects_invalid_block_size(table):
table.create_index("text", config=FTS(block_size=129)) table.create_index("text", config=FTS(block_size=129))
def test_custom_stop_words_list(table):
table.create_index(
"text",
config=FTS(stem=False, custom_stop_words=["lance"]),
)
assert table.list_indices()[0].index_details["custom_stop_words"] == ["lance"]
tokens = table.tokenize("the lance data", column="text")
assert [token.text for token in tokens] == ["the", "data"]
empty_tokens = ldb.tokenize("the lance data", stem=False, custom_stop_words=[])
assert [token.text for token in empty_tokens] == ["the", "lance", "data"]
with pytest.raises(TypeError, match=r"custom_stop_words.*int"):
ldb.tokenize(
"the lance data",
custom_stop_words=["lance", 42],
)
def test_search_fts(table): def test_search_fts(table):
table.create_fts_index("text") table.create_fts_index("text")
results = table.search("puppy").select(["id", "text"]).limit(5).to_list() results = table.search("puppy").select(["id", "text"]).limit(5).to_list()
+2
View File
@@ -771,6 +771,7 @@ def test_table_create_indices():
"text", "text",
wait_timeout=timedelta(seconds=2), wait_timeout=timedelta(seconds=2),
block_size=256, block_size=256,
custom_stop_words=["cloud"],
name="custom_fts_idx", name="custom_fts_idx",
) )
@@ -795,6 +796,7 @@ def test_table_create_indices():
assert "name" in fts_req assert "name" in fts_req
assert fts_req["name"] == "custom_fts_idx" assert fts_req["name"] == "custom_fts_idx"
assert fts_req["block_size"] == 256 assert fts_req["block_size"] == 256
assert fts_req["custom_stop_words"] == ["cloud"]
# Check vector index request has custom name # Check vector index request has custom name
vector_req = received_requests[2] vector_req = received_requests[2]
+3 -1
View File
@@ -59,7 +59,8 @@ pub fn extract_index_params(source: &Option<Bound<'_, PyAny>>) -> PyResult<Lance
.ascii_folding(params.ascii_folding) .ascii_folding(params.ascii_folding)
.ngram_min_length(params.ngram_min_length) .ngram_min_length(params.ngram_min_length)
.ngram_max_length(params.ngram_max_length) .ngram_max_length(params.ngram_max_length)
.ngram_prefix_only(params.prefix_only); .ngram_prefix_only(params.prefix_only)
.custom_stop_words(params.custom_stop_words);
let inner_opts = inner_opts let inner_opts = inner_opts
.block_size(params.block_size) .block_size(params.block_size)
.map_err(|err| PyValueError::new_err(err.to_string()))?; .map_err(|err| PyValueError::new_err(err.to_string()))?;
@@ -206,6 +207,7 @@ struct FtsParams {
lower_case: bool, lower_case: bool,
stem: bool, stem: bool,
remove_stop_words: bool, remove_stop_words: bool,
custom_stop_words: Option<Vec<String>>,
ascii_folding: bool, ascii_folding: bool,
ngram_min_length: u32, ngram_min_length: u32,
ngram_max_length: u32, ngram_max_length: u32,
+4 -1
View File
@@ -520,6 +520,7 @@ impl From<LanceDbFtsToken> for FtsToken {
lower_case = true, lower_case = true,
stem = true, stem = true,
remove_stop_words = true, remove_stop_words = true,
custom_stop_words = None,
ascii_folding = true, ascii_folding = true,
ngram_min_length = 3, ngram_min_length = 3,
ngram_max_length = 3, ngram_max_length = 3,
@@ -534,6 +535,7 @@ pub fn tokenize(
lower_case: bool, lower_case: bool,
stem: bool, stem: bool,
remove_stop_words: bool, remove_stop_words: bool,
custom_stop_words: Option<Vec<String>>,
ascii_folding: bool, ascii_folding: bool,
ngram_min_length: u32, ngram_min_length: u32,
ngram_max_length: u32, ngram_max_length: u32,
@@ -555,7 +557,8 @@ pub fn tokenize(
.ascii_folding(ascii_folding) .ascii_folding(ascii_folding)
.ngram_min_length(ngram_min_length) .ngram_min_length(ngram_min_length)
.ngram_max_length(ngram_max_length) .ngram_max_length(ngram_max_length)
.ngram_prefix_only(prefix_only); .ngram_prefix_only(prefix_only)
.custom_stop_words(custom_stop_words);
let tokens = lancedb_tokenize(&query, &params).infer_error()?; let tokens = lancedb_tokenize(&query, &params).infer_error()?;
Ok(tokens.into_iter().map(FtsToken::from).collect()) Ok(tokens.into_iter().map(FtsToken::from).collect())
} }
+6 -1
View File
@@ -76,7 +76,12 @@ async fn create_table(db: &Connection) -> Result<Table> {
async fn create_index(table: &Table) -> Result<()> { async fn create_index(table: &Table) -> Result<()> {
table table
.create_index(&["doc"], Index::FTS(FtsIndexBuilder::default())) .create_index(
&["doc"],
Index::FTS(
FtsIndexBuilder::default().custom_stop_words(Some(vec!["example".to_owned()])),
),
)
.execute() .execute()
.await?; .await?;
Ok(()) Ok(())
+15 -5
View File
@@ -4553,6 +4553,19 @@ mod tests {
}, },
Index::FTS(InvertedIndexParams::default().block_size(256).unwrap()), Index::FTS(InvertedIndexParams::default().block_size(256).unwrap()),
), ),
(
"FTS",
{
let mut body = serde_json::to_value(InvertedIndexParams::default()).unwrap();
body["custom_stop_words"] = json!(["cat", " cat ", "CAT"]);
body
},
Index::FTS(InvertedIndexParams::default().custom_stop_words(Some(vec![
"cat".to_string(),
" cat ".to_string(),
"CAT".to_string(),
]))),
),
]; ];
for (index_type, expected_body, index) in cases { for (index_type, expected_body, index) in cases {
@@ -5084,8 +5097,9 @@ mod tests {
"max_token_length": 40, "max_token_length": 40,
"lower_case": true, "lower_case": true,
"stem": false, "stem": false,
"remove_stop_words": false, "remove_stop_words": true,
"ascii_folding": true, "ascii_folding": true,
"custom_stop_words": ["hello"],
}) })
.to_string(); .to_string();
let table = Table::new_with_handler("my_table", move |request| { let table = Table::new_with_handler("my_table", move |request| {
@@ -5123,10 +5137,6 @@ mod tests {
assert_eq!( assert_eq!(
tokens, tokens,
vec![ vec![
FtsToken {
text: "hello".to_string(),
position: 0,
},
FtsToken { FtsToken {
text: "こんにちは".to_string(), text: "こんにちは".to_string(),
position: 1, position: 1,
+38 -7
View File
@@ -21,6 +21,7 @@ use lance::dataset::WriteMode;
use lance::dataset::builder::DatasetBuilder; use lance::dataset::builder::DatasetBuilder;
use lance::dataset::{InsertBuilder, WriteParams}; use lance::dataset::{InsertBuilder, WriteParams};
use lance::index::DatasetIndexExt; use lance::index::DatasetIndexExt;
use lance::index::scalar::load_segment_params;
use lance::io::{ObjectStoreParams, WrappingObjectStore}; use lance::io::{ObjectStoreParams, WrappingObjectStore};
use lance_datafusion::utils::StreamingWriteSource; use lance_datafusion::utils::StreamingWriteSource;
use lance_index::IndexCriteria; use lance_index::IndexCriteria;
@@ -3193,10 +3194,9 @@ impl BaseTable for NativeTable {
async fn list_indices(&self) -> Result<Vec<IndexConfig>> { async fn list_indices(&self) -> Result<Vec<IndexConfig>> {
let dataset = self.dataset.get().await?; let dataset = self.dataset.get().await?;
let total_rows = dataset.count_rows(None).await? as u64; let total_rows = dataset.count_rows(None).await? as u64;
let indices = dataset let descriptions = dataset.describe_indices(None).await?;
.describe_indices(None) let mut indices: Vec<IndexConfig> = descriptions
.await? .iter()
.into_iter()
.filter_map(|idx_desc| { .filter_map(|idx_desc| {
let index_type: crate::index::IndexType = idx_desc let index_type: crate::index::IndexType = idx_desc
.index_type() .index_type()
@@ -3254,6 +3254,31 @@ impl BaseTable for NativeTable {
}) })
}) })
.collect(); .collect();
for index in indices
.iter_mut()
.filter(|index| index.index_type == crate::index::IndexType::FTS)
{
let Some(description) = descriptions
.iter()
.find(|description| description.name() == index.name)
else {
continue;
};
let segments = description.segments();
let Some(segment) = segments.first() else {
continue;
};
let params = load_segment_params(&dataset, segment).await?;
let details = serde_json::to_string(&params).map_err(|source| Error::Other {
message: format!(
"Failed to serialize full text search configuration for index '{}'",
index.name
),
source: Some(Box::new(source)),
})?;
index.index_details = Some(details);
}
Ok(indices) Ok(indices)
} }
@@ -4111,10 +4136,10 @@ mod tests {
Box::new(RecordBatchIterator::new(vec![Ok(batch)], schema)) Box::new(RecordBatchIterator::new(vec![Ok(batch)], schema))
} }
// Windows does not support precise sleep durations due to timer resolution limitations.
#[cfg(not(target_os = "windows"))]
#[tokio::test] #[tokio::test]
async fn test_read_consistency_interval() { async fn test_read_consistency_interval() {
use crate::utils::background_cache::clock;
let intervals = vec![ let intervals = vec![
None, None,
Some(0), Some(0),
@@ -4141,6 +4166,12 @@ mod tests {
let conn2 = conn2.execute().await.unwrap(); let conn2 = conn2.execute().await.unwrap();
let table2 = conn2.open_table("my_table").execute().await.unwrap(); let table2 = conn2.open_table("my_table").execute().await.unwrap();
// Freeze the consistency clock now that `table2` has seeded its cache, so the
// interval only elapses when this test advances it. Otherwise the write and
// count_rows calls below race the real 100ms interval, which a loaded CI
// runner loses. Must come after open_table: creating the cache clears the mock.
clock::pin();
assert_eq!(table1.count_rows(None).await.unwrap(), 0); assert_eq!(table1.count_rows(None).await.unwrap(), 0);
assert_eq!(table2.count_rows(None).await.unwrap(), 0); assert_eq!(table2.count_rows(None).await.unwrap(), 0);
@@ -4158,7 +4189,7 @@ mod tests {
} }
Some(100) => { Some(100) => {
assert_eq!(table2.count_rows(None).await.unwrap(), 0); assert_eq!(table2.count_rows(None).await.unwrap(), 0);
tokio::time::sleep(Duration::from_millis(100)).await; clock::advance_by(Duration::from_millis(100));
assert_eq!(table2.count_rows(None).await.unwrap(), 1); assert_eq!(table2.count_rows(None).await.unwrap(), 1);
} }
_ => unreachable!(), _ => unreachable!(),
+35 -1
View File
@@ -1366,11 +1366,19 @@ mod tests {
table table
.create_index( .create_index(
&["text"], &["text"],
Index::FTS(FtsIndexBuilder::default().block_size(256).unwrap()), Index::FTS(
FtsIndexBuilder::default()
.stem(false)
.custom_stop_words(Some(vec!["cat".to_string()]))
.block_size(256)
.unwrap(),
),
) )
.execute() .execute()
.await .await
.unwrap(); .unwrap();
drop(table);
let table = conn.open_table("test_bitmap").execute().await.unwrap();
let index_configs = table.list_indices().await.unwrap(); let index_configs = table.list_indices().await.unwrap();
assert_eq!(index_configs.len(), 1); assert_eq!(index_configs.len(), 1);
let index = index_configs.into_iter().next().unwrap(); let index = index_configs.into_iter().next().unwrap();
@@ -1381,6 +1389,32 @@ mod tests {
let index_params: FtsIndexBuilder = let index_params: FtsIndexBuilder =
serde_json::from_str(index.index_details.as_deref().unwrap()).unwrap(); serde_json::from_str(index.index_details.as_deref().unwrap()).unwrap();
assert_eq!(index_params.posting_block_size(), 256); assert_eq!(index_params.posting_block_size(), 256);
assert_eq!(
serde_json::to_value(&index_params).unwrap()["custom_stop_words"],
serde_json::json!(["cat"])
);
assert_eq!(
table
.tokenize("cat dog", "text_idx")
.await
.unwrap()
.into_iter()
.map(|token| token.text)
.collect::<Vec<_>>(),
vec!["dog"]
);
let batches = table
.query()
.full_text_search(FullTextSearchQuery::new("cat dog".to_string()))
.limit(120)
.execute()
.await
.unwrap()
.try_collect::<Vec<_>>()
.await
.unwrap();
assert_eq!(batches.iter().map(RecordBatch::num_rows).sum::<usize>(), 40);
let num_rows = 120; let num_rows = 120;
let stats = table.index_stats("text_idx").await.unwrap().unwrap(); let stats = table.index_stats("text_idx").await.unwrap().unwrap();