@platform/fsdb.mongo
Version:
Standard IDb abstraction over mongodb.
148 lines (147 loc) • 4.94 kB
JavaScript
import { Subject } from 'rxjs';
import { share } from 'rxjs/operators';
import { defaultValue, keys, mongodb } from '../common';
const MongoClient = mongodb.MongoClient;
export class MongoStore {
constructor(args) {
this._dispose$ = new Subject();
this.dispose$ = this._dispose$.pipe(share());
this.isConnected = false;
const { uri } = args;
this.client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
this._args = args;
if (args.connect) {
this.ensureConnected();
}
}
static create(args) {
return new MongoStore(args);
}
dispose() {
this.client.close();
this.isConnected = false;
this._dispose$.next();
this._dispose$.complete();
}
get isDisposed() {
return this._dispose$.isStopped;
}
ensureConnected() {
this.throwIfDisposed('ensureConnected');
return new Promise((resolve, reject) => {
if (this.isConnected) {
return resolve();
}
this.client.connect(err => {
if (err) {
reject(err);
}
this.db = this.client.db(this._args.db);
this.collection = this.db.collection(this._args.collection);
this.isConnected = true;
resolve();
});
});
}
async insert(doc, options = {}) {
this.throwIfDisposed('insert');
return (await this.insertMany([doc], options))[0];
}
insertMany(docs, options = {}) {
this.throwIfDisposed('insertMany');
return new Promise(async (resolve, reject) => {
await this.ensureConnected();
if (defaultValue(options.escapeKeys, true)) {
docs = keys.encodeObjectKeys(docs);
}
this.collection.insertMany(docs, (err, res) => {
if (err) {
reject(err);
}
else {
resolve(res.ops);
}
});
});
}
updateOne(query, update, options = {}) {
this.throwIfDisposed('update');
return new Promise(async (resolve, reject) => {
await this.ensureConnected();
this.collection.updateOne(query, { $set: update }, options, (err, res) => {
if (err) {
reject(err);
}
else {
const upsert = res.upsertedCount > 0;
const modified = upsert ? res.upsertedCount > 0 : res.modifiedCount > 0;
const doc = upsert ? Object.assign(Object.assign({}, update), { _id: res.upsertedId._id.toString() }) : undefined;
resolve({ modified, upsert, doc });
}
});
});
}
find(query) {
this.throwIfDisposed('find');
return new Promise(async (resolve, reject) => {
await this.ensureConnected();
try {
const cursor = this.collection.find(query);
const docs = [];
await cursor.forEach((doc) => docs.push(doc));
resolve(docs);
}
catch (error) {
reject(error);
}
});
}
findOne(query) {
this.throwIfDisposed('findOne');
return new Promise(async (resolve, reject) => {
await this.ensureConnected();
this.collection.findOne(query, (err, res) => {
if (err) {
reject(err);
}
else {
let doc = res ? Object.assign(Object.assign({}, res), { _id: res._id.toString() }) : undefined;
doc = keys.decodeObjectKeys(doc);
resolve(doc);
}
});
});
}
remove(query, options = {}) {
this.throwIfDisposed('remove');
return new Promise(async (resolve, reject) => {
await this.ensureConnected();
const { multi } = options;
const single = !Boolean(multi);
try {
await this.collection.remove(query, { single });
resolve({});
}
catch (error) {
reject(error);
}
});
}
async drop() {
await this.ensureConnected();
const exists = await this.exists();
if (exists) {
await this.collection.drop();
}
}
async exists() {
await this.ensureConnected();
const name = this._args.collection;
return (await this.db.collections()).some(c => c.collectionName === name);
}
throwIfDisposed(action) {
if (this.isDisposed) {
throw new Error(`Cannot ${action} because MongoStore is disposed.`);
}
}
}