UNPKG

@berish/orm-rethinkdb-db-adapter

Version:

Адаптер базы данных RethinkDB для @berish/orm

363 lines 15.3 kB
"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