axiodb
Version:
The Pure JavaScript Alternative to SQLite. Embedded NoSQL database for Node.js with MongoDB-style queries, zero native dependencies, built-in InMemoryCache, and web GUI. Perfect for desktop apps, CLI tools, and embedded systems. No compilation, no platfor
176 lines • 8.97 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadIndex = void 0;
/* eslint-disable @typescript-eslint/no-explicit-any */
const Index_service_1 = require("./Index.service");
const IndexCache_service_1 = require("./IndexCache.service");
class ReadIndex extends Index_service_1.IndexManager {
constructor(path) {
super(path);
this.indexCache = new IndexCache_service_1.IndexCache(path);
}
/**
* Retrieve file path(s) from an index that match the provided query.
*
* OPTIMIZED: Uses in-memory index cache for O(1) lookups instead of disk I/O.
* Falls back to disk on cache miss (cold start recovery).
*
* @param query - An object containing the value to look up. The concrete lookup key is determined
* by the matched index metadata's `fieldName` (i.e. the method will use
* query[metaContent.fieldName] to find entries).
*
* @returns A Promise that resolves to an array of string file paths associated with the query value.
* If no matching index metadata is found, the promise resolves to an empty array.
* If a matching index is found but no entries exist for the queried value, the returned
* value may be undefined at runtime (callers should guard against a missing entry).
*
* @remarks
* - Tries memory cache first for maximum performance (no disk I/O)
* - Falls back to disk on cache miss (cold start recovery)
* - Skips index for complex operators ($regex, $in, $gt, etc.) - full scan required
*
* @throws The returned promise will reject if reading or parsing the index file fails (for example,
* due to I/O errors or converter failures).
*/
getFileFromIndex(query) {
return __awaiter(this, void 0, void 0, function* () {
const matchedIndexFile = yield this.findMatchingIndexMeta(query);
if (matchedIndexFile !== undefined) {
// FAST PATH: Try to get from memory cache first (O(1), no disk I/O)
const indexData = yield this.indexCache.getIndex(matchedIndexFile.indexFieldName);
if (indexData) {
// Memory cache hit - use cached index data
const queryValue = query[indexData.fieldName];
// Skip index lookup for complex query operators
if (typeof queryValue === 'object' && queryValue !== null) {
return [];
}
const finalValueFiles = indexData.indexEntries[queryValue];
return finalValueFiles || [];
}
// Cache miss - fall back to disk read (cold start recovery)
const metaContent = this.converter.ToObject((yield this.fileManager.ReadFile(matchedIndexFile.path)).data);
const queryValue = query[metaContent.fieldName];
// Skip index lookup for complex query operators
if (typeof queryValue === 'object' && queryValue !== null) {
return [];
}
const finalValueFiles = metaContent.indexEntries[queryValue];
return finalValueFiles || [];
}
else {
return [];
}
});
}
/**
* Retrieve file paths from an index for documents matching any value in the $in array.
*
* OPTIMIZED: Uses index lookups for each value in the $in array, unions the results.
* This is significantly faster than full collection scan for indexed fields.
*
* @param fieldName - The field name to query (must have an index)
* @param values - Array of values to match (from $in operator)
*
* @returns Promise resolving to array of unique file paths matching any value
*
* @remarks
* - Uses Set for automatic deduplication of file paths
* - Returns empty array if field has no index
* - O(K) lookups where K = values.length (much faster than O(N) full scan)
*
* @example
* // For query: { category: { $in: ['Electronics', 'Books'] } }
* const files = await readIndex.getFilesForInOperator('category', ['Electronics', 'Books']);
*/
getFilesForInOperator(fieldName, values) {
return __awaiter(this, void 0, void 0, function* () {
const indexData = yield this.indexCache.getIndex(fieldName);
if (!indexData)
return [];
const fileSet = new Set();
for (const value of values) {
const files = indexData.indexEntries[value];
if (files) {
files.forEach(f => fileSet.add(f));
}
}
return Array.from(fileSet);
});
}
/**
* Retrieve file paths from an index for documents where field value starts with a prefix.
*
* OPTIMIZED: Uses index to filter values by prefix, avoiding full collection scan.
* Works with hash-based indexes by filtering index keys.
*
* @param fieldName - The field name to query (must have an index)
* @param prefix - The prefix string to match
* @param caseInsensitive - Whether to perform case-insensitive matching (default: false)
*
* @returns Promise resolving to array of unique file paths where field starts with prefix
*
* @remarks
* - Filters index keys for prefix matches (O(K) where K = index key count)
* - Much faster than full collection scan for prefix patterns
* - Falls back to empty array if field has no index
* - Best used for regex patterns like /^John/ or /^admin@/
*
* @example
* // For query: { name: { $regex: /^John/i } }
* const files = await readIndex.getFilesForPrefixQuery('name', 'John', true);
*/
getFilesForPrefixQuery(fieldName_1, prefix_1) {
return __awaiter(this, arguments, void 0, function* (fieldName, prefix, caseInsensitive = false) {
const indexData = yield this.indexCache.getIndex(fieldName);
if (!indexData)
return [];
const normalizedPrefix = caseInsensitive ? prefix.toLowerCase() : prefix;
const fileSet = new Set();
// Iterate through index keys and find matches
// For hash-based indexes, this is O(K) where K = number of unique values
for (const [value, files] of Object.entries(indexData.indexEntries)) {
const normalizedValue = caseInsensitive ? value.toLowerCase() : value;
if (normalizedValue.startsWith(normalizedPrefix)) {
files.forEach(f => fileSet.add(f));
}
}
return Array.from(fileSet);
});
}
/**
* Finds index metadata entries that correspond to properties present on the provided document.
*
* Reads the index metadata file at `this.indexMetaPath`, converts its content into an object,
* and returns the subset of metadata entries whose `indexFieldName` is an own property of `doc`.
*
* @param doc - The document to check for matching index fields. The function tests own properties
* (via `Object.prototype.hasOwnProperty.call`) rather than inherited properties.
* @returns A Promise that resolves to an array of matching index metadata entries, or `undefined`
* if the index metadata file could not be successfully read. The array may be empty if
* no metadata entries match.
*
* @throws May propagate errors from `fileManager.ReadFile` or `converter.ToObject` if those
* operations throw or reject.
*/
findMatchingIndexMeta(document) {
return __awaiter(this, void 0, void 0, function* () {
const indexMetaContent = yield this.fileManager.ReadFile(this.indexMetaPath);
if (indexMetaContent.status) {
const indexMeta = this.converter.ToObject(indexMetaContent.data);
return indexMeta.find((meta) => Object.prototype.hasOwnProperty.call(document, meta.indexFieldName));
}
});
}
}
exports.ReadIndex = ReadIndex;
//# sourceMappingURL=ReadIndex.service.js.map