@berish/orm-rethinkdb-db-adapter
Version:
Адаптер базы данных RethinkDB для @berish/orm
363 lines • 15.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const emitter_1 = require("@berish/emitter");
const linq_1 = require("@berish/linq");
const r = require("rethinkdb");
const orm_1 = require("@berish/orm");
class RethinkDBAdapter extends orm_1.BaseDBAdapter {
constructor() {
super(...arguments);
this.connection = null;
this._cacheEmitter = new emitter_1.CacheEmitter();
this.tables = [];
this.indexNames = {};
this.subscribeCursors = [];
this._subscribeEventHashes = [];
}
initialize(params) {
return __awaiter(this, void 0, void 0, function* () {
params = params || {};
params.subscribe = params.subscribe || {};
params.subscribe.changefeedQueueSize =
typeof params.subscribe.changefeedQueueSize === 'number' ? params.subscribe.changefeedQueueSize : 100000;
params.subscribe.squash =
typeof params.subscribe.squash === 'number' || typeof params.subscribe.squash === 'boolean'
? params.subscribe.squash
: false;
params.subscribe.includeInitial =
typeof params.subscribe.includeInitial === 'boolean' ? params.subscribe.includeInitial : false;
this.params = params;
this.connection = yield r.connect({
db: params.dbName,
host: params.host,
password: params.password,
port: params.port,
ssl: params.ssl,
user: params.user,
});
yield this.db(params.dbName);
});
}
close() {
return __awaiter(this, void 0, void 0, function* () {
for (const hash of this._subscribeEventHashes) {
this._cacheEmitter.unsubscribe(hash);
}
if (this.connection && this.connection.open) {
yield this.connection.close();
}
this.params = null;
this.connection = null;
this.tables = [];
this.indexNames = {};
this.subscribeCursors = [];
});
}
reconnect() {
return __awaiter(this, void 0, void 0, function* () {
const params = this.params;
yield this.close();
yield this.initialize(params);
});
}
count(queryData) {
return __awaiter(this, void 0, void 0, function* () {
const { value: className } = linq_1.default.from(queryData).last(m => m.key === 'className');
const table = yield this.table(className);
const seq = yield this.filter(table, queryData);
const result = yield seq.count().run(this.connection);
return result;
});
}
get(query) {
return __awaiter(this, void 0, void 0, function* () {
const items = yield this.find(query);
return items && items[0];
});
}
create(tableName, items) {
return __awaiter(this, void 0, void 0, function* () {
return this.update(tableName, items);
});
}
update(tableName, items) {
return __awaiter(this, void 0, void 0, function* () {
const table = yield this.table(tableName);
items.forEach(item => (item.createdAt = item.updatedAt = +new Date()));
yield table
.insert(items, {
conflict: (id, oldDoc, newDoc) => oldDoc.merge(newDoc).merge({ createdAt: oldDoc('createdAt') }),
})
.run(this.connection);
});
}
index(tableName, indexName, keys) {
return __awaiter(this, void 0, void 0, function* () {
const table = yield this.table(tableName);
const indexTable = this.indexNames[tableName];
if (indexTable.includes(indexName))
return void 0;
const indexList = yield table.indexList().run(this.connection);
if (!indexList.includes(indexName)) {
if (keys && keys.length)
yield table
.indexCreate(indexName, keys.map(key => r.row(key)))
.run(this.connection);
else
yield table.indexCreate(indexName).run(this.connection);
yield table.indexWait(indexName).run(this.connection);
}
indexTable.push(indexName);
});
}
find(queryData) {
return __awaiter(this, void 0, void 0, function* () {
const { value: className } = linq_1.default.from(queryData).last(m => m.key === 'className');
const table = yield this.table(className);
const seq = yield this.filter(table, queryData);
const cursor = yield seq.run(this.connection);
return cursor.toArray();
});
}
delete(queryData) {
return __awaiter(this, void 0, void 0, function* () {
const { value: className } = linq_1.default.from(queryData).last(m => m.key === 'className');
const table = yield this.table(className);
const seq = yield this.filter(table, queryData);
yield seq.delete().run(this.connection);
});
}
subscribe(query, callback, onError) {
const { subscribe: { squash, changefeedQueueSize, includeInitial }, } = this.params;
const queryHash = orm_1.Query.getHash(query);
const eventHash = this._cacheEmitter.subscribe(`subscribe_${queryHash}`, (callback) => __awaiter(this, void 0, void 0, function* () {
const { value: className } = linq_1.default.from(query).last(m => m.key === 'className');
const table = yield this.table(className);
const seq = yield this.filter(table, query);
const cursor = yield seq
.changes({
changefeedQueueSize,
includeInitial,
includeOffsets: false,
includeStates: false,
includeTypes: false,
squash,
})
.run(this.connection);
cursor.each((err, data) => {
if (err)
return onError && onError(err);
const { old_val: oldValue, new_val: newValue } = data;
try {
callback({ oldValue, newValue });
}
catch (err) {
// IGNORE
}
});
this.subscribeCursors.push(cursor);
return () => {
this.closeCursor(cursor, () => this.reconnect());
};
}), ({ oldValue, newValue }) => callback(oldValue, newValue));
this._subscribeEventHashes.push(eventHash);
return () => {
this._subscribeEventHashes = this._subscribeEventHashes.filter(m => m !== eventHash);
this._cacheEmitter.unsubscribe(eventHash);
};
}
closeCursor(cursor, onError) {
return __awaiter(this, void 0, void 0, function* () {
if (cursor) {
try {
yield cursor.close();
}
catch (err) {
if (onError)
onError(err);
}
this.subscribeCursors = this.subscribeCursors.filter(m => m !== cursor);
}
});
}
db(dbName) {
const _db = (dbName) => __awaiter(this, void 0, void 0, function* () {
const dbList = yield r.dbList().run(this.connection);
if (!dbList.includes(dbName)) {
yield r.dbCreate(dbName).run(this.connection);
yield r
.db(dbName)
.wait()
.run(this.connection);
}
return r.db(dbName);
});
return this._cacheEmitter.call(dbName, () => _db(dbName));
}
table(tableName) {
const _table = (tableName) => __awaiter(this, void 0, void 0, function* () {
const db = yield this.db(this.params.dbName);
if (this.tables.includes(tableName)) {
const tableList = yield db.tableList().run(this.connection);
if (tableList.includes(tableName))
return db.table(tableName);
this.tables.splice(this.tables.indexOf(tableName), 1);
return _table(tableName);
}
const tableList = yield db.tableList().run(this.connection);
if (!tableList.includes(tableName)) {
yield db.tableCreate(tableName).run(this.connection);
yield db
.table(tableName)
.wait()
.run(this.connection);
}
this.indexNames = Object.assign(this.indexNames, { [tableName]: [] });
this.tables.push(tableName);
return db.table(tableName);
});
return this._cacheEmitter.call(tableName, () => _table(tableName));
}
filter(table, queryData) {
return __awaiter(this, void 0, void 0, function* () {
return queryData.reduce((seqPromise, { key, value }) => __awaiter(this, void 0, void 0, function* () {
const seq = yield seqPromise;
if (key === 'ids')
return this.ids(table, value);
if (key === 'limit')
return this.limit(seq, value);
if (key === 'skip')
return this.skip(seq, value);
if (key === 'less')
return this.less(seq, value);
if (key === 'lessOrEqual')
return this.lessOrEqual(seq, value);
if (key === 'greater')
return this.greater(seq, value);
if (key === 'greaterOrEqual')
return this.greaterOrEqual(seq, value);
if (key === 'where')
return this.where(seq, value);
if (key === 'subQueries')
return this.subQuery(seq, value);
if (key === 'contains')
return this.contains(seq, value);
if (key === 'pluck')
return this.pluck(seq, value);
return seq;
}), Promise.resolve(table));
});
}
subQuery(mainSeq, subQueries) {
return __awaiter(this, void 0, void 0, function* () {
const entries = Object.entries(subQueries);
const subSeqs = yield Promise.all(entries.map(([key, info]) => __awaiter(this, void 0, void 0, function* () {
const { query, key: keyInQuery } = info;
const { value: className } = linq_1.default.from(query).last(m => m.key === 'className');
const subSeq = yield this.table(className);
const subFilter = yield this.filter(subSeq, query);
return {
className,
keys: key && key.split('.'),
keysInQuery: keyInQuery && keyInQuery.split('.'),
seq: subFilter,
};
})));
for (const { keys, keysInQuery, className, seq } of subSeqs) {
if (keysInQuery && keysInQuery.length > 0) {
mainSeq = mainSeq.filter(row => {
const plucked = keysInQuery.reduceRight((plucked, key) => {
if (!plucked)
return { [key]: true };
return { [key]: plucked };
}, null);
return seq
.pluck(plucked)
.map(subRow => keysInQuery.reduce((subRow, key) => subRow(key), subRow))
.contains(keys.reduce((row, key) => row(key), row));
});
}
else {
mainSeq = mainSeq.filter(row => {
return seq
.pluck('id')
.map(subRow => r.expr(subRow('id'))['add'](`:${className}`))
.contains(keys.reduce((row, key) => row(key), row)('link'));
});
}
}
return mainSeq;
});
}
where(seq, value) {
return Object.entries(value).reduce((seq, [key, value]) => seq.filter(row => key
.split('.')
.reduce((row, key) => row(key), row)
.eq(value)), seq);
}
contains(seq, value) {
return Object.entries(value).reduce((seq, [key, values]) => seq.filter(row => r.expr(values).contains(key.split('.').reduce((row, key) => row(key), row))), seq);
}
limit(seq, limit) {
return seq.limit(limit);
}
skip(seq, skip) {
return seq.skip(skip);
}
ids(table, ids) {
return table.getAll(...ids);
}
less(seq, key) {
return Object.entries(key).reduce((seq, [key, value]) => seq.filter(row => key
.split('.')
.reduce((row, key) => row(key), row)
.lt(value)), seq);
}
lessOrEqual(seq, key) {
return Object.entries(key).reduce((seq, [key, value]) => seq.filter(row => key
.split('.')
.reduce((row, key) => row(key), row)
.le(value)), seq);
}
greater(seq, key) {
return Object.entries(key).reduce((seq, [key, value]) => seq.filter(row => key
.split('.')
.reduce((row, key) => row(key), row)
.gt(value)), seq);
}
greaterOrEqual(seq, key) {
return Object.entries(key).reduce((seq, [key, value]) => seq.filter(row => key
.split('.')
.reduce((row, key) => row(key), row)
.ge(value)), seq);
}
pluck(seq, keys) {
const pluckedObj = keys.reduce((pluckedObj, key) => {
const words = key.split('.');
const wordsCount = words.length;
words.reduce((out, word, index) => {
if (index === wordsCount - 1) {
out[word] = true;
return out;
}
else {
out[word] = out[word] || {};
return out[word];
}
}, pluckedObj);
return pluckedObj;
}, {});
return seq.pluck(pluckedObj);
}
}
exports.default = RethinkDBAdapter;
//# sourceMappingURL=index.js.map