@codai/cbd
Version:
Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server
283 lines • 10.5 kB
JavaScript
/**
* Document Storage Engine - MongoDB-compatible document database
* Part of CBD Universal Database Phase 2 & 3
*/
import { EventEmitter } from 'events';
export class DocumentStorageEngine extends EventEmitter {
collections = new Map();
indexes = new Map();
constructor() {
super();
}
async initialize() {
// Initialize the document storage engine
this.emit('initialized');
}
/**
* Insert a single document
*/
async insertDocument(collection, document) {
const coll = this.getOrCreateCollection(collection);
const id = document._id || this.generateId();
document._id = id;
coll.set(id, { ...document });
this.emit('document:inserted', { collection, document });
return id;
}
/**
* Insert a single document (MongoDB-compatible alias)
*/
async insertOne(collection, document) {
return await this.insertDocument(collection, document);
}
/**
* Insert multiple documents
*/
async insertMany(collection, documents) {
const insertedIds = [];
for (const doc of documents) {
const id = await this.insertDocument(collection, doc);
insertedIds.push(id);
}
return insertedIds;
}
/**
* Find documents matching query
*/
async findDocuments(collection, query = {}, options = {}) {
const coll = this.collections.get(collection);
if (!coll)
return [];
let results = Array.from(coll.values());
// Apply query filter
if (Object.keys(query).length > 0) {
results = results.filter(doc => this.matchesQuery(doc, query));
}
// Apply sorting
if (options.sort) {
results.sort((a, b) => {
for (const [field, direction] of Object.entries(options.sort)) {
const aVal = a[field];
const bVal = b[field];
const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
if (comparison !== 0) {
return comparison * direction;
}
}
return 0;
});
}
// Apply skip and limit
if (options.skip) {
results = results.slice(options.skip);
}
if (options.limit) {
results = results.slice(0, options.limit);
}
return results;
}
/**
* Find documents (MongoDB-compatible alias)
*/
async find(collection, query = {}, options = {}) {
return await this.findDocuments(collection, query, options);
}
/**
* Find a single document
*/
async findOne(collection, query = {}, options = {}) {
const results = await this.findDocuments(collection, query, { ...options, limit: 1 });
return results.length > 0 ? results[0] : null;
}
/**
* Update a single document
*/
async updateDocument(collection, filter, update) {
const docs = await this.findDocuments(collection, filter, { limit: 1 });
if (docs.length === 0) {
return { matchedCount: 0, modifiedCount: 0 };
}
const doc = docs[0];
const updatedDoc = this.applyUpdate(doc, update);
const coll = this.getOrCreateCollection(collection);
coll.set(doc._id, updatedDoc);
this.emit('document:updated', { collection, filter, update, document: updatedDoc });
return { matchedCount: 1, modifiedCount: 1 };
}
/**
* Update a single document (MongoDB-compatible alias)
*/
async updateOne(collection, filter, update) {
return await this.updateDocument(collection, filter, update);
}
/**
* Delete documents matching filter
*/
async deleteDocuments(collection, filter) {
const docsToDelete = await this.findDocuments(collection, filter);
const coll = this.collections.get(collection);
if (!coll || docsToDelete.length === 0) {
return 0;
}
for (const doc of docsToDelete) {
coll.delete(doc._id);
}
this.emit('document:deleted', { collection, filter, count: docsToDelete.length });
return docsToDelete.length;
}
/**
* Delete a single document
*/
async deleteOne(collection, filter) {
const docs = await this.findDocuments(collection, filter, { limit: 1 });
if (docs.length === 0) {
return 0;
}
const coll = this.collections.get(collection);
if (coll) {
coll.delete(docs[0]._id);
this.emit('document:deleted', { collection, filter, count: 1 });
return 1;
}
return 0;
}
/**
* Get collection statistics
*/
async getCollectionStats(collection) {
if (collection) {
const coll = this.collections.get(collection);
if (!coll)
return null;
const docs = Array.from(coll.values());
const totalSize = docs.reduce((size, doc) => size + JSON.stringify(doc).length, 0);
return {
name: collection,
count: docs.length,
size: totalSize,
avgObjSize: docs.length > 0 ? totalSize / docs.length : 0,
storageSize: totalSize * 1.2, // Estimated with overhead
indexCount: this.indexes.get(collection)?.size || 0
};
}
// Return stats for all collections when no collection specified
const allStats = {};
for (const [collName, coll] of this.collections) {
const docs = Array.from(coll.values());
const totalSize = docs.reduce((size, doc) => size + JSON.stringify(doc).length, 0);
allStats[collName] = {
name: collName,
count: docs.length,
size: totalSize,
avgObjSize: docs.length > 0 ? totalSize / docs.length : 0,
storageSize: totalSize * 1.2,
indexCount: this.indexes.get(collName)?.size || 0
};
}
return allStats;
}
getOrCreateCollection(collection) {
if (!this.collections.has(collection)) {
this.collections.set(collection, new Map());
}
return this.collections.get(collection);
}
generateId() {
return `doc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
matchesQuery(document, query) {
for (const [key, value] of Object.entries(query)) {
if (key.startsWith('$')) {
// Handle logical operators
if (key === '$and' && Array.isArray(value)) {
return value.every(subQuery => this.matchesQuery(document, subQuery));
}
if (key === '$or' && Array.isArray(value)) {
return value.some(subQuery => this.matchesQuery(document, subQuery));
}
if (key === '$not') {
return !this.matchesQuery(document, value);
}
}
else {
// Handle field queries
if (!this.matchesFieldQuery(document[key], value)) {
return false;
}
}
}
return true;
}
matchesFieldQuery(fieldValue, queryValue) {
if (typeof queryValue === 'object' && queryValue !== null && !Array.isArray(queryValue)) {
for (const [operator, operatorValue] of Object.entries(queryValue)) {
switch (operator) {
case '$eq':
return fieldValue === operatorValue;
case '$ne':
return fieldValue !== operatorValue;
case '$gt':
return typeof fieldValue === 'number' && typeof operatorValue === 'number'
? fieldValue > operatorValue : false;
case '$gte':
return typeof fieldValue === 'number' && typeof operatorValue === 'number'
? fieldValue >= operatorValue : false;
case '$lt':
return typeof fieldValue === 'number' && typeof operatorValue === 'number'
? fieldValue < operatorValue : false;
case '$lte':
return typeof fieldValue === 'number' && typeof operatorValue === 'number'
? fieldValue <= operatorValue : false;
case '$in':
return Array.isArray(operatorValue) && operatorValue.includes(fieldValue);
case '$nin':
return Array.isArray(operatorValue) && !operatorValue.includes(fieldValue);
case '$exists':
return operatorValue ? fieldValue !== undefined : fieldValue === undefined;
case '$regex':
return typeof fieldValue === 'string' && typeof operatorValue === 'string'
? new RegExp(operatorValue).test(fieldValue) : false;
default:
return false;
}
}
return true;
}
else {
return fieldValue === queryValue;
}
}
applyUpdate(document, update) {
const updatedDoc = { ...document };
if (update.$set) {
Object.assign(updatedDoc, update.$set);
}
if (update.$unset) {
for (const field of Object.keys(update.$unset)) {
delete updatedDoc[field];
}
}
if (update.$inc) {
for (const [field, increment] of Object.entries(update.$inc)) {
updatedDoc[field] = (updatedDoc[field] || 0) + Number(increment);
}
}
return updatedDoc;
}
/**
* Alias for insertDocument - Cloud service compatibility
*/
async insert(collection, document) {
return this.insertDocument(collection, document);
}
/**
* Find document by ID - Cloud service compatibility
*/
async findById(collection, id) {
const coll = this.collections.get(collection);
if (!coll)
return null;
return coll.get(id) || null;
}
}
//# sourceMappingURL=DocumentStorageEngine.js.map