Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
import { Utils } from '../lib/Utils'; | |
import db from '../models/Database'; | |
import * as mongodb from 'mongodb'; | |
import { ObjectID, Cursor } from 'mongodb'; | |
export class ObjectFactory { | |
static create(__type: any, o: any): any { | |
if (__type.fromObject) { | |
return __type.fromObject(o); | |
} | |
const newObject = new __type(); | |
return Object.assign(newObject, o); | |
} | |
} | |
export class MongoObject { | |
protected static __type; // The current class | |
protected static __collectionName: string; // The Mongo collection name | |
protected static __idField: string = "_id"; // Default id used to findOne | |
protected static __wlistJsonAttrs: string[] = []; // Whitelist of attributes to serialize. | |
// Json serialization. | |
private toJsonRepr(): object { | |
return Utils.pick(this, (<any>this.constructor).__wlistJsonAttrs); | |
} | |
toJson(): string { | |
return JSON.stringify(this.toJsonRepr()); | |
} | |
/// Find family of methods | |
static async findOne<T>(id: string | ObjectID | mongodb.FilterQuery<T>, options?: mongodb.FindOneOptions): Promise<T | null> { | |
const q = (typeof id === 'string' || id instanceof ObjectID) | |
? { [this.__idField]: id } | |
: id; | |
const o = await db.collection(this.__collectionName).findOne(q, options); | |
if (o) { | |
return ObjectFactory.create(this.__type, o); | |
} | |
return null; | |
} | |
static async findOneAndUpdate<T>(filter: mongodb.FilterQuery<T>, update: Object, options?: mongodb.FindOneAndReplaceOption): Promise<T | null> { | |
const o = await db.collection(this.__collectionName).findOneAndUpdate(filter, update, options); | |
if (o && o.value) { | |
return ObjectFactory.create(this.__type, o.value); | |
} | |
return null; | |
} | |
static find<T>(query: mongodb.FilterQuery<T> = {}, options?: mongodb.FindOneOptions): HfCursor<T> { | |
const cursor = db.collection(this.__collectionName).find(query, options); | |
return HfCursor.cast<T>(cursor, this.__type); | |
} | |
} | |
export class HfCursor<T> extends Cursor<T> { | |
protected __type; | |
static cast<T>(cursor: Cursor<T>, type: any): HfCursor<T> { | |
// “The use of __proto__ is controversial, and has been discouraged.” | |
// see stackoverflow.com/a/32186367 | |
// see developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto | |
(<any>cursor).__proto__ = HfCursor.prototype; | |
(<any>cursor).__type = type; | |
return cursor as HfCursor<T>; | |
} | |
toArray(): Promise<T[]> { | |
return super.toArray().then((objs) => { | |
return objs.map((o) => { | |
return ObjectFactory.create(this.__type, o); | |
}); | |
}); | |
} | |
forEach(__iterator: mongodb.IteratorCallback<T>, __callback: mongodb.EndCallback = () => {}) { | |
super.forEach((o) => { | |
const newObject = ObjectFactory.create(this.__type, o); | |
__iterator(newObject); | |
}, __callback); | |
} | |
on(event: string, listener: (...args) => void): this { | |
if (event === 'data') { | |
super.on('data', (o) => { | |
const newObject = ObjectFactory.create(this.__type, o); | |
listener(newObject); | |
}); | |
} | |
else { | |
super.on(event, listener); | |
} | |
return this; | |
} | |
once(event: string, listener: (...args) => void): this { | |
if (event === 'data') { | |
super.once('data', (o) => { | |
const newObject = ObjectFactory.create(this.__type, o); | |
listener(newObject); | |
}); | |
} | |
else { | |
super.once(event, listener); | |
} | |
return this; | |
} | |
// | |
// Below: cursor methods are only here to make Typescript | |
// know that they return the HfCursor object itself. | |
// (We have checked that the mongo driver does the right thing underneath) | |
// | |
limit(value: number): HfCursor<T> { | |
return super.limit(value) as HfCursor<T>; | |
} | |
skip(value: number): HfCursor<T> { | |
return super.skip(value) as HfCursor<T>; | |
} | |
sort(keyOrList: string | Object[] | Object, direction?: number): HfCursor<T> { | |
return super.sort(keyOrList, direction) as HfCursor<T>; | |
} | |
stream(options?: { transform?: Function }): HfCursor<T> { | |
return super.stream(options) as HfCursor<T>; | |
} | |
} | |