From 259b3b7c81d8a67caf5c971090ac4420dc3ade14 Mon Sep 17 00:00:00 2001 From: izure Date: Sat, 13 Apr 2024 06:50:28 +0900 Subject: [PATCH] feat: Add delete method to SerializeStrategy class. --- package.json | 2 +- src/BPTreeAsync.ts | 10 ++++++++++ src/BPTreeSync.ts | 10 ++++++++++ src/SerializeStrategyAsync.ts | 5 +++++ src/SerializeStrategySync.ts | 5 +++++ src/base/BPTree.ts | 10 ++++++++-- src/base/SerializeStrategy.ts | 7 +++++++ test/unit.test.ts | 10 ++++++++++ 8 files changed, 56 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 691f21b..f917b83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serializable-bptree", - "version": "3.3.1", + "version": "3.4.0-alpha.0", "description": "Store the B+tree flexibly, not only in-memory.", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.mjs", diff --git a/src/BPTreeAsync.ts b/src/BPTreeAsync.ts index d3318f5..3379e47 100644 --- a/src/BPTreeAsync.ts +++ b/src/BPTreeAsync.ts @@ -159,6 +159,7 @@ export class BPTreeAsync extends BPTree { if (this.root === node && node.keys.length === 1) { const keys = node.keys as number[] + this.bufferForNodeDelete(this.root) this.root = await this.getNode(keys[0]) this.root.parent = 0 this.strategy.head.root = this.root.id @@ -255,6 +256,7 @@ export class BPTreeAsync extends BPTree { await this._deleteEntry(await this.getNode(node.parent), node.id, guess!) this.bufferForNodeUpdate(pointer) + this.bufferForNodeDelete(node) } else { if (isPredecessor) { @@ -491,6 +493,13 @@ export class BPTreeAsync extends BPTree { this._nodeUpdateBuffer.clear() } + protected async commitNodeDeleteBuffer(): Promise { + for (const node of this._nodeDeleteBuffer.values()) { + await this.strategy.delete(node.id) + } + this._nodeDeleteBuffer.clear() + } + public async keys(condition: BPTreeCondition, filterValues?: Set): Promise> { for (const k in condition) { const key = k as keyof BPTreeCondition @@ -608,6 +617,7 @@ export class BPTreeAsync extends BPTree { await this.commitHeadBuffer() await this.commitNodeCreateBuffer() await this.commitNodeUpdateBuffer() + await this.commitNodeDeleteBuffer() } public async exists(key: K, value: V): Promise { diff --git a/src/BPTreeSync.ts b/src/BPTreeSync.ts index 69adc74..44641e9 100644 --- a/src/BPTreeSync.ts +++ b/src/BPTreeSync.ts @@ -159,6 +159,7 @@ export class BPTreeSync extends BPTree { if (this.root === node && node.keys.length === 1) { const keys = node.keys as number[] + this.bufferForNodeDelete(this.root) this.root = this.getNode(keys[0]) this.root.parent = 0 this.strategy.head.root = this.root.id @@ -255,6 +256,7 @@ export class BPTreeSync extends BPTree { this._deleteEntry(this.getNode(node.parent), node.id, guess!) this.bufferForNodeUpdate(pointer) + this.bufferForNodeDelete(node) } else { if (isPredecessor) { @@ -491,6 +493,13 @@ export class BPTreeSync extends BPTree { this._nodeUpdateBuffer.clear() } + protected commitNodeDeleteBuffer(): void { + for (const node of this._nodeDeleteBuffer.values()) { + this.strategy.delete(node.id) + } + this._nodeDeleteBuffer.clear() + } + public keys(condition: BPTreeCondition, filterValues?: Set): Set { for (const k in condition) { const key = k as keyof BPTreeCondition @@ -608,6 +617,7 @@ export class BPTreeSync extends BPTree { this.commitHeadBuffer() this.commitNodeCreateBuffer() this.commitNodeUpdateBuffer() + this.commitNodeDeleteBuffer() } public exists(key: K, value: V): boolean { diff --git a/src/SerializeStrategyAsync.ts b/src/SerializeStrategyAsync.ts index 89295bb..aa8ed33 100644 --- a/src/SerializeStrategyAsync.ts +++ b/src/SerializeStrategyAsync.ts @@ -6,6 +6,7 @@ export abstract class SerializeStrategyAsync extends SerializeStrategy abstract read(id: number): Promise> abstract write(id: number, node: BPTreeNode): Promise + abstract delete(id: number): Promise abstract readHead(): Promise abstract writeHead(head: SerializeStrategyHead): Promise @@ -52,6 +53,10 @@ export class InMemoryStoreStrategyAsync extends SerializeStrategyAsync { + delete this.node[id] + } + async readHead(): Promise { if (this.head.root === 0) { return null diff --git a/src/SerializeStrategySync.ts b/src/SerializeStrategySync.ts index 2068d2d..d1f7e7f 100644 --- a/src/SerializeStrategySync.ts +++ b/src/SerializeStrategySync.ts @@ -6,6 +6,7 @@ export abstract class SerializeStrategySync extends SerializeStrategy abstract write(id: number, node: BPTreeNode): void + abstract delete(id: number): void abstract readHead(): SerializeStrategyHead|null abstract writeHead(head: SerializeStrategyHead): void @@ -52,6 +53,10 @@ export class InMemoryStoreStrategySync extends SerializeStrategySync this.node[id] = node } + delete(id: number): void { + delete this.node[id] + } + readHead(): SerializeStrategyHead|null { if (this.head.root === 0) { return null diff --git a/src/base/BPTree.ts b/src/base/BPTree.ts index cb563d0..88c68a5 100644 --- a/src/base/BPTree.ts +++ b/src/base/BPTree.ts @@ -61,6 +61,7 @@ export abstract class BPTree { protected readonly _nodeCreateBuffer: Map> protected readonly _nodeUpdateBuffer: Map> + protected readonly _nodeDeleteBuffer: Map> protected readonly verifierMap: Record< keyof BPTreeCondition, @@ -120,6 +121,7 @@ export abstract class BPTree { this._cachedRegexp = new CacheBranchSync() this._nodeCreateBuffer = new Map() this._nodeUpdateBuffer = new Map() + this._nodeDeleteBuffer = new Map() this.nodes = new Map() this.strategy = strategy this.comparator = comparator @@ -249,14 +251,18 @@ export abstract class BPTree { } } - protected bufferForNodeCreate(node: BPTreeUnknownNode): Deferred { + protected bufferForNodeCreate(node: BPTreeUnknownNode): void { this._nodeCreateBuffer.set(node.id, node) } - protected bufferForNodeUpdate(node: BPTreeUnknownNode): Deferred { + protected bufferForNodeUpdate(node: BPTreeUnknownNode): void { this._nodeUpdateBuffer.set(node.id, node) } + protected bufferForNodeDelete(node: BPTreeUnknownNode): void { + this._nodeDeleteBuffer.set(node.id, node) + } + /** * Returns the user-defined data stored in the head of the tree. * This value can be set using the `setHeadData` method. If no data has been previously inserted, the default value is returned, and the default value is `{}`. diff --git a/src/base/SerializeStrategy.ts b/src/base/SerializeStrategy.ts index 52e56af..ec0fe48 100644 --- a/src/base/SerializeStrategy.ts +++ b/src/base/SerializeStrategy.ts @@ -46,6 +46,13 @@ export abstract class SerializeStrategy { */ abstract write(id: number, node: BPTreeNode): void|Promise + /** + * This method is called when previously created nodes become no longer needed due to deletion or other processes. + * It can be used to free up space by deleting existing stored nodes. + * @param id This is the ID of the node to be deleted. + */ + abstract delete(id: number): void|Promise + /** * It is called when the `init` method of the tree instance is called. * This method should return the information needed to initialize the tree. This information refers to the values stored in the `writeHead` method. diff --git a/test/unit.test.ts b/test/unit.test.ts index e37c393..4d11bd6 100644 --- a/test/unit.test.ts +++ b/test/unit.test.ts @@ -14,12 +14,14 @@ import { import { readFileSync, writeFileSync, + unlinkSync, existsSync, mkdirSync, } from 'fs' import { readFile, writeFile, + unlink } from 'fs/promises' import { join } from 'path' @@ -518,6 +520,10 @@ class FileIOStrategySync extends SerializeStrategySync { writeFileSync(this._filePath(id), stringify, 'utf8') } + delete(id: number): void { + unlinkSync(this._filePath(id)) + } + readHead(): SerializeStrategyHead|null { const filePath = this._filePath('head') if (!existsSync(filePath)) { @@ -566,6 +572,10 @@ class FileIOStrategyAsync extends SerializeStrategyAsync { await writeFile(this._filePath(id), stringify, 'utf8') } + async delete(id: number): Promise { + await unlink(this._filePath(id)) + } + async readHead(): Promise { const filePath = this._filePath('head') if (!existsSync(filePath)) {