UNPKG

@push.rocks/smartdata

Version:

An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.

293 lines 22.6 kB
import * as plugins from './plugins.js'; import { SmartdataDb } from './classes.db.js'; import { SmartdataDbCursor } from './classes.cursor.js'; import { SmartDataDbDoc } from './classes.doc.js'; import { SmartdataDbWatcher } from './classes.watcher.js'; import { CollectionFactory } from './classes.collectionfactory.js'; const collectionFactory = new CollectionFactory(); /** * This is a decorator that will tell the decorated class what dbTable to use * @param dbArg */ export function Collection(dbArg) { return function classDecorator(constructor) { const decoratedClass = class extends constructor { static { this.className = constructor.name; } static get collection() { if (!(dbArg instanceof SmartdataDb)) { dbArg = dbArg(); } const coll = collectionFactory.getCollection(constructor.name, dbArg); // Attach document constructor for searchableFields lookup if (!coll.docCtor) { coll.docCtor = decoratedClass; } return coll; } get collection() { if (!(dbArg instanceof SmartdataDb)) { dbArg = dbArg(); } const coll = collectionFactory.getCollection(constructor.name, dbArg); if (!coll.docCtor) { coll.docCtor = decoratedClass; } return coll; } }; return decoratedClass; }; } export const setDefaultManagerForDoc = (managerArg, dbDocArg) => { dbDocArg.prototype.defaultManager = managerArg; return dbDocArg; }; /** * This is a decorator that will tell the decorated class what dbTable to use * @param dbArg */ export function managed(managerArg) { return function classDecorator(constructor) { const decoratedClass = class extends constructor { static { this.className = constructor.name; } static get collection() { let dbArg; if (!managerArg) { dbArg = this.prototype.defaultManager.db; } else if (managerArg['db']) { dbArg = managerArg.db; } else { dbArg = managerArg().db; } return collectionFactory.getCollection(constructor.name, dbArg); } get collection() { let dbArg; if (!managerArg) { //console.log(this.defaultManager.db); //process.exit(0) dbArg = this.defaultManager.db; } else if (managerArg['db']) { dbArg = managerArg.db; } else { dbArg = managerArg().db; } return collectionFactory.getCollection(constructor.name, dbArg); } static get manager() { let manager; if (!managerArg) { manager = this.prototype.defaultManager; } else if (managerArg['db']) { manager = managerArg; } else { manager = managerArg(); } return manager; } get manager() { let manager; if (!managerArg) { manager = this.defaultManager; } else if (managerArg['db']) { manager = managerArg; } else { manager = managerArg(); } return manager; } }; return decoratedClass; }; } /** * @dpecrecated use @managed instead */ export const Manager = managed; export class SmartdataCollection { constructor(classNameArg, smartDataDbArg) { this.objectValidation = null; this.uniqueIndexes = []; this.regularIndexes = []; // flag to ensure text index is created only once this.textIndexCreated = false; // tell the collection where it belongs this.collectionName = classNameArg; this.smartdataDb = smartDataDbArg; // tell the db class about it (important since Db uses different systems under the hood) this.smartdataDb.addCollection(this); } /** * makes sure a collection exists within MongoDb that maps to the SmartdataCollection */ async init() { if (!this.mongoDbCollection) { // connect this instance to a MongoDB collection const availableMongoDbCollections = await this.smartdataDb.mongoDb.collections(); const wantedCollection = availableMongoDbCollections.find((collection) => { return collection.collectionName === this.collectionName; }); if (!wantedCollection) { await this.smartdataDb.mongoDb.createCollection(this.collectionName); console.log(`Successfully initiated Collection ${this.collectionName}`); } this.mongoDbCollection = this.smartdataDb.mongoDb.collection(this.collectionName); // Auto-create a compound text index on all searchable fields // Use document constructor's searchableFields registered via decorator const docCtor = this.docCtor; const searchableFields = docCtor?.searchableFields || []; if (searchableFields.length > 0 && !this.textIndexCreated) { // Build a compound text index spec const indexSpec = {}; searchableFields.forEach(f => { indexSpec[f] = 'text'; }); // Cast to any to satisfy TypeScript IndexSpecification typing await this.mongoDbCollection.createIndex(indexSpec, { name: 'smartdata_text_index' }); this.textIndexCreated = true; } } } /** * mark unique index */ markUniqueIndexes(keyArrayArg = []) { for (const key of keyArrayArg) { if (!this.uniqueIndexes.includes(key)) { this.mongoDbCollection.createIndex(key, { unique: true, }); // make sure we only call this once and not for every doc we create this.uniqueIndexes.push(key); } } } /** * creates regular indexes for the collection */ createRegularIndexes(indexesArg = []) { for (const indexDef of indexesArg) { // Check if we've already created this index const indexKey = indexDef.field; if (!this.regularIndexes.some(i => i.field === indexKey)) { this.mongoDbCollection.createIndex({ [indexDef.field]: 1 }, // Simple single-field index indexDef.options); // Track that we've created this index this.regularIndexes.push(indexDef); } } } /** * adds a validation function that all newly inserted and updated objects have to pass */ addDocValidation(funcArg) { this.objectValidation = funcArg; } /** * finds an object in the DbCollection */ async findOne(filterObject, opts) { await this.init(); // Use MongoDB driver's findOne with optional session return this.mongoDbCollection.findOne(filterObject, { session: opts?.session }); } async getCursor(filterObjectArg, dbDocArg, opts) { await this.init(); const cursor = this.mongoDbCollection.find(filterObjectArg, { session: opts?.session }); return new SmartdataDbCursor(cursor, dbDocArg); } /** * finds an object in the DbCollection */ async findAll(filterObject, opts) { await this.init(); const cursor = this.mongoDbCollection.find(filterObject, { session: opts?.session }); const result = await cursor.toArray(); cursor.close(); return result; } /** * watches the collection while applying a filter */ async watch(filterObject, smartdataDbDocArg) { await this.init(); const changeStream = this.mongoDbCollection.watch([ { $match: filterObject, }, ], { fullDocument: 'updateLookup', }); const smartdataWatcher = new SmartdataDbWatcher(changeStream, smartdataDbDocArg); await smartdataWatcher.readyDeferred.promise; return smartdataWatcher; } /** * create an object in the database */ async insert(dbDocArg, opts) { await this.init(); await this.checkDoc(dbDocArg); this.markUniqueIndexes(dbDocArg.uniqueIndexes); // Create regular indexes if available if (dbDocArg.regularIndexes && dbDocArg.regularIndexes.length > 0) { this.createRegularIndexes(dbDocArg.regularIndexes); } const saveableObject = await dbDocArg.createSavableObject(); const result = await this.mongoDbCollection.insertOne(saveableObject, { session: opts?.session }); return result; } /** * inserts object into the DbCollection */ async update(dbDocArg, opts) { await this.init(); await this.checkDoc(dbDocArg); const identifiableObject = await dbDocArg.createIdentifiableObject(); const saveableObject = await dbDocArg.createSavableObject(); const updateableObject = {}; for (const key of Object.keys(saveableObject)) { if (identifiableObject[key]) { continue; } updateableObject[key] = saveableObject[key]; } const result = await this.mongoDbCollection.updateOne(identifiableObject, { $set: updateableObject }, { upsert: true, session: opts?.session }); return result; } async delete(dbDocArg, opts) { await this.init(); await this.checkDoc(dbDocArg); const identifiableObject = await dbDocArg.createIdentifiableObject(); await this.mongoDbCollection.deleteOne(identifiableObject, { session: opts?.session }); } async getCount(filterObject, opts) { await this.init(); return this.mongoDbCollection.countDocuments(filterObject, { session: opts?.session }); } /** * checks a Doc for constraints * if this.objectValidation is not set it passes. */ checkDoc(docArg) { const done = plugins.smartpromise.defer(); let validationResult = true; if (this.objectValidation) { validationResult = this.objectValidation(docArg); } if (validationResult) { done.resolve(); } else { done.reject('validation of object did not pass'); } return done.promise; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jb2xsZWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5jb2xsZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsY0FBYyxFQUFzQixNQUFNLGtCQUFrQixDQUFDO0FBQ3RFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzFELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBZW5FLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO0FBRWxEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxVQUFVLENBQUMsS0FBMEM7SUFDbkUsT0FBTyxTQUFTLGNBQWMsQ0FBeUMsV0FBYztRQUNuRixNQUFNLGNBQWMsR0FBRyxLQUFNLFNBQVEsV0FBVztxQkFDaEMsY0FBUyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUM7WUFDcEMsTUFBTSxLQUFLLFVBQVU7Z0JBQzFCLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxXQUFXLENBQUMsRUFBRSxDQUFDO29CQUNwQyxLQUFLLEdBQUcsS0FBSyxFQUFFLENBQUM7Z0JBQ2xCLENBQUM7Z0JBQ0QsTUFBTSxJQUFJLEdBQUcsaUJBQWlCLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ3RFLDBEQUEwRDtnQkFDMUQsSUFBSSxDQUFFLElBQVksQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFDMUIsSUFBWSxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUM7Z0JBQ3pDLENBQUM7Z0JBQ0QsT0FBTyxJQUFJLENBQUM7WUFD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