XciD's picture
XciD HF staff
initial commit
8969f81
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>;
}
}