UNPKG

js-data-rethinkdb

Version:
1,148 lines (1,052 loc) 37 kB
import {utils} from 'js-data' import { Adapter, reserved } from 'js-data-adapter' import rethinkdbdash from 'rethinkdbdash' import snakeCase from 'lodash.snakecase' const __super__ = Adapter.prototype const R_OPTS_DEFAULTS = { db: 'test' } const INSERT_OPTS_DEFAULTS = {} const UPDATE_OPTS_DEFAULTS = {} const DELETE_OPTS_DEFAULTS = {} const RUN_OPTS_DEFAULTS = {} const equal = function (r, row, field, value) { return row(field).default(null).eq(value) } const notEqual = function (r, row, field, value) { return row(field).default(null).ne(value) } /** * Default predicate functions for the filtering operators. * * @name module:js-data-rethinkdb.OPERATORS * @property {function} = Equality operator. * @property {function} == Equality operator. * @property {function} != Inequality operator. * @property {function} > "Greater than" operator. * @property {function} >= "Greater than or equal to" operator. * @property {function} < "Less than" operator. * @property {function} <= "Less than or equal to" operator. * @property {function} isectEmpty Operator to test that the intersection * between two arrays is empty. * @property {function} isectNotEmpty Operator to test that the intersection * between two arrays is NOT empty. * @property {function} in Operator to test whether a value is found in the * provided array. * @property {function} notIn Operator to test whether a value is NOT found in * the provided array. * @property {function} contains Operator to test whether an array contains the * provided value. * @property {function} notContains Operator to test whether an array does NOT * contain the provided value. */ export const OPERATORS = { '=': equal, '==': equal, '===': equal, '!=': notEqual, '!==': notEqual, '>': function (r, row, field, value) { return row(field).default(null).gt(value) }, '>=': function (r, row, field, value) { return row(field).default(null).ge(value) }, '<': function (r, row, field, value) { return row(field).default(null).lt(value) }, '<=': function (r, row, field, value) { return row(field).default(null).le(value) }, 'isectEmpty': function (r, row, field, value) { return row(field).default([]).setIntersection(r.expr(value).default([])).count().eq(0) }, 'isectNotEmpty': function (r, row, field, value) { return row(field).default([]).setIntersection(r.expr(value).default([])).count().ne(0) }, 'in': function (r, row, field, value) { return r.expr(value).default(r.expr([])).contains(row(field).default(null)) }, 'notIn': function (r, row, field, value) { return r.expr(value).default(r.expr([])).contains(row(field).default(null)).not() }, 'contains': function (r, row, field, value) { return row(field).default([]).contains(value) }, 'notContains': function (r, row, field, value) { return row(field).default([]).contains(value).not() } } Object.freeze(OPERATORS) /** * RethinkDBAdapter class. * * @example * // Use Container instead of DataStore on the server * import { Container } from 'js-data'; * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * * // Create a store to hold your Mappers * const store = new Container(); * * // Create an instance of RethinkDBAdapter with default settings * const adapter = new RethinkDBAdapter(); * * // Mappers in "store" will use the RethinkDB adapter by default * store.registerAdapter('rethinkdb', adapter, { default: true }); * * // Create a Mapper that maps to a "user" table * store.defineMapper('user'); * * @class RethinkDBAdapter * @extends Adapter * @param {object} [opts] Configuration options. * @param {boolean} [opts.debug=false] See {@link Adapter#debug}. * @param {object} [opts.deleteOpts={}] See {@link RethinkDBAdapter#deleteOpts}. * @param {object} [opts.insertOpts={}] See {@link RethinkDBAdapter#insertOpts}. * @param {object} [opts.operators={@link module:js-data-rethinkdb.OPERATORS}] See {@link RethinkDBAdapter#operators}. * @param {object} [opts.r] See {@link RethinkDBAdapter#r}. * @param {boolean} [opts.raw=false] See {@link Adapter#raw}. * @param {object} [opts.rOpts={}] See {@link RethinkDBAdapter#rOpts}. * @param {object} [opts.runOpts={}] See {@link RethinkDBAdapter#runOpts}. * @param {object} [opts.updateOpts={}] See {@link RethinkDBAdapter#updateOpts}. */ export function RethinkDBAdapter (opts) { utils.classCallCheck(this, RethinkDBAdapter) opts || (opts = {}) // Setup non-enumerable properties Object.defineProperties(this, { /** * The rethinkdbdash instance used by this adapter. Use this directly when * you need to write custom queries. * * @example <caption>Use default instance.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter(); * adapter.r.dbDrop('foo').then(...); * * @example <caption>Configure default instance.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter({ * rOpts: { * user: 'myUser', * password: 'myPassword' * } * }); * adapter.r.dbDrop('foo').then(...); * * @example <caption>Provide a custom instance.</caption> * import rethinkdbdash from 'rethinkdbdash'; * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const r = rethinkdbdash(); * const adapter = new RethinkDBAdapter({ * r: r * }); * adapter.r.dbDrop('foo').then(...); * * @name RethinkDBAdapter#r * @type {object} */ r: { writable: true, value: undefined }, databases: { value: {} }, indices: { value: {} }, tables: { value: {} } }) Adapter.call(this, opts) /** * Default options to pass to r#insert. * * @name RethinkDBAdapter#insertOpts * @type {object} * @default {} */ this.insertOpts || (this.insertOpts = {}) utils.fillIn(this.insertOpts, INSERT_OPTS_DEFAULTS) /** * Default options to pass to r#update. * * @name RethinkDBAdapter#updateOpts * @type {object} * @default {} */ this.updateOpts || (this.updateOpts = {}) utils.fillIn(this.updateOpts, UPDATE_OPTS_DEFAULTS) /** * Default options to pass to r#delete. * * @name RethinkDBAdapter#deleteOpts * @type {object} * @default {} */ this.deleteOpts || (this.deleteOpts = {}) utils.fillIn(this.deleteOpts, DELETE_OPTS_DEFAULTS) /** * Default options to pass to r#run. * * @name RethinkDBAdapter#runOpts * @type {object} * @default {} */ this.runOpts || (this.runOpts = {}) utils.fillIn(this.runOpts, RUN_OPTS_DEFAULTS) /** * Override the default predicate functions for the specified operators. * * @name RethinkDBAdapter#operators * @type {object} * @default {} */ this.operators || (this.operators = {}) utils.fillIn(this.operators, OPERATORS) /** * Options to pass to a new `rethinkdbdash` instance, if one was not provided * at {@link RethinkDBAdapter#r}. See the [rethinkdbdash README][readme] for * instance options. * * [readme]: https://github.com/neumino/rethinkdbdash#importing-the-driver * * @example <caption>Connect to localhost:8080, and let the driver find other instances.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter({ * rOpts: { * discovery: true * } * }); * * @example <caption>Connect to and only to localhost:8080.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter(); * * @example <caption>Do not create a connection pool.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter({ * rOpts: { * pool: false * } * }); * * @example <caption>Connect to a cluster seeding from `192.168.0.100`, `192.168.0.101`, `192.168.0.102`.</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter({ * rOpts: { * servers: [ * { host: '192.168.0.100', port: 28015 }, * { host: '192.168.0.101', port: 28015 }, * { host: '192.168.0.102', port: 28015 } * ] * } * }); * * @name RethinkDBAdapter#rOpts * @see https://github.com/neumino/rethinkdbdash#importing-the-driver * @type {object} */ this.rOpts || (this.rOpts = {}) utils.fillIn(this.rOpts, R_OPTS_DEFAULTS) this.r || (this.r = rethinkdbdash(this.rOpts)) } Adapter.extend({ constructor: RethinkDBAdapter, _handleErrors (cursor) { if (cursor && cursor.errors > 0) { if (cursor.first_error) { throw new Error(cursor.first_error) } throw new Error('Unknown RethinkDB Error') } }, _count (mapper, query, opts) { opts || (opts = {}) query || (query = {}) return this.filterSequence(this.selectTable(mapper, opts), query) .count() .run(this.getOpt('runOpts', opts)) .then((count) => [count, {}]) }, _create (mapper, props, opts) { props || (props = {}) opts || (opts = {}) const insertOpts = this.getOpt('insertOpts', opts) insertOpts.returnChanges = true return this.selectTable(mapper, opts) .insert(props, insertOpts) .run(this.getOpt('runOpts', opts)) .then((cursor) => { this._handleErrors(cursor) let record if (cursor && cursor.changes && cursor.changes.length && cursor.changes[0].new_val) { record = cursor.changes[0].new_val } return [record, cursor] }) }, _createMany (mapper, props, opts) { props || (props = {}) opts || (opts = {}) const insertOpts = this.getOpt('insertOpts', opts) insertOpts.returnChanges = true return this.selectTable(mapper, opts) .insert(props, insertOpts) .run(this.getOpt('runOpts', opts)) .then((cursor) => { this._handleErrors(cursor) let records = [] if (cursor && cursor.changes && cursor.changes.length && cursor.changes) { records = cursor.changes.map((change) => change.new_val) } return [records, cursor] }) }, _destroy (mapper, id, opts) { opts || (opts = {}) return this.selectTable(mapper, opts) .get(id) .delete(this.getOpt('deleteOpts', opts)) .run(this.getOpt('runOpts', opts)) .then((cursor) => { this._handleErrors(cursor) return [undefined, cursor] }) }, _destroyAll (mapper, query, opts) { query || (query = {}) opts || (opts = {}) return this.filterSequence(this.selectTable(mapper, opts), query) .delete(this.getOpt('deleteOpts', opts)) .run(this.getOpt('runOpts', opts)) .then((cursor) => { this._handleErrors(cursor) return [undefined, cursor] }) }, _find (mapper, id, opts) { opts || (opts = {}) return this._pluck(mapper, this.selectTable(mapper, opts).get(id), opts) .run(this.getOpt('runOpts', opts)) .then((record) => { if (!record) { record = undefined } return [record, {}] }) }, _findAll (mapper, query, opts) { opts || (opts = {}) query || (query = {}) return this._pluck(mapper, this.filterSequence(this.selectTable(mapper, opts), query), opts) .run(this.getOpt('runOpts', opts)) .then((records) => [records, {}]) }, _pluck (mapper, rql, opts) { if (utils.isString(opts.fields)) { opts.fields = [opts.fields] } if (utils.isArray(opts.fields)) { return rql.pluck(...opts.fields) } return rql }, _sum (mapper, field, query, opts) { if (!utils.isString(field)) { throw new Error('field must be a string!') } opts || (opts = {}) query || (query = {}) return this.filterSequence(this.selectTable(mapper, opts), query) .sum(field) .run(this.getOpt('runOpts', opts)) .then((sum) => [sum, {}]) }, _update (mapper, id, props, opts) { props || (props = {}) opts || (opts = {}) const updateOpts = this.getOpt('updateOpts', opts) updateOpts.returnChanges = true return this.selectTable(mapper, opts) .get(id) .update(props, updateOpts) .run(this.getOpt('runOpts', opts)) .then((cursor) => { this._handleErrors(cursor) if (cursor.skipped) { throw new Error('Not Found') } else if (cursor && cursor.changes && cursor.changes.length && cursor.changes[0].new_val) { return [cursor.changes[0].new_val, cursor] } else { return this._find(mapper, id, opts) } }) }, _updateAll (mapper, props, query, opts) { props || (props = {}) query || (query = {}) opts || (opts = {}) const updateOpts = this.getOpt('updateOpts', opts) updateOpts.returnChanges = true return this.filterSequence(this.selectTable(mapper, opts), query) .update(props, updateOpts) .run(this.getOpt('runOpts', opts)) .then((cursor) => { let records = [] this._handleErrors(cursor) if (cursor && cursor.changes && cursor.changes.length) { records = cursor.changes.map((change) => change.new_val) } return [records, cursor] }) }, _updateMany (mapper, records, opts) { records || (records = []) opts || (opts = {}) const insertOpts = this.getOpt('insertOpts', opts) insertOpts.returnChanges = true insertOpts.conflict = 'update' return this.selectTable(mapper, opts) .insert(records, insertOpts) .run(this.getOpt('runOpts', opts)) .then((cursor) => { records = [] this._handleErrors(cursor) if (cursor && cursor.changes && cursor.changes.length) { records = cursor.changes.map((change) => change.new_val) } return [records, cursor] }) }, _applyWhereFromObject (where) { const fields = [] const ops = [] const predicates = [] utils.forOwn(where, (clause, field) => { if (!utils.isObject(clause)) { clause = { '==': clause } } utils.forOwn(clause, (expr, op) => { fields.push(field) ops.push(op) predicates.push(expr) }) }) return { fields, ops, predicates } }, _applyWhereFromArray (where) { const groups = [] where.forEach((_where, i) => { if (utils.isString(_where)) { return } const prev = where[i - 1] const parser = utils.isArray(_where) ? this._applyWhereFromArray : this._applyWhereFromObject const group = parser.call(this, _where) if (prev === 'or') { group.isOr = true } groups.push(group) }) groups.isArray = true return groups }, _testObjectGroup (rql, group, row, opts) { let i const r = this.r const fields = group.fields const ops = group.ops const predicates = group.predicates const len = ops.length for (i = 0; i < len; i++) { let op = ops[i] const isOr = op.charAt(0) === '|' op = isOr ? op.substr(1) : op const predicateFn = this.getOperator(op, opts) if (predicateFn) { const predicateResult = predicateFn(r, row, fields[i], predicates[i]) if (isOr) { rql = rql ? rql.or(predicateResult) : predicateResult } else { rql = rql ? rql.and(predicateResult) : predicateResult } } else { throw new Error(`Operator ${op} not supported!`) } } return rql }, _testArrayGroup (rql, groups, row, opts) { let i const len = groups.length for (i = 0; i < len; i++) { const group = groups[i] let subQuery if (group.isArray) { subQuery = this._testArrayGroup(rql, group, row, opts) } else { subQuery = this._testObjectGroup(null, group, row, opts) } if (groups[i - 1]) { if (group.isOr) { rql = rql.or(subQuery) } else { rql = rql.and(subQuery) } } else { rql = rql ? rql.and(subQuery) : subQuery } } return rql }, /** * Apply the specified selection query to the provided RQL sequence. * * @name RethinkDBAdapter#filterSequence * @method * @param {object} mapper The mapper. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. */ filterSequence (sequence, query, opts) { const r = this.r query = utils.plainCopy(query || {}) opts || (opts = {}) opts.operators || (opts.operators = {}) query.where || (query.where = {}) query.orderBy || (query.orderBy = query.sort) query.orderBy || (query.orderBy = []) query.skip || (query.skip = query.offset) // Transform non-keyword properties to "where" clause configuration utils.forOwn(query, (config, keyword) => { if (reserved.indexOf(keyword) === -1) { if (utils.isObject(config)) { query.where[keyword] = config } else { query.where[keyword] = { '==': config } } delete query[keyword] } }) let rql = sequence // Filter let groups if (utils.isObject(query.where) && Object.keys(query.where).length !== 0) { groups = this._applyWhereFromArray([query.where]) } else if (utils.isArray(query.where)) { groups = this._applyWhereFromArray(query.where) } if (groups) { rql = rql.filter((row) => this._testArrayGroup(null, groups, row, opts) || true) } // Sort if (query.orderBy) { if (utils.isString(query.orderBy)) { query.orderBy = [ [query.orderBy, 'asc'] ] } for (var i = 0; i < query.orderBy.length; i++) { if (utils.isString(query.orderBy[i])) { query.orderBy[i] = [query.orderBy[i], 'asc'] } rql = (query.orderBy[i][1] || '').toUpperCase() === 'DESC' ? rql.orderBy(r.desc(query.orderBy[i][0])) : rql.orderBy(query.orderBy[i][0]) } } // Offset if (query.skip) { rql = rql.skip(+query.skip) } // Limit if (query.limit) { rql = rql.limit(+query.limit) } return rql }, selectDb (opts) { return this.r.db(utils.isUndefined(opts.db) ? this.rOpts.db : opts.db) }, selectTable (mapper, opts) { return this.selectDb(opts).table(mapper.table || snakeCase(mapper.name)) }, waitForDb (opts) { opts || (opts = {}) const db = utils.isUndefined(opts.db) ? this.rOpts.db : opts.db if (!this.databases[db]) { this.databases[db] = this.r.branch( this.r.dbList().contains(db), true, this.r.dbCreate(db) ).run() } return this.databases[db] }, waitForTable (mapper, opts) { opts || (opts = {}) const table = utils.isString(mapper) ? mapper : (mapper.table || snakeCase(mapper.name)) let db = utils.isUndefined(opts.db) ? this.rOpts.db : opts.db return this.waitForDb(opts).then(() => { this.tables[db] = this.tables[db] || {} if (!this.tables[db][table]) { this.tables[db][table] = this.r.branch(this.r.db(db).tableList().contains(table), true, this.r.db(db).tableCreate(table)).run() } return this.tables[db][table] }) }, waitForIndex (table, index, opts) { opts || (opts = {}) let db = utils.isUndefined(opts.db) ? this.rOpts.db : opts.db return this.waitForDb(opts).then(() => this.waitForTable(table, opts)).then(() => { this.indices[db] = this.indices[db] || {} this.indices[db][table] = this.indices[db][table] || {} if (!this.tables[db][table][index]) { this.tables[db][table][index] = this.r.branch(this.r.db(db).table(table).indexList().contains(index), true, this.r.db(db).table(table).indexCreate(index)).run().then(() => { return this.r.db(db).table(table).indexWait(index).run() }) } return this.tables[db][table][index] }) }, /** * Return the number of records that match the selection query. * * @name RethinkDBAdapter#count * @method * @param {object} mapper the mapper. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ count (mapper, query, opts) { opts || (opts = {}) query || (query = {}) return this.waitForTable(mapper, opts) .then(() => __super__.count.call(this, mapper, query, opts)) }, /** * Create a new record. * * @name RethinkDBAdapter#create * @method * @param {object} mapper The mapper. * @param {object} props The record to be created. * @param {object} [opts] Configuration options. * @param {object} [opts.insertOpts] Options to pass to r#insert. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ create (mapper, props, opts) { props || (props = {}) opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.create.call(this, mapper, props, opts)) }, /** * Create multiple records in a single batch. * * @name RethinkDBAdapter#createMany * @method * @param {object} mapper The mapper. * @param {object} props The records to be created. * @param {object} [opts] Configuration options. * @param {object} [opts.insertOpts] Options to pass to r#insert. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ createMany (mapper, props, opts) { props || (props = {}) opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.createMany.call(this, mapper, props, opts)) }, /** * Destroy the record with the given primary key. * * @name RethinkDBAdapter#destroy * @method * @param {object} mapper The mapper. * @param {(string|number)} id Primary key of the record to destroy. * @param {object} [opts] Configuration options. * @param {object} [opts.deleteOpts] Options to pass to r#delete. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ destroy (mapper, id, opts) { opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.destroy.call(this, mapper, id, opts)) }, /** * Destroy the records that match the selection query. * * @name RethinkDBAdapter#destroyAll * @method * @param {object} mapper the mapper. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {object} [opts.deleteOpts] Options to pass to r#delete. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ destroyAll (mapper, query, opts) { opts || (opts = {}) query || (query = {}) return this.waitForTable(mapper, opts) .then(() => __super__.destroyAll.call(this, mapper, query, opts)) }, /** * Retrieve the record with the given primary key. * * @name RethinkDBAdapter#find * @method * @param {object} mapper The mapper. * @param {(string|number)} id Primary key of the record to retrieve. * @param {object} [opts] Configuration options. * @param {string[]} [opts.fields] Select a subset of fields to be returned. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @param {string[]} [opts.with=[]] Relations to eager load. * @return {Promise} */ find (mapper, id, opts) { opts || (opts = {}) opts.with || (opts.with = []) const relationList = mapper.relationList || [] let tasks = [this.waitForTable(mapper, opts)] relationList.forEach((def) => { const relationName = def.relation const relationDef = def.getRelation() if (!opts.with || opts.with.indexOf(relationName) === -1) { return } if (def.foreignKey && def.type !== 'belongsTo') { if (def.type === 'belongsTo') { tasks.push(this.waitForIndex(mapper.table || snakeCase(mapper.name), def.foreignKey, opts)) } else { tasks.push(this.waitForIndex(relationDef.table || snakeCase(relationDef.name), def.foreignKey, opts)) } } }) return Promise.all(tasks).then(() => __super__.find.call(this, mapper, id, opts)) }, /** * Retrieve the records that match the selection query. * * @name RethinkDBAdapter#findAll * @method * @param {object} mapper The mapper. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {string[]} [opts.fields] Select a subset of fields to be returned. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @param {string[]} [opts.with=[]] Relations to eager load. * @return {Promise} */ findAll (mapper, query, opts) { opts || (opts = {}) opts.with || (opts.with = []) query || (query = {}) const relationList = mapper.relationList || [] let tasks = [this.waitForTable(mapper, opts)] relationList.forEach((def) => { const relationName = def.relation const relationDef = def.getRelation() if (!opts.with || opts.with.indexOf(relationName) === -1) { return } if (def.foreignKey && def.type !== 'belongsTo') { if (def.type === 'belongsTo') { tasks.push(this.waitForIndex(mapper.table || snakeCase(mapper.name), def.foreignKey, opts)) } else { tasks.push(this.waitForIndex(relationDef.table || snakeCase(relationDef.name), def.foreignKey, opts)) } } }) return Promise.all(tasks).then(() => __super__.findAll.call(this, mapper, query, opts)) }, /** * Resolve the predicate function for the specified operator based on the * given options and this adapter's settings. * * @name RethinkDBAdapter#getOperator * @method * @param {string} operator The name of the operator. * @param {object} [opts] Configuration options. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @return {*} The predicate function for the specified operator. */ getOperator (operator, opts) { opts || (opts = {}) opts.operators || (opts.operators = {}) let ownOps = this.operators || {} return utils.isUndefined(opts.operators[operator]) ? ownOps[operator] : opts.operators[operator] }, /** * Return the sum of the specified field of records that match the selection * query. * * @name RethinkDBAdapter#sum * @method * @param {object} mapper The mapper. * @param {string} field The field to sum. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ sum (mapper, field, query, opts) { opts || (opts = {}) query || (query = {}) return this.waitForTable(mapper, opts) .then(() => __super__.sum.call(this, mapper, field, query, opts)) }, /** * Apply the given update to the record with the specified primary key. * * @name RethinkDBAdapter#update * @method * @param {object} mapper The mapper. * @param {(string|number)} id The primary key of the record to be updated. * @param {object} props The update to apply to the record. * @param {object} [opts] Configuration options. * @param {object} [opts.updateOpts] Options to pass to r#update. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ update (mapper, id, props, opts) { props || (props = {}) opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.update.call(this, mapper, id, props, opts)) }, /** * Apply the given update to all records that match the selection query. * * @name RethinkDBAdapter#updateAll * @method * @param {object} mapper The mapper. * @param {object} props The update to apply to the selected records. * @param {object} [query] Selection query. * @param {object} [query.where] Filtering criteria. * @param {string|Array} [query.orderBy] Sorting criteria. * @param {string|Array} [query.sort] Same as `query.sort`. * @param {number} [query.limit] Limit results. * @param {number} [query.skip] Offset results. * @param {number} [query.offset] Same as `query.skip`. * @param {object} [opts] Configuration options. * @param {object} [opts.operators] Override the default predicate functions * for specified operators. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @param {object} [opts.updateOpts] Options to pass to r#update. * @return {Promise} */ updateAll (mapper, props, query, opts) { props || (props = {}) query || (query = {}) opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.updateAll.call(this, mapper, props, query, opts)) }, /** * Update the given records in a single batch. * * @name RethinkDBAdapter#updateMany * @method * @param {object} mapper The mapper. * @param {Object[]} records The records to update. * @param {object} [opts] Configuration options. * @param {object} [opts.insertOpts] Options to pass to r#insert. * @param {boolean} [opts.raw=false] Whether to return a more detailed * response object. * @param {object} [opts.runOpts] Options to pass to r#run. * @return {Promise} */ updateMany (mapper, records, opts) { records || (records = []) opts || (opts = {}) return this.waitForTable(mapper, opts) .then(() => __super__.updateMany.call(this, mapper, records, opts)) } }) /** * Details of the current version of the `js-data-rethinkdb` module. * * @example <caption>ES2015 modules import</caption> * import { version } from 'js-data-rethinkdb'; * console.log(version.full); * * @example <caption>CommonJS import</caption> * const version = require('js-data-rethinkdb').version; * console.log(version.full); * * @name module:js-data-rethinkdb.version * @type {object} * @property {string} version.full The full semver value. * @property {number} version.major The major version number. * @property {number} version.minor The minor version number. * @property {number} version.patch The patch version number. * @property {(string|boolean)} version.alpha The alpha version value, * otherwise `false` if the current version is not alpha. * @property {(string|boolean)} version.beta The beta version value, * otherwise `false` if the current version is not beta. */ export const version = '<%= version %>' /** * {@link RethinkDBAdapter} class. * * @example <caption>ES2015 modules import</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter(); * * @example <caption>CommonJS import</caption> * const RethinkDBAdapter = require('js-data-rethinkdb').RethinkDBAdapter; * const adapter = new RethinkDBAdapter(); * * @name module:js-data-rethinkdb.RethinkDBAdapter * @see RethinkDBAdapter * @type {Constructor} */ /** * Registered as `js-data-rethinkdb` in NPM. * * @example <caption>Install from NPM</caption> * npm i --save js-data-rethinkdb js-data * * @example <caption>ES2015 modules import</caption> * import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const adapter = new RethinkDBAdapter(); * * @example <caption>CommonJS import</caption> * const RethinkDBAdapter = require('js-data-rethinkdb').RethinkDBAdapter; * const adapter = new RethinkDBAdapter(); * * @module js-data-rethinkdb */ /** * Create a subclass of this RethinkDBAdapter: * @example <caption>RethinkDBAdapter.extend</caption> * // Normally you would do: import { RethinkDBAdapter } from 'js-data-rethinkdb'; * const JSDataRethinkDB = require('js-data-rethinkdb'); * const { RethinkDBAdapter } = JSDataRethinkDB; * console.log('Using JSDataRethinkDB v' + JSDataRethinkDB.version.full); * * // Extend the class using ES2015 class syntax. * class CustomRethinkDBAdapterClass extends RethinkDBAdapter { * foo () { return 'bar'; } * static beep () { return 'boop'; } * } * const customRethinkDBAdapter = new CustomRethinkDBAdapterClass(); * console.log(customRethinkDBAdapter.foo()); * console.log(CustomRethinkDBAdapterClass.beep()); * * // Extend the class using alternate method. * const OtherRethinkDBAdapterClass = RethinkDBAdapter.extend({ * foo () { return 'bar'; } * }, { * beep () { return 'boop'; } * }); * const otherRethinkDBAdapter = new OtherRethinkDBAdapterClass(); * console.log(otherRethinkDBAdapter.foo()); * console.log(OtherRethinkDBAdapterClass.beep()); * * // Extend the class, providing a custom constructor. * function AnotherRethinkDBAdapterClass () { * RethinkDBAdapter.call(this); * this.created_at = new Date().getTime(); * } * RethinkDBAdapter.extend({ * constructor: AnotherRethinkDBAdapterClass, * foo () { return 'bar'; } * }, { * beep () { return 'boop'; } * }) * const anotherRethinkDBAdapter = new AnotherRethinkDBAdapterClass(); * console.log(anotherRethinkDBAdapter.created_at); * console.log(anotherRethinkDBAdapter.foo()); * console.log(AnotherRethinkDBAdapterClass.beep()); * * @method RethinkDBAdapter.extend * @param {object} [props={}] Properties to add to the prototype of the * subclass. * @param {object} [props.constructor] Provide a custom constructor function * to be used as the subclass itself. * @param {object} [classProps={}] Static properties to add to the subclass. * @returns {Constructor} Subclass of this RethinkDBAdapter class. * @since 3.0.0 */