UNPKG

@aurios/jason

Version:

A simple, lightweight, and embeddable JSON document database built on Bun.

127 lines (113 loc) 4.22 kB
import { Effect } from "effect"; import { ConfigManager } from "./config.js"; import { makeBtreeService } from "../make/btree.js"; export const makeIndexService = <Doc extends { id?: string }>( index_name: string ) => Effect.gen(function* () { const config = yield* ConfigManager; const collection_path = yield* config.getCollectionPath(index_name); const doc_schema = yield* config.getCollectionSchema(index_name); const index_definitions = yield* config.getIndexDefinitions(index_name); const cache_config = yield* config.getCacheConfig; // For each field that needs being indexed, create a // B-tree service instance const btree_services = yield* Effect.all( Object.fromEntries( Object.entries(index_definitions) .filter( ([_, def]) => def.indexed || def.unique || def.primary_key || def.multi_entry ) .map(([field_name]) => { const key_schema = "fields" in doc_schema ? (doc_schema as any).fields[field_name] : undefined; if (!key_schema) { throw new Error( `Field ${field_name} does not exist in document schema or automatic indexing is not supported for this schema type` ); } const btree_service_effect = makeBtreeService( `${collection_path}/${field_name}`, key_schema, 8, // B-tree order cache_config?.index_capacity ); return [field_name, btree_service_effect]; }) ) ); /** * Updates the index for a document. * @param old_doc The old document. * @param new_doc The new document. * @returns An effect that updates the index. */ const update = (old_doc: Doc | undefined, new_doc: Doc | undefined) => Effect.forEach( Object.keys(btree_services), (field_name) => Effect.gen(function* () { const btree = btree_services[field_name]; const old_value = old_doc?.[field_name as keyof Doc]; const new_value = new_doc?.[field_name as keyof Doc]; // If the value didn't change, do nothing if (old_value === new_value) { return; } if (old_value !== undefined) { yield* btree.delete(old_value as any); } // Insert new value into index // The key is the value of the field (ex: 'test@email.com') // The value is always the main document ID. if (new_value !== undefined) { yield* btree.insert(new_value as any, new_doc?.id); } }), { discard: true, concurrency: "inherit" } ); /** * Finds all IDs of documents that match a given field value. * @param field_name The name of the field to search. * @param value The value to search for. * @returns An effect that returns the IDs of matching documents. */ const findAllIds = (field_name: string, value: unknown) => Effect.gen(function* () { const btree = btree_services[field_name]; if (!btree) { return yield* Effect.fail( new Error(`Index on field ${field_name} does not exist`) ); } return yield* btree.findAll(value as any); }); /** * Finds all key-value pairs within a specified range on an indexed field. * @param field_name The name of the field to search. * @param options Range options. * @returns An effect that returns the key-value pairs within the range. */ const findRange = ( field_name: string, options: { min?: any; max?: any; minInclusive?: boolean; maxInclusive?: boolean; } ) => Effect.gen(function* () { const btree = btree_services[field_name]; if (!btree) { return yield* Effect.fail( new Error(`Index on field ${field_name} does not exist`) ); } return yield* btree.findRange(options); }); return { update, findAllIds, findRange }; });