Files
lancedb/docs/web-tests/ts/quickstart.test.ts
T
Jack YeandClaude Sonnet 5 de95ed3f6d docs: overhaul curation, feature engineering, security and deployment guides (#4748)
A broad documentation pass across the areas LanceDB Enterprise and
Embedded
diverge on most:

**Data Curation** -- GPU Function execution, Slurm scheduling (and what
Slurm is, for readers new to it), blob late materialization as its own
page, joins, aggregation/profiling, sampling, near-duplicate detection,
chunking, distributed shuffle, and IVF-partition clustering.

**Feature Engineering** -- computed columns and materialized views,
restructured to lead with the Function-based path and show the SQL form
side by side with Python throughout; a single Auto Refresh page instead
of
duplicating scheduling details on every refresh page; adaptive
execution;
checkpointing; GPU and Slurm placement for refresh workers; a Training
Cache page for the PyTorch data loader's automatic cache benefit.

**Security & Governance** -- an Audit Log reference (schema, access,
grants), Secrets promoted to an ordinary catalog object with their own
SQL
DDL and a vending/rotation guide, and privilege-table fixes verified
against the authorization source.

**Deployment & Maintenance** -- Horizontal Scaling and OpenTelemetry
moved
into a real Deployment area; Automated Cleanup folded into Intelligent
Auto-Maintenance (every other maintenance decision already lived there);
RTE Compaction and Advanced Conflict Resolution documented in depth.

**Landing page** -- the Enterprise feature comparison table reorganized
(several redundant or stale rows removed, several new areas added), and
the
"why Enterprise" bullets replaced with ones tied to a concrete page
(Performance, Scale, Fault tolerance, Multicloud deployment, Simplify
operations) instead of vague promises.

Plus a full pass addressing lancedb-gatekeeper's review: a real
test-safety
bug (a quickstart test could silently overwrite and delete a
deployment's
own `frames` table), a fabricated SQL statement with no backing
implementation, an actual dedup-guarantee bug in `take_row_ids`, and
dozens
of documentation-accuracy fixes verified against source rather than
taken
on faith -- each either fixed, or left with an explicit note on why it
needs a feature or deployment owner's sign-off instead of being guessed
at.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_018PXMCbRH6i5w47TsjeEUMT

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-03 16:17:24 -08:00

212 lines
6.9 KiB
TypeScript

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
import { describe, expect, test } from "@jest/globals";
import { randomUUID } from "node:crypto";
import * as fs from "node:fs";
import * as path from "node:path";
import * as lancedb from "@lancedb/lancedb";
import { withTempDirectory } from "./util.ts";
// What the quickstart's download step leaves in an application's directory.
const IMAGE = path.join(
__dirname,
"../../web/static/assets/images/quickstart/sir-lancelot.jpg",
);
const deployment = ["LANCEDB_URI", "LANCEDB_HOST_OVERRIDE", "LANCEDB_API_KEY"];
// The connection settings alone never send these tests to a deployment.
const optIn = "LANCEDB_DOCS_TEST_DEPLOYMENT";
const connections =
process.env[optIn] === "1" ? ["local", "enterprise"] : ["local"];
async function tableNames(db: lancedb.Connection) {
const names: string[] = [];
for await (const name of db.listTables()) names.push(name);
return names;
}
// A deployment connection on which a test creates only tables of its own:
// each name gets a suffix unique to the test and is claimed only while no
// table has it; the test drops what it claimed, whether or not it passed.
async function withOwnedTables(
db: lancedb.Connection,
fn: (db: lancedb.Connection) => Promise<void>,
) {
const suffix = randomUUID().replaceAll("-", "").slice(0, 12);
const claimed: string[] = [];
const createTable = async (
name: string,
data: Parameters<lancedb.Connection["createTable"]>[1],
options: Partial<lancedb.CreateTableOptions> = {},
) => {
const owned = `${name}_${suffix}`;
let mode = options.mode;
if (!claimed.includes(owned)) {
if ((await tableNames(db)).includes(owned)) {
throw new Error(`${owned} already exists and is not this test's`);
}
claimed.push(owned);
mode = "create";
}
return db.createTable(owned, data, { ...options, mode });
};
try {
await fn(
new Proxy(db, {
get: (target, property, receiver) =>
property === "createTable"
? createTable
: Reflect.get(target, property, receiver),
}),
);
} finally {
const existing = await tableNames(db);
for (const name of claimed) {
if (existing.includes(name)) {
await db.dropTable(name);
}
}
}
}
async function withDatabase(
connection: string,
fn: (db: lancedb.Connection) => Promise<void>,
) {
if (connection === "enterprise") {
const missing = deployment.filter((name) => !process.env[name]);
if (missing.length) {
throw new Error(`${optIn}=1 needs ${missing.join(", ")}`);
}
const db = await lancedb.connect(process.env.LANCEDB_URI!, {
apiKey: process.env.LANCEDB_API_KEY!,
region: process.env.LANCEDB_REGION ?? "us-east-1",
hostOverride: process.env.LANCEDB_HOST_OVERRIDE!,
});
await withOwnedTables(db, fn);
return;
}
await withTempDirectory(async (databaseDir) => {
await fn(await lancedb.connect(databaseDir));
});
}
async function createFrames(db: lancedb.Connection) {
// --8<-- [start:quickstart_data]
const data = [
{
frame_id: "1",
video_id: "clip-1",
caption: "a robot arm reaching toward a cup",
stats: { motion: 3, sharpness: 4, exposure: 4, framing: 3 },
embedding: [0.7, 0.1, 0.9, 0.7],
},
{
frame_id: "2",
video_id: "clip-1",
caption: "a two-finger gripper closing on a cup",
stats: { motion: 5, sharpness: 4, exposure: 5, framing: 2 },
embedding: [0.2, 0.9, 0.4, 0.9],
},
{
frame_id: "3",
video_id: "clip-2",
caption: "a robot arm holding a cup steady",
stats: { motion: 2, sharpness: 3, exposure: 3, framing: 4 },
embedding: [0.9, 0.1, 0.5, 0.4],
},
];
// --8<-- [end:quickstart_data]
// --8<-- [start:quickstart_create_table]
const table = await db.createTable("frames", data, { mode: "overwrite" });
// --8<-- [end:quickstart_create_table]
expect(await table.countRows()).toBe(3);
// --8<-- [start:quickstart_add_data]
await table.add([
{
frame_id: "4",
video_id: "clip-2",
caption: "a gripper lowering a cup onto the table",
stats: { motion: 5, sharpness: 4, exposure: 4, framing: 2 },
embedding: [0.3, 0.9, 0.6, 0.8],
},
]);
// --8<-- [end:quickstart_add_data]
expect(await table.countRows()).toBe(4);
return table;
}
describe.each(connections)("quickstart (%s)", (connection) => {
test("create, add and search", async () => {
await withDatabase(connection, async (db) => {
const table = await createFrames(db);
// --8<-- [start:quickstart_vector_search_1]
// Search for frames similar to "a gripper closing on an object"
const queryVector = [0.2, 0.8, 0.4, 0.9];
const result = await table
.search(queryVector)
.select(["frame_id", "video_id", "caption", "_distance"])
.limit(2)
.toArray();
console.table(result);
// --8<-- [end:quickstart_vector_search_1]
expect(result.map((row) => row.frame_id)).toEqual(["2", "4"]);
});
});
test("multimodal data", async () => {
await withDatabase(connection, async (db) => {
await withTempDirectory(async (app) => {
fs.mkdirSync(path.join(app, "data"));
fs.copyFileSync(IMAGE, path.join(app, "data/sample-frame.jpg"));
const cwd = process.cwd();
process.chdir(app);
try {
// --8<-- [start:quickstart_multimodal_bytes]
const arrow = await import("apache-arrow");
const { readFile } = await import("node:fs/promises");
const imageBytes = await readFile("data/sample-frame.jpg");
const imageSchema = new arrow.Schema([
new arrow.Field("frame_id", new arrow.Utf8()),
new arrow.Field("caption", new arrow.Utf8()),
new arrow.Field("image", new arrow.Binary()),
new arrow.Field(
"embedding",
new arrow.FixedSizeList(
4,
new arrow.Field("item", new arrow.Float32(), true),
),
),
]);
const imageData = lancedb.makeArrowTable(
[
{
frame_id: "thumb-1",
caption: "Sample frame thumbnail",
image: imageBytes,
embedding: [0.9, 0.1, 0.5, 0.4],
},
],
{ schema: imageSchema },
);
const multimodalTable = await db.createTable(
"frame_images",
imageData,
{ mode: "overwrite" },
);
// --8<-- [end:quickstart_multimodal_bytes]
expect(await multimodalTable.countRows()).toBe(1);
const [row] = await multimodalTable.query().select(["image"]).toArray();
expect(Buffer.from(row.image)).toEqual(fs.readFileSync(IMAGE));
} finally {
process.chdir(cwd);
}
});
});
});
});