File size: 3,917 Bytes
8969f81
 
 
0a4b8cb
8969f81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a4b8cb
8969f81
0a4b8cb
8969f81
 
 
0a4b8cb
8969f81
 
 
0a4b8cb
 
8969f81
0a4b8cb
 
 
8969f81
 
0a4b8cb
8969f81
 
 
 
 
 
0a4b8cb
 
8969f81
 
 
 
 
 
0a4b8cb
 
8969f81
0a4b8cb
8969f81
 
 
 
 
0a4b8cb
8969f81
0a4b8cb
 
8969f81
 
 
 
 
0a4b8cb
8969f81
0a4b8cb
8969f81
 
 
 
 
 
 
0a4b8cb
 
 
8969f81
0a4b8cb
 
8969f81
0a4b8cb
 
 
 
8969f81
 
 
 
 
 
 
 
 
 
 
 
0a4b8cb
8969f81
 
 
 
 
 
 
 
 
 
 
 
0a4b8cb
8969f81
 
 
 
 
0a4b8cb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8969f81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { Utils } from '../lib/Utils';
import db from '../models/Database';
import * as mongodb from 'mongodb';
import { ObjectId, FindCursor } 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.Filter<T>, options?: mongodb.FindOptions): 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.Filter<T>, update: Object, options: mongodb.FindOneAndUpdateOptions = {}): 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.Filter<T> = {}, options?: mongodb.FindOptions): HfCursor<T> {
		const cursor = db.collection(this.__collectionName).find(query, options);
		return HfCursor.cast<T>(cursor as any, this.__type);
	}
}



export class HfCursor<T> extends FindCursor<T> {
	protected __type;

	static cast<T>(cursor: FindCursor<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 unknown as HfCursor<T>;
	}

	toArray(): Promise<T[]> {
		return super.toArray().then((objs) => {
			return objs.map((o) => {
				return ObjectFactory.create(this.__type, o);
			});
		});
	}

	forEach(__iterator: (doc: T) => boolean | void): Promise<void> {
		return super.forEach((o) => {
			const newObject = ObjectFactory.create(this.__type, o);
			return __iterator(newObject);
		});
	}




	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>;
	// }
}