rethinkts
Version:
A model system for RethinkDB, written in and for TypeScript.
191 lines • 8.7 kB
JavaScript
;
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