From ef3093bc2351dc60858d15a05a301d297b85b208 Mon Sep 17 00:00:00 2001 From: Ryan Green Date: Wed, 5 Feb 2025 10:06:19 -0330 Subject: [PATCH] feat: drop_index() remote implementation (#2093) Support drop_index operation in remote table. --- docs/openapi.yml | 26 ++++++++++++++++++++++++++ node/src/index.ts | 14 +++++++++++++- node/src/remote/index.ts | 12 ++++++++++++ node/src/test/test.ts | 21 +++++++++++++++++++++ python/python/lancedb/remote/table.py | 3 +++ python/python/tests/test_remote_db.py | 6 ++++++ rust/ffi/node/src/lib.rs | 1 + rust/ffi/node/src/table.rs | 4 ++++ rust/lancedb/src/error.rs | 2 ++ rust/lancedb/src/remote/table.rs | 26 +++++++++++++++++++++----- 10 files changed, 109 insertions(+), 6 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 6f83d372..ed4b83c3 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -38,6 +38,13 @@ components: required: true schema: type: string + index_name: + name: index_name + in: path + description: name of the index + required: true + schema: + type: string responses: invalid_request: description: Invalid request @@ -485,3 +492,22 @@ paths: $ref: "#/components/responses/unauthorized" "404": $ref: "#/components/responses/not_found" + /v1/table/{name}/index/{index_name}/drop/: + post: + description: Drop an index from the table + tags: + - Tables + summary: Drop an index from the table + operationId: dropIndex + parameters: + - $ref: "#/components/parameters/table_name" + - $ref: "#/components/parameters/index_name" + responses: + "200": + description: Index successfully dropped + "400": + $ref: "#/components/responses/invalid_request" + "401": + $ref: "#/components/responses/unauthorized" + "404": + $ref: "#/components/responses/not_found" \ No newline at end of file diff --git a/node/src/index.ts b/node/src/index.ts index d63306ba..705bbac1 100644 --- a/node/src/index.ts +++ b/node/src/index.ts @@ -47,7 +47,8 @@ const { tableSchema, tableAddColumns, tableAlterColumns, - tableDropColumns + tableDropColumns, + tableDropIndex // eslint-disable-next-line @typescript-eslint/no-var-requires } = require("../native.js"); @@ -604,6 +605,13 @@ export interface Table { */ dropColumns(columnNames: string[]): Promise + /** + * Drop an index from the table + * + * @param indexName The name of the index to drop + */ + dropIndex(indexName: string): Promise + /** * Instrument the behavior of this Table with middleware. * @@ -1206,6 +1214,10 @@ export class LocalTable implements Table { return tableDropColumns.call(this._tbl, columnNames); } + async dropIndex(indexName: string): Promise { + return tableDropIndex.call(this._tbl, indexName); + } + withMiddleware(middleware: HttpMiddleware): Table { return this; } diff --git a/node/src/remote/index.ts b/node/src/remote/index.ts index b8a96641..a8035047 100644 --- a/node/src/remote/index.ts +++ b/node/src/remote/index.ts @@ -471,6 +471,18 @@ export class RemoteTable implements Table { ) } } + async dropIndex (index_name: string): Promise { + const res = await this._client.post( + `/v1/table/${encodeURIComponent(this._name)}/index/${encodeURIComponent(index_name)}/drop/` + ) + if (res.status !== 200) { + throw new Error( + `Server Error, status: ${res.status}, ` + + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `message: ${res.statusText}: ${await res.body()}` + ) + } + } async countRows (filter?: string): Promise { const result = await this._client.post(`/v1/table/${encodeURIComponent(this._name)}/count_rows/`, { diff --git a/node/src/test/test.ts b/node/src/test/test.ts index eacdfc36..3f00e751 100644 --- a/node/src/test/test.ts +++ b/node/src/test/test.ts @@ -894,6 +894,27 @@ describe("LanceDB client", function () { expect(stats.distanceType).to.equal("l2"); expect(stats.numIndices).to.equal(1); }).timeout(50_000); + + // not yet implemented + // it("can drop index", async function () { + // const uri = await createTestDB(32, 300); + // const con = await lancedb.connect(uri); + // const table = await con.openTable("vectors"); + // await table.createIndex({ + // type: "ivf_pq", + // column: "vector", + // num_partitions: 2, + // max_iters: 2, + // num_sub_vectors: 2 + // }); + // + // const indices = await table.listIndices(); + // expect(indices).to.have.lengthOf(1); + // expect(indices[0].name).to.equal("vector_idx"); + // + // await table.dropIndex("vector_idx"); + // expect(await table.listIndices()).to.have.lengthOf(0); + // }).timeout(50_000); }); describe("when using a custom embedding function", function () { diff --git a/python/python/lancedb/remote/table.py b/python/python/lancedb/remote/table.py index d3caeb5f..688ee8de 100644 --- a/python/python/lancedb/remote/table.py +++ b/python/python/lancedb/remote/table.py @@ -526,6 +526,9 @@ class RemoteTable(Table): def drop_columns(self, columns: Iterable[str]): return LOOP.run(self._table.drop_columns(columns)) + def drop_index(self, index_name: str): + return LOOP.run(self._table.drop_index(index_name)) + def uses_v2_manifest_paths(self) -> bool: raise NotImplementedError( "uses_v2_manifest_paths() is not supported on the LanceDB Cloud" diff --git a/python/python/tests/test_remote_db.py b/python/python/tests/test_remote_db.py index 874787da..280e0f10 100644 --- a/python/python/tests/test_remote_db.py +++ b/python/python/tests/test_remote_db.py @@ -255,6 +255,9 @@ def test_table_create_indices(): ) ) request.wfile.write(payload.encode()) + elif "/drop/" in request.path: + request.send_response(200) + request.end_headers() else: request.send_response(404) request.end_headers() @@ -266,6 +269,9 @@ def test_table_create_indices(): table.create_scalar_index("id") table.create_fts_index("text") table.create_scalar_index("vector") + table.drop_index("vector_idx") + table.drop_index("id_idx") + table.drop_index("text_idx") @contextlib.contextmanager diff --git a/rust/ffi/node/src/lib.rs b/rust/ffi/node/src/lib.rs index 23903411..229a4cea 100644 --- a/rust/ffi/node/src/lib.rs +++ b/rust/ffi/node/src/lib.rs @@ -169,5 +169,6 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("tableAddColumns", JsTable::js_add_columns)?; cx.export_function("tableAlterColumns", JsTable::js_alter_columns)?; cx.export_function("tableDropColumns", JsTable::js_drop_columns)?; + cx.export_function("tableDropIndex", JsTable::js_drop_index)?; Ok(()) } diff --git a/rust/ffi/node/src/table.rs b/rust/ffi/node/src/table.rs index 0583affb..a2e16ed7 100644 --- a/rust/ffi/node/src/table.rs +++ b/rust/ffi/node/src/table.rs @@ -638,4 +638,8 @@ impl JsTable { Ok(promise) } + + pub(crate) fn js_drop_index(_cx: FunctionContext) -> JsResult { + todo!("not implemented") + } } diff --git a/rust/lancedb/src/error.rs b/rust/lancedb/src/error.rs index 1b653913..a79a800d 100644 --- a/rust/lancedb/src/error.rs +++ b/rust/lancedb/src/error.rs @@ -15,6 +15,8 @@ pub enum Error { InvalidInput { message: String }, #[snafu(display("Table '{name}' was not found"))] TableNotFound { name: String }, + #[snafu(display("Index '{name}' was not found"))] + IndexNotFound { name: String }, #[snafu(display("Embedding function '{name}' was not found. : {reason}"))] EmbeddingFunctionNotFound { name: String, reason: String }, diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index b27e5702..9a5428c9 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -820,11 +820,14 @@ impl TableInternal for RemoteTable { Ok(Some(stats)) } - /// Not yet supported on LanceDB Cloud. - async fn drop_index(&self, _name: &str) -> Result<()> { - Err(Error::NotSupported { - message: "Drop index is not yet supported on LanceDB Cloud.".into(), - }) + async fn drop_index(&self, index_name: &str) -> Result<()> { + let request = self.client.post(&format!( + "/v1/table/{}/index/{}/drop/", + self.name, index_name + )); + let (request_id, response) = self.client.send(request, true).await?; + self.check_table_response(&request_id, response).await?; + Ok(()) } async fn table_definition(&self) -> Result { @@ -2022,4 +2025,17 @@ mod tests { table.drop_columns(&["a", "b"]).await.unwrap(); } + + #[tokio::test] + async fn test_drop_index() { + let table = Table::new_with_handler("my_table", |request| { + assert_eq!(request.method(), "POST"); + assert_eq!( + request.url().path(), + "/v1/table/my_table/index/my_index/drop/" + ); + http::Response::builder().status(200).body("{}").unwrap() + }); + table.drop_index("my_index").await.unwrap(); + } }