fix(node): reject unsupported FTS languages without panicking (#4369)

`Index.fts({ language: "english" })` and other unsupported language
values abort the Node process because the native factory unwraps the
error returned by `FtsIndexBuilder::language`.

Convert that error into a catchable JavaScript `Error`, using the same
message as `tokenize` and Python. Add regression coverage for
`"Klingon"` and lowercase `"english"`, plus a valid English stemming and
search case across Arrow 15–18.

Validation:
- Native and TypeScript builds: `pnpm build:debug --profile ci` and
`pnpm tsc`.
- Full-text and tokenization tests: `pnpm test __test__/table.test.ts
--runInBand --testNamePattern 'full text|FTS|tokeniz'` — 69 passed.
- Rust formatting and lint: `cargo fmt --all`; `cargo clippy --profile
ci --target x86_64-unknown-linux-gnu -p lancedb-nodejs -- -D warnings`.
- TypeScript formatting and lint: `pnpm exec biome format --write
__test__/table.test.ts` and `pnpm lint`; `pnpm run docs` passed without
generated changes.

Fixes #4367

<!-- lance-gatekeeper-fix:v1 agent=271bb96150580678f4e03b8e36beca03
generation=1 -->

Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com>
This commit is contained in:
lancedb-gatefixer[bot]
2026-10-08 19:45:09 -07:00
committed by GitHub
parent 920b1e7bbb
commit e2899e4dea
2 changed files with 28 additions and 1 deletions
+22
View File
@@ -4050,6 +4050,28 @@ describe.each(arrowVersions)(
);
});
test.each(["Klingon", "english"])(
"rejects unsupported full text language %s with a catchable error",
(language) => {
expect(() => Index.fts({ language })).toThrow(
new Error(
`LanceDB does not support the requested language: '${language}'`,
),
);
},
);
test("full text search with a supported language", async () => {
const db = await connect(tmpDir.name);
const table = await db.createTable("test", [{ text: "running" }]);
await table.createIndex("text", {
config: Index.fts({ language: "English", stem: true }),
});
const results = await table.search("run").toArray();
expect(results.map((row) => row.text)).toEqual(["running"]);
});
test("full text search without lowercase", async () => {
const db = await connect(tmpDir.name);
const data = [
+6 -1
View File
@@ -239,7 +239,12 @@ impl Index {
opts = opts.base_tokenizer(base_tokenizer);
}
if let Some(language) = language {
opts = opts.language(&language).unwrap();
opts = opts.language(&language).map_err(|_| {
napi::Error::from_reason(format!(
"LanceDB does not support the requested language: '{}'",
language
))
})?;
}
if let Some(max_token_length) = max_token_length {
opts = opts.max_token_length(Some(max_token_length as usize));