From 62ea2260cae2eb23eb37a687f7eac8aa17d121a5 Mon Sep 17 00:00:00 2001 From: lino-levan <11367844+lino-levan@users.noreply.github.com> Date: Sun, 22 Jan 2023 09:08:58 -0800 Subject: [PATCH] feat: delete by query --- mod.ts | 25 ++++++++++++++-- mod_test.ts | 27 +++++++++++++++--- src/operations/delete.ts | 61 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/mod.ts b/mod.ts index a02f4d4..97fc2f7 100644 --- a/mod.ts +++ b/mod.ts @@ -6,7 +6,7 @@ import { bulkInsert, insert } from "./src/operations/insert.ts"; import { get } from "./src/operations/get.ts"; import { update } from "./src/operations/update.ts"; import { drop } from "./src/operations/drop.ts"; -import { del } from "./src/operations/delete.ts"; +import { deleteByKey, deleteByQuery } from "./src/operations/delete.ts"; import { select } from "./src/operations/select.ts"; import { deleteTenant, @@ -119,8 +119,27 @@ export function start(directory: string) { } case "delete": { - del(directory, tenant, table, key); - break; + let res: Response | undefined; + if (key) { + return new Response( + JSON.stringify(deleteByKey(directory, tenant, table, key)), + { status: 200 }, + ); + } + + if (body.where) { + return new Response( + JSON.stringify( + deleteByQuery(directory, tenant, table, body.where), + ), + { status: 200 }, + ); + } + + if (!res) { + throw "You must include either a key or a query"; + } + return res; } case "get": { diff --git a/mod_test.ts b/mod_test.ts index 6667f9e..d23ce76 100644 --- a/mod_test.ts +++ b/mod_test.ts @@ -685,14 +685,33 @@ Deno.test({ basicFetchOptions, ); - assertEquals(await req.text(), "success"); + assertEquals(await req.json(), yogiKey); req = await fetch( `http://localhost:8777/delete/tableWithSchema/${olekKey}`, basicFetchOptions, ); - assertEquals(await req.text(), "success"); + assertEquals(await req.json(), olekKey); + }, + sanitizeResources: false, + sanitizeOps: false, +}); + +Deno.test({ + name: "Able to delete documents by key", + async fn() { + const req = await fetch( + `http://localhost:8777/delete/table`, + { + ...basicFetchOptions, + body: JSON.stringify({ + where: "starts_with($name, 'Yogi')", + }), + }, + ); + + assertEquals((await req.json()).sort(), yogiKeys.sort()); }, sanitizeResources: false, sanitizeOps: false, @@ -706,7 +725,7 @@ Deno.test({ basicFetchOptions, ); - assertEquals(await req.json(), { schema: null, size: 441 }); + assertEquals(await req.json(), { schema: null, size: 0 }); req = await fetch( `http://localhost:8777/meta/tableWithSchema`, @@ -751,7 +770,7 @@ Deno.test({ }, }, }, - size: 1733, + size: 848, }); }, sanitizeResources: false, diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 755c8b9..b0282a6 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -6,12 +6,13 @@ import { writeMeta, } from "../util/fileOperations.ts"; import { unindexDocument } from "../util/indexDocument.ts"; +import { Chunk } from "../util/types.ts"; +import { select } from "./select.ts"; /** * Delete a document by key from a table. - * Named this way to avoid naming conflict with javascript built-in. */ -export function del( +export function deleteByKey( directory: string, tenant: string, table: string, @@ -56,3 +57,59 @@ export function del( return key; } + +/** + * Delete documents by query from a table. + */ +export function deleteByQuery( + directory: string, + tenant: string, + table: string, + where: string, +) { + // Get documents using a basic select, ideally we would seperate this but eh + const documents = select(directory, tenant, table, { + where, + maxResults: -1, + expandKeys: false, + }); + + const meta = readMeta(directory, tenant); + const schema = meta.table_index[table].schema; + const chunks: Record = {}; + + for (const document of documents) { + const key = document.key as string; + const chunkName = meta.key_index[key][1]; + + // Delete key from key_index + delete meta.key_index[key]; + + if (!(chunkName in chunks)) { + chunks[chunkName] = readChunk(directory, tenant, chunkName); + } + + // Unindex document + if (schema) { + unindexDocument(chunks[chunkName][key], schema, meta, table); + } + + delete chunks[chunkName][key]; + } + + for (const [chunkName, chunk] of Object.entries(chunks)) { + // Delete chunk if empty, otherwise just update it + if (Object.keys(chunk).length === 0) { + deleteChunk(directory, tenant, chunkName); + + const index = meta.table_index[table].chunks.indexOf(chunkName); + meta.table_index[table].chunks.splice(index, 1); + } else { + writeChunk(directory, tenant, chunkName, chunk); + } + } + + writeMeta(directory, tenant, meta); + + return documents.map((doc) => doc.key); +}