UNPKG

rethinkts

Version:

A model system for RethinkDB, written in and for TypeScript.

191 lines 8.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const rethinkdb = require("rethinkdbdash"); const model_1 = require("./model"); const relationships_1 = require("./relationships"); exports.adapterKey = Symbol('rethink adapter'); class RethinkAdapter { constructor(ropts) { this.ropts = ropts; this.r = rethinkdb(this.ropts); this.init = []; ropts.models.forEach(model => { model[exports.adapterKey] = this; const modelInfo = model_1.Model.getInfo(model); this.init.push(this.ensureTable(model)); }); } static fromModel(model) { return model[exports.adapterKey] || model.contructor[exports.adapterKey]; } /** * Resolves when tables and indexes are ready. */ wait() { return Promise.all(this.init).then(() => undefined); } /** * Drain the connection pool, allowing your app to exit. */ close() { this.r.getPoolMaster().drain(); } all(ctor, opts) { return this.find(ctor, null, opts); } find(ctor, filter, opts = {}) { return __awaiter(this, void 0, void 0, function* () { const rows = yield this.query(ctor, filter != null && (q => q.filter(filter)), opts.predicate); return rows.map(row => { const model = model_1.Model.construct(ctor, row); model_1.Model.notify(model, 'afterRetrieve'); return model; }); }); } findOne(ctor, filter, opts = {}) { return __awaiter(this, void 0, void 0, function* () { const rows = yield this.find(ctor, filter, Object.assign({}, opts, { predicate: q => (opts.predicate || (() => q))(q).limit(1) })); return rows[0]; }); } get(ctor, value, opts = {}) { return __awaiter(this, void 0, void 0, function* () { const rows = yield this.query(ctor, q => q.getAll(value, { index: opts.index || model_1.Model.getInfo(ctor).primaryKey }), opts.predicate); return rows.map(row => { const model = model_1.Model.construct(ctor, row); model_1.Model.notify(model, 'afterRetrieve'); return model; }); }); } getOne(ctor, value, opts = {}) { return __awaiter(this, void 0, void 0, function* () { const rows = yield this.get(ctor, value, Object.assign({}, opts, { predicate: q => (opts.predicate || (() => q))(q).limit(1) })); return rows[0]; }); } query(ctor, ...predicates) { return predicates.reduce((q, predicate) => (predicate || (() => q))(q), this.getModelQuery(ctor)); } changes(ctor, opts = {}) { return this.getModelQuery(ctor).changes(opts); } save(model) { return __awaiter(this, void 0, void 0, function* () { const ctor = model.constructor; const modelInfo = model_1.Model.getInfo(ctor); model_1.Model.notify(model, 'beforeSave'); // todo(birtles): Actually figure out what changed. const changed = modelInfo.columns .filter(col => !col.computed) .reduce((doc, col) => (Object.assign({}, doc, { [col.key]: model[col.modelKey] })), {}); const doc = yield this.getModelQuery(ctor).insert(changed, { conflict: 'update' }); if (model[modelInfo.primaryKey] == null && doc.generated_keys) { model[modelInfo.primaryKey] = doc.generated_keys[0]; } model_1.Model.notify(model, 'afterSave'); return model; }); } join(model, relationshipKey, opts = {}) { return __awaiter(this, void 0, void 0, function* () { const ctor = model.constructor; const modelInfo = model_1.Model.getInfo(ctor); const relationship = modelInfo.relationships.find(relationship => relationship.key === relationshipKey); if (!relationship) { throw new Error(`No relationship found for ${relationshipKey}`); } model_1.Model.notify(model, 'beforeJoin', relationship); let joinData; const relationshipModel = relationship.model(model); switch (relationship.kind) { case relationships_1.Relationship.HasMany: joinData = yield this.get(relationshipModel, model[modelInfo.primaryKey], { index: relationship.foreignKey }); if (opts.predicate) { yield Promise.all(joinData.map(opts.predicate)); } break; case relationships_1.Relationship.BelongsTo: joinData = yield this.getOne(relationshipModel, model[relationship.foreignKey]); if (opts.predicate) { yield opts.predicate(joinData); } break; case relationships_1.Relationship.HasOne: joinData = yield this.getOne(relationshipModel, model[modelInfo.primaryKey], { index: relationship.foreignKey }); if (opts.predicate) { yield opts.predicate(joinData); } break; default: throw new Error(`Unhandled relationship type ${relationship.kind}`); } model[relationship.key] = joinData; model_1.Model.notify(model, 'afterJoin', relationship); return model; }); } delete(model) { return __awaiter(this, void 0, void 0, function* () { const ctor = model.constructor; const modelInfo = model_1.Model.getInfo(ctor); model_1.Model.notify(model, 'beforeDelete'); if (!model[modelInfo.primaryKey]) { throw new Error('Cannot delete model without a populated primary key.'); } yield this.getModelQuery(ctor).get(model[modelInfo.primaryKey]).delete(); model_1.Model.notify(model, 'afterDelete'); }); } getModelQuery(ctor) { const modelInfo = model_1.Model.getInfo(ctor); return this.r.db(modelInfo.database).table(modelInfo.table); } /** * Create the table and indexes if they don't already exist. * Doesn't create the database. */ ensureTable(ctor) { return __awaiter(this, void 0, void 0, function* () { const modelInfo = model_1.Model.getInfo(ctor); const tableExists = yield this.r.db(modelInfo.database).tableList().contains(modelInfo.table); if (!tableExists) { yield this.r.db(modelInfo.database).tableCreate(modelInfo.table); } const existingIndexes = yield this.getModelQuery(ctor).indexList(); yield Promise.all(modelInfo.indexes .filter(index => !existingIndexes.includes(index.name)) .map(index => { let key; if (Array.isArray(index.keys)) { key = index.keys.map(key => this.getRowFromPath(key)); if (key.length === 1) { key = key[0]; } } else if (typeof index.keys === 'function') { key = () => index.keys(this.r); } else { key = index.keys; } return this.getModelQuery(ctor).indexCreate(index.name, key, index.options); })); yield this.getModelQuery(ctor).indexWait(); }); } getRowFromPath(path) { return path.split('.').reduce((row, key) => row(key), this.r.row); } } exports.RethinkAdapter = RethinkAdapter; //# sourceMappingURL=adapter.js.map