blow-data-service
Version:
Observable data service for Blow.
204 lines (168 loc) • 5.28 kB
text/typescript
'use strict';
import {Query} from 'blow-query';
import {Observable} from 'rxjs';
import * as mongodb from 'mongodb';
import {DataConnector} from './DataConnector';
import {Entity} from '../Entity';
export interface ConnectOptions {
uriDecodeAuth: boolean;
db: Object;
server: Object;
replSet: Object;
mongos: Object;
}
export class MongoClient {
static connect(url: string, options?: ConnectOptions): Observable<Db> {
return Observable.from<mongodb.Db>(mongodb.MongoClient.connect(url, options))
.map(db => new Db(db));
}
}
export class Db {
protected _db: mongodb.Db;
constructor(db: mongodb.Db) {
this._db = db;
}
collection<T>(name: string): Collection<T> {
return new Collection(this._db.collection(name));
}
delete() {
return Observable.from(this._db.dropDatabase())
.mergeMap(() => this.close());
}
close(): Observable<boolean> {
return Observable.from(this._db.close());
}
}
export class Collection<T> {
protected _collection: mongodb.Collection;
constructor(collection: mongodb.Collection) {
this._collection = collection;
}
find(query?): Observable<T> {
query = Object.assign({}, { where: {} }, query || {});
let cursor = this._collection.find(query.where);
Object.keys(query).forEach(key => {
if (key !== 'where') {
cursor = cursor[key](query[key]);
}
});
return Observable.create(subscriber => {
cursor.forEach(document => {
subscriber.next(document);
}, error => {
if (error) {
subscriber.error(error);
}
subscriber.complete();
});
});
}
count(query?): Observable<number> {
query = query || {};
return Observable.from(this._collection.count(query));
}
delete(query?): Observable<number> {
query = query || {};
return Observable.from(this._collection.deleteMany(query));
}
insert(doc): Observable<T> {
return Observable.from(this._collection.insertOne(doc))
.map(response => response['ops'][0]);
}
update(query, doc, options?): Observable<T> {
return Observable.from(this._collection.updateOne(query, { $set: doc }, options))
.map(response => {
if (response['modifiedCount']) {
doc._id = query._id.toString();
return doc;
} else {
return null;
}
});
}
}
export class MongoDBConnector extends DataConnector {
protected _db: Db;
protected _buildQueryWhereForId(id: string) {
return this._prepareQueryWhere({
_id: id
});
}
protected _normalizeId(value) {
let id;
try {
id = new mongodb.ObjectID(value);
} catch (e) {
id = value;
}
return id;
}
protected _prepareQueryWhere(queryWhere: { [key: string]: any }): { [key: string]: any } {
const where = {};
Object.keys(queryWhere).forEach(key => {
let value = queryWhere[key];
if (key === '_id') {
value = this._normalizeId(value);
}
where[key] = value;
});
return where;
}
protected _collection<T>(collectionName: string): Collection<T> {
return this._db.collection<T>(collectionName);
}
connect(): Observable<MongoDBConnector> {
this._state.connecting();
return Observable.from<Db>(MongoClient.connect(<string>this._settings['url']))
.do(db => {
this._db = db;
this._state.connected();
})
.mapTo(this);
}
disconnect(): Observable<MongoDBConnector> {
this._state.disconnecting();
return Observable.from(this._db.close())
.do(() => this._state.disconnected())
.mapTo(this);
}
destroyDb(): Observable<MongoDBConnector> {
return this._db.delete();
}
find<T>(collectionName: string, query?: Query): Observable<T> {
return this._collection(collectionName)
.find(this._prepareQuery(query));
}
count(collectionName: string, query?: Query): Observable<number> {
return this._collection(collectionName)
.count(this._prepareQuery(query).where);
}
delete(collectionName: string, query?: Query): Observable<number> {
return this._collection(collectionName)
.delete(this._prepareQuery(query).where)
.map(response => response['result'].n);
}
deleteById(collectionName: string, id: string): Observable<boolean> {
return this._collection(collectionName)
.delete(this._buildQueryWhereForId(id))
.map(response => response['result'].n === 1);
}
get<T>(collectionName: string, id: string): Observable<T> {
return this._collection(collectionName)
.find({ where: this._buildQueryWhereForId(id) });
}
save<T>(collectionName: string, doc: Entity): Observable<T> {
const hasId = Object.keys(doc).indexOf('_id') > -1 && doc['_id'];
if (!hasId) {
return this._collection(collectionName).insert(doc);
} else {
return this._collection(collectionName).update(this._buildQueryWhereForId(doc['_id']), doc, {upsert: true});
}
}
updateAttributes<T>(collectionName: string, id: string, attributes: Entity): Observable<T> {
delete attributes['_id'];
return this.get<T>(collectionName, id)
.map(result => Object.assign(result, attributes))
.mergeMap(doc => this.save<T>(collectionName, doc));
}
}