diff --git a/docs/src/js/classes/Connection.md b/docs/src/js/classes/Connection.md index d46d414d3..75af5102e 100644 --- a/docs/src/js/classes/Connection.md +++ b/docs/src/js/classes/Connection.md @@ -319,6 +319,38 @@ Creates a new Table and initialize it with new data. *** +### createView() + +```ts +abstract createView( + name, + query, + namespacePath?): Promise +``` + +Create a view: a named query the database plans on every read. + +The query is planned once, at creation, so one that cannot be planned is +rejected now rather than at the first read. A view holds no rows, and its +readers see its sources as they are at read time. + +There is no replace: a name already taken is an error, and changing a +view is a drop followed by a create. + +#### Parameters + +* **name**: `string` + +* **query**: `string` + +* **namespacePath?**: `string`[] + +#### Returns + +`Promise`<[`ViewDescription`](../interfaces/ViewDescription.md)> + +*** + ### describeNamespace() ```ts @@ -342,6 +374,27 @@ The namespace's properties *** +### describeView() + +```ts +abstract describeView(name, namespacePath?): Promise +``` + +What this database records about the view named `name`: its defining +query and the schema that query resolved to. + +#### Parameters + +* **name**: `string` + +* **namespacePath?**: `string`[] + +#### Returns + +`Promise`<[`ViewDescription`](../interfaces/ViewDescription.md)> + +*** + ### display() ```ts @@ -498,6 +551,28 @@ on the returned job to know when cleanup has finished. *** +### dropView() + +```ts +abstract dropView(name, namespacePath?): Promise +``` + +Drop the view named `name`. + +The tables it reads are untouched: a view holds no rows of its own. + +#### Parameters + +* **name**: `string` + +* **namespacePath?**: `string`[] + +#### Returns + +`Promise`<`void`> + +*** + ### isOpen() ```ts @@ -636,6 +711,26 @@ A page of table names and an *** +### listViews() + +```ts +abstract listViews(namespacePath?): Promise +``` + +The names of the views in one namespace. + +Names only; a definition comes from [describeView](Connection.md#describeview). + +#### Parameters + +* **namespacePath?**: `string`[] + +#### Returns + +`Promise`<`string`[]> + +*** + ### openJob() ```ts diff --git a/docs/src/js/globals.md b/docs/src/js/globals.md index 6d242a15b..2f49578b1 100644 --- a/docs/src/js/globals.md +++ b/docs/src/js/globals.md @@ -149,6 +149,7 @@ - [UpdateOptions](interfaces/UpdateOptions.md) - [UpdateResult](interfaces/UpdateResult.md) - [Version](interfaces/Version.md) +- [ViewDescription](interfaces/ViewDescription.md) - [WriteExecutionOptions](interfaces/WriteExecutionOptions.md) - [WriteProgress](interfaces/WriteProgress.md) diff --git a/docs/src/js/interfaces/ViewDescription.md b/docs/src/js/interfaces/ViewDescription.md new file mode 100644 index 000000000..346e29ecc --- /dev/null +++ b/docs/src/js/interfaces/ViewDescription.md @@ -0,0 +1,63 @@ +[**@lancedb/lancedb**](../README.md) • **Docs** + +*** + +[@lancedb/lancedb](../globals.md) / ViewDescription + +# Interface: ViewDescription + +What a database records about one view. + +A view holds no rows: it stores the statement that defines it and the +schema that statement resolved to, so a reader sees its sources as they +are at read time. + +## Properties + +### defaultDatabase + +```ts +defaultDatabase: string; +``` + +The database that unqualified table names in [query](ViewDescription.md#query) resolve against. + +*** + +### name + +```ts +name: string; +``` + +The view's name within its namespace. + +*** + +### namespacePath + +```ts +namespacePath: string[]; +``` + +The namespace holding the view; empty is the root namespace. + +*** + +### query + +```ts +query: string; +``` + +The defining query, as the database stores it. + +*** + +### schema + +```ts +schema: Schema; +``` + +The schema the defining query resolved to when the view was created. diff --git a/nodejs/__test__/remote.test.ts b/nodejs/__test__/remote.test.ts index 6781fb799..cf28e29b4 100644 --- a/nodejs/__test__/remote.test.ts +++ b/nodejs/__test__/remote.test.ts @@ -110,19 +110,20 @@ describe("remote connection", () => { name: "adults", namespace: ["analytics"], query: "SELECT name FROM people", + // biome-ignore lint/style/useNamingConvention: the wire field is snake_case default_database: "db", schema: { - fields: [{ name: "name", nullable: true, type: { type: "utf8" } }], + fields: [ + { name: "name", nullable: true, type: { type: "utf8" } }, + ], }, }), ); }, async (db) => { - const view = await db.createView( - "adults", - "SELECT name FROM people", - ["analytics"], - ); + 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"); diff --git a/nodejs/lancedb/connection.ts b/nodejs/lancedb/connection.ts index 2387e1e39..9637d60c2 100644 --- a/nodejs/lancedb/connection.ts +++ b/nodejs/lancedb/connection.ts @@ -23,7 +23,6 @@ import { validateNonNegativeInteger, } from "./materialized_view"; import { Connection as LanceDbConnection } from "./native"; -import { ViewDescription, viewDescriptionFromNative } from "./view"; import type { CreateNamespaceResponse, DescribeNamespaceResponse, @@ -32,6 +31,7 @@ import type { ListNamespacesResponse, ListTablesResponse, } from "./native"; +import { ViewDescription, viewDescriptionFromNative } from "./view"; export type { CreateNamespaceResponse, DescribeNamespaceResponse, diff --git a/python/python/lancedb/db.py b/python/python/lancedb/db.py index e68f48c02..7288a964d 100644 --- a/python/python/lancedb/db.py +++ b/python/python/lancedb/db.py @@ -972,7 +972,9 @@ class DBConnection(EnforceOverrides): "View operations are not supported for this connection type" ) - def drop_view(self, name: str, *, namespace_path: Optional[List[str]] = None) -> None: + def drop_view( + self, name: str, *, namespace_path: Optional[List[str]] = None + ) -> None: """Drop a view. The tables it reads are untouched: a view holds no rows of its own. @@ -1821,7 +1823,9 @@ class LanceDBConnection(DBConnection): return LOOP.run(self._conn.describe_view(name, namespace_path=namespace_path)) @override - def drop_view(self, name: str, *, namespace_path: Optional[List[str]] = None) -> None: + def drop_view( + self, name: str, *, namespace_path: Optional[List[str]] = None + ) -> None: LOOP.run(self._conn.drop_view(name, namespace_path=namespace_path)) @override @@ -2795,9 +2799,7 @@ class AsyncConnection(object): [DBConnection.create_view][lancedb.DBConnection.create_view]. """ return _view_description( - await self._inner.create_view( - name, query, list(namespace_path or []) - ) + await self._inner.create_view(name, query, list(namespace_path or [])) ) async def describe_view( diff --git a/python/python/lancedb/remote/db.py b/python/python/lancedb/remote/db.py index ed354a632..962ef66e1 100644 --- a/python/python/lancedb/remote/db.py +++ b/python/python/lancedb/remote/db.py @@ -926,7 +926,9 @@ class RemoteDBConnection(DBConnection): return LOOP.run(self._conn.describe_view(name, namespace_path=namespace_path)) @override - def drop_view(self, name: str, *, namespace_path: Optional[List[str]] = None) -> None: + def drop_view( + self, name: str, *, namespace_path: Optional[List[str]] = None + ) -> None: LOOP.run(self._conn.drop_view(name, namespace_path=namespace_path)) @override diff --git a/python/src/connection.rs b/python/src/connection.rs index 6ca15d021..05a0dc274 100644 --- a/python/src/connection.rs +++ b/python/src/connection.rs @@ -104,13 +104,15 @@ fn parse_default_namespace_path(path: Option>) -> PyResult, String, String, Py); + /// A view description as a plain tuple, with the schema converted to the /// pyarrow schema the caller would get from any other lancedb API. The Python /// layer names the fields; this keeps the binding free of a class that would /// have to be kept in step with the Rust struct. -fn view_description_to_py( - view: lancedb::view::ViewDescription, -) -> PyResult<(String, Vec, String, String, Py)> { +fn view_description_to_py(view: lancedb::view::ViewDescription) -> PyResult { Python::attach(|py| { let schema = view.schema.to_pyarrow(py)?.unbind(); Ok(( diff --git a/rust/lancedb/src/remote/db.rs b/rust/lancedb/src/remote/db.rs index ee781ab26..79fc2deb4 100644 --- a/rust/lancedb/src/remote/db.rs +++ b/rust/lancedb/src/remote/db.rs @@ -4057,7 +4057,7 @@ mod tests { /// view, so it is refused before it reaches a route. #[tokio::test] async fn test_view_names_that_would_resplit_are_refused() { - let conn = Connection::new_with_handler(|_| { + let conn = Connection::new_with_handler(|_| -> http::Response { panic!("an invalid identifier must not reach the service") }); for (name, namespace) in [