mirror of
https://github.com/lancedb/lancedb.git
synced 2026-11-15 07:06:52 +00:00
feat: add view CRUD APIs
A view is a named query a database stores and plans on every read. It
holds no rows, which is the whole difference from a materialized view.
Adds `create_view`, `describe_view`, `drop_view` and `list_views` on
`Connection` and the `Database` trait, the remote client that speaks
`/v1/view/{id}/*` and `/v1/namespace/{id}/view/list`, and the Python and
Node bindings. Local databases return `NotSupported`; the server side
lives in Sophon.
`ViewDescription` carries the defining query, the database unqualified
names in it resolve against, and the schema the query resolved to.
`create_view` returns one, so a caller has the schema without a second
call. There is no replace: changing a view is a drop and a create.
Querying a view stays SQL's job -- there are no rows behind it, so there
is no `open_view` returning a `Table`.
This commit is contained in:
@@ -100,6 +100,63 @@ describe("remote connection", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("creates a view and decodes the schema it resolved to", async () => {
|
||||
await withMockDatabase(
|
||||
(req, res) => {
|
||||
expect(req.method).toBe("POST");
|
||||
expect(req.url).toBe("/v1/view/analytics$adults/create");
|
||||
res.writeHead(200, { "content-type": "application/json" }).end(
|
||||
JSON.stringify({
|
||||
name: "adults",
|
||||
namespace: ["analytics"],
|
||||
query: "SELECT name FROM people",
|
||||
default_database: "db",
|
||||
schema: {
|
||||
fields: [{ name: "name", nullable: true, type: { type: "utf8" } }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
async (db) => {
|
||||
const view = await db.createView(
|
||||
"adults",
|
||||
"SELECT name FROM people",
|
||||
["analytics"],
|
||||
);
|
||||
expect(view.name).toBe("adults");
|
||||
expect(view.namespacePath).toEqual(["analytics"]);
|
||||
expect(view.query).toBe("SELECT name FROM people");
|
||||
expect(view.defaultDatabase).toBe("db");
|
||||
expect(view.schema.fields.map((f) => f.name)).toEqual(["name"]);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("lists and drops views through their own routes", async () => {
|
||||
await withMockDatabase(
|
||||
(req, res) => {
|
||||
expect(req.url).toBe("/v1/namespace/$/view/list");
|
||||
res
|
||||
.writeHead(200, { "content-type": "application/json" })
|
||||
.end(JSON.stringify({ views: ["adults"] }));
|
||||
},
|
||||
async (db) => {
|
||||
expect(await db.listViews()).toEqual(["adults"]);
|
||||
},
|
||||
);
|
||||
|
||||
await withMockDatabase(
|
||||
(req, res) => {
|
||||
expect(req.method).toBe("POST");
|
||||
expect(req.url).toBe("/v1/view/adults/drop");
|
||||
res.writeHead(200, { "content-type": "application/json" }).end("{}");
|
||||
},
|
||||
async (db) => {
|
||||
await db.dropView("adults");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should accept partial connection options", async () => {
|
||||
await connect("db://test", {
|
||||
apiKey: "fake",
|
||||
|
||||
Reference in New Issue
Block a user