diff --git a/test/test.ts b/test/test.ts index 7321fcd..745fd08 100644 --- a/test/test.ts +++ b/test/test.ts @@ -200,14 +200,19 @@ class Truck extends smartdata.SmartDataDbDoc { tap.test('should store a new Truck', async () => { const truck = new Truck('blue', 'MAN'); await truck.save(); - const myTruck = await Truck.getInstance({ color: 'blue' }); - myTruck.id = 'foo'; - await myTruck.save(); const myTruck2 = await Truck.getInstance({ color: 'blue' }); - console.log(myTruck2); + myTruck2.color = 'red'; + await myTruck2.save(); + const myTruck3 = await Truck.getInstance({ color: 'blue' }); + console.log(myTruck3); }); -tap.test('should ', async () => {}); +tap.test('should use a cursor', async () => { + const cursor = await Truck.getCursor({}); + cursor.forEach(async truckArg => { + console.log(truckArg.id); + }); +}); // ======================================= // close the database connection diff --git a/ts/smartdata.classes.collection.ts b/ts/smartdata.classes.collection.ts index 00d29cf..cab19df 100644 --- a/ts/smartdata.classes.collection.ts +++ b/ts/smartdata.classes.collection.ts @@ -1,5 +1,6 @@ import * as plugins from './smartdata.plugins'; import { SmartdataDb } from './smartdata.classes.db'; +import { SmartdataDbCursor } from './smartdata.classes.cursor'; import { SmartDataDbDoc } from './smartdata.classes.doc'; import { CollectionFactory } from './smartdata.classes.collectionfactory'; @@ -170,9 +171,28 @@ export class SmartdataCollection { /** * finds an object in the DbCollection */ - public async find(filterObject: any): Promise { + public async findOne(filterObject: any): Promise { await this.init(); - const result = await this.mongoDbCollection.find(filterObject).toArray(); + const cursor = this.mongoDbCollection.find(filterObject); + const result = await cursor.next(); + cursor.close(); + return result; + } + + public async getCursor(filterObject: any): Promise> { + await this.init(); + const cursor = this.mongoDbCollection.find(filterObject); + return new SmartdataDbCursor(cursor); + } + + /** + * finds an object in the DbCollection + */ + public async findAll(filterObject: any): Promise { + await this.init(); + const cursor = this.mongoDbCollection.find(filterObject); + const result = await cursor.toArray(); + cursor.close(); return result; } diff --git a/ts/smartdata.classes.cursor.ts b/ts/smartdata.classes.cursor.ts new file mode 100644 index 0000000..42d79ff --- /dev/null +++ b/ts/smartdata.classes.cursor.ts @@ -0,0 +1,39 @@ +import * as plugins from './smartdata.plugins'; + +/** + * a wrapper for the native mongodb cursor. Exposes better + */ +export class SmartdataDbCursor { + // STATIC + + // INSTANCE + public mongodbCursor: plugins.mongodb.Cursor; + constructor(cursorArg: plugins.mongodb.Cursor) { + this.mongodbCursor = cursorArg + }; + + public async next(closeAtEnd = true) { + const result = await this.mongodbCursor.next(); + if (!result && closeAtEnd) { + await this.close(); + }; + return result; + }; + + public async forEach(forEachFuncArg: (itemArg: T) => Promise, closeCursorAtEnd = true) { + let currentValue: T; + do { + currentValue = await this.mongodbCursor.next(); + if (currentValue) { + await forEachFuncArg(currentValue); + } + } while (currentValue); + if (closeCursorAtEnd) { + await this.close(); + } + } + + public async close() { + await this.mongodbCursor.close(); + } +} \ No newline at end of file diff --git a/ts/smartdata.classes.doc.ts b/ts/smartdata.classes.doc.ts index 9784ea6..8d4b6d6 100644 --- a/ts/smartdata.classes.doc.ts +++ b/ts/smartdata.classes.doc.ts @@ -3,6 +3,7 @@ import * as plugins from './smartdata.plugins'; import { ObjectMap } from '@pushrocks/lik'; import { SmartdataDb } from './smartdata.classes.db'; +import { SmartdataDbCursor } from './smartdata.classes.cursor'; import { IManager, SmartdataCollection } from './smartdata.classes.collection'; export type TDocCreation = 'db' | 'new' | 'mixed'; @@ -41,6 +42,31 @@ export function unI() { }; } +export const convertFilterForMongoDb = (filterArg: { [key: string]: any }) => { + const convertedFilter: { [key: string]: any } = {}; + const convertFilterArgument = (keyPathArg2: string, filterArg2: any) => { + if (typeof filterArg2 === 'object') { + for (const key of Object.keys(filterArg2)) { + if (key.startsWith('$')) { + convertedFilter[keyPathArg2] = filterArg2; + return; + } else if (key.includes('.')) { + throw new Error('keys cannot contain dots'); + } + } + for (const key of Object.keys(filterArg2)) { + convertFilterArgument(`${keyPathArg2}.${key}`, filterArg2[key]); + } + } else { + convertedFilter[keyPathArg2] = filterArg2; + } + }; + for (const key of Object.keys(filterArg)) { + convertFilterArgument(key, filterArg[key]); + } + return convertedFilter; +}; + export class SmartDataDbDoc { /** * the collection object an Doc belongs to @@ -81,54 +107,81 @@ export class SmartDataDbDoc( + this: plugins.tsclass.typeFest.Class, + mongoDbNativeDocArg: any + ): T { + const newInstance = new this(); + (newInstance as any).creationStatus = 'db'; + for (const key of Object.keys(mongoDbNativeDocArg)) { + newInstance[key] = mongoDbNativeDocArg[key]; + } + return newInstance; + } + + /** + * gets all instances as array + * @param this + * @param filterArg + * @returns + */ public static async getInstances( this: plugins.tsclass.typeFest.Class, filterArg: plugins.tsclass.typeFest.PartialDeep ): Promise { - const convertedFilter: any = {}; - const convertFilterArgument = (keyPathArg: string, filterArg2: any) => { - if (typeof filterArg2 === 'object') { - for (const key of Object.keys(filterArg2)) { - if (key.startsWith('$')) { - convertedFilter[keyPathArg] = filterArg2; - return; - } else if (key.includes('.')) { - throw new Error('keys cannot contain dots'); - } - } - for (const key of Object.keys(filterArg2)) { - convertFilterArgument(`${keyPathArg}.${key}`, filterArg2[key]); - } - } else { - convertedFilter[keyPathArg] = filterArg2; - } - }; - for (const key of Object.keys(filterArg)) { - convertFilterArgument(key, filterArg[key]); - } - const foundDocs = await (this as any).collection.find(convertedFilter); + const foundDocs = await (this as any).collection.findAll(convertFilterForMongoDb(filterArg)); const returnArray = []; - for (const item of foundDocs) { - const newInstance = new this(); - (newInstance as any).creationStatus = 'db'; - for (const key of Object.keys(item)) { - newInstance[key] = item[key]; - } + for (const foundDoc of foundDocs) { + const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc); returnArray.push(newInstance); } return returnArray; } + /** + * gets the first matching instance + * @param this + * @param filterArg + * @returns + */ public static async getInstance( this: plugins.tsclass.typeFest.Class, filterArg: plugins.tsclass.typeFest.PartialDeep ): Promise { - const result = await (this as any).getInstances(filterArg); - if (result && result.length > 0) { - return result[0]; + const foundDoc = await (this as any).collection.findOne(convertFilterForMongoDb(filterArg)); + if (foundDoc) { + const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc); + return newInstance; + } else { + return null; } } + /** + * get cursor + * @returns + */ + public static async getCursor( + this: plugins.tsclass.typeFest.Class, + filterArg: plugins.tsclass.typeFest.PartialDeep + ) { + const cursor: SmartdataDbCursor = await (this as any).collection.getCursor(convertFilterForMongoDb(filterArg)); + return cursor; + } + + /** + * run a function for all instances + * @returns + */ + public static async forEach( + this: plugins.tsclass.typeFest.Class, + filterArg: plugins.tsclass.typeFest.PartialDeep, + forEachFunction: (itemArg: T) => Promise + ) { + const cursor: SmartdataDbCursor = await (this as any).getCursor(filterArg); + await cursor.forEach(forEachFunction) + } + /** * saves this instance but not any connected items * may lead to data inconsistencies, but is faster