UNPKG

feathers-couchdb

Version:
391 lines (321 loc) 13.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.default = init; var _uberproto = require('uberproto'); var _uberproto2 = _interopRequireDefault(_uberproto); var _feathersQueryFilters = require('feathers-query-filters'); var _feathersQueryFilters2 = _interopRequireDefault(_feathersQueryFilters); var _errorHandler = require('./error-handler'); var _errorHandler2 = _interopRequireDefault(_errorHandler); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Service = function () { function Service(options) { _classCallCheck(this, Service); if (!options) { throw new Error('CouchDB options have to be provided'); } if (!options.connection) { throw new Error('You must provide couchdb connection'); } if (!options.Model) { throw new Error('You must provide a Model name'); } this.id = options.id || '_id'; this.events = options.events || []; this.connection = options.connection; this.Model = options.Model.toString().toLowerCase(); this.paginate = options.paginate || {}; this.db = this.database(); } _createClass(Service, [{ key: 'database', value: function database() { var db = this.connection.database(this.Model); return new Promise(function (resolve, reject) { db.exists(function (err, exists) { if (err) { return reject(err); } if (exists) { resolve(db); } else { db.create(function (err) { if (err) { if (err.name === 'CouchError' && err.error === 'file_exists') { return resolve(db); } return reject(err); } resolve(db); }); } }); }); } }, { key: 'extend', value: function extend(obj) { return _uberproto2.default.extend(obj, this); } }, { key: '_createMapRCondition', value: function _createMapRCondition(id, key, query) { var self = this, METHOD = ['$in', '$nin', '$ne', '$not', '$lt', '$lte', '$gt', '$gte'], COMPAT = ['.indexOf', '.indexOf', '!==', '!==', '<', '<=', '>', '>=']; if (id === key && id === '$or') { var retval = ''; for (var i = 0, N = query.length; i < N; i++) { (function (obj) { for (var _k in obj) { retval += ' || ' + self._createMapRCondition(_k, _k, obj[_k]); } })(query[i]); } return '(' + retval.substr(4) + ')'; } else if (id === key && (typeof query === 'undefined' ? 'undefined' : _typeof(query)) !== 'object') { return 'doc.' + id + '==' + JSON.stringify(query); } else { var arr = ''; for (var k in query) { var repl = COMPAT[METHOD.indexOf(k)]; var qb = 'doc.' + id + repl + query[k]; if (k === '$in') { qb = 'RegExp(\'' + query[k].join('|') + '\',\'gi\').test(doc.' + id + ')'; } else if (k === '$nin') { qb = '!RegExp(\'' + query[k].join('|') + '\',\'gi\').test(doc.' + id + ')'; } arr += ' && ' + qb; } return arr.substr(4); } } // avoid using temporaryView CouchDB, use predefined view! }, { key: '_createTempView', value: function _createTempView(filters, query) { var self = this; // var fields = 'doc'; if (filters.$select) { var obj = '_id: doc._id'; obj += ', _rev: doc._rev'; for (var i = 0, N = filters.$select.length; i < N; i++) { obj += ', ' + filters.$select[i] + ': doc.' + filters.$select[i]; } fields = '{' + obj + '}'; } var conditions = 'doc'; for (var key in query) { (function (k, v) { conditions += ' && ' + self._createMapRCondition(k, k, v); })(key, query[key]); } var fnBody = '\n if (' + conditions + ') {\n emit(null, ' + fields + ');\n }'; /*jshint evil: true */ var fn = new Function('doc', fnBody); var FntoString = fn.toString().replace(fn.name, ''); fn.toString = function () { return FntoString.replace(/\r?\n|\/\*\*\/| /gim, ''); }; return fn; } }, { key: 'find', value: function find(params) { var self = this; var paginate = params && typeof params.paginate !== 'undefined' ? params.paginate : this.paginate; var q = params.query.q; var _filter = (0, _feathersQueryFilters2.default)(params.query || {}, paginate); var filters = _filter.filters; var query = _filter.query; return this.db.then(function (db) { return new Promise(function (resolve, reject) { var opts = { limit: filters.$limit || paginate.default || 100, skip: filters.$skip || 0 }; var promisify = function promisify(err, res) { if (err) { return reject(err); } for (var i = 0, N = res.length; i < N; i++) { res[i] = res[i].value; res[i].id = res[i]._id; delete res[i]._id; delete res[i]._rev; var arr = filters.$select; if (arr && Array.isArray(arr) && arr.length > 0) { var tmpData = {}; for (var j = 0, _N = arr.length; j < _N; j++) { tmpData[arr[j]] = res[i][arr[j]]; } res[i] = tmpData; } } resolve(res); }; if (q) { return db.view(q, opts, promisify); } var viewFn = self._createTempView(filters, query); db.temporaryView({ map: viewFn }, function (err, res) { if (err) { // try to create new _design view // (ie. Cloudant doesn't allow temporaryView) db.save('_design/feathers', { views: { temp: { map: viewFn } } }, function (err, obj) { db.view('feathers/temp', opts, function (err, res) { if (err) { return promisify(err, res); } db.remove('_design/feathers', obj.rev, function (err) { return promisify(err, res); }); }); }); } else { promisify(err, res); } }); }); }).catch(_errorHandler2.default); } }, { key: '_get', value: function _get(id) { return this.db.then(function (db) { return new Promise(function (resolve, reject) { db.get(id, function (err, res) { if (err) { return reject(err); } resolve(res); }); }); }); } }, { key: 'get', value: function get(id, params) { return this._get(id, params).then(function (res) { var obj = Object.assign({ id: res._id }, res); delete obj._id; delete obj._rev; return obj; }).catch(_errorHandler2.default); } }, { key: 'create', value: function create(data) { var entry = void 0, _id = void 0; // bulk insert if (Array.isArray(data)) { var N = data.length; entry = new Array(N); for (var i = 0; i < N; i++) { entry[i] = Object.assign({}, data[i]); } } // single doc insert else { if (data._id || data.id) { _id = data._id || data.id; data.id = data._id = undefined; } entry = Object.assign({}, data); if (_id && _id.startsWith('_design/')) { _id = _id.toLowerCase(); } } return this.db.then(function (db) { return new Promise(function (resolve, reject) { var promisify = function promisify(err, res) { if (err) { return reject(err); } entry.id = res.id; resolve(entry); }; _id ? db.save(_id, entry, promisify) : db.save(entry, promisify); }); }).catch(_errorHandler2.default); } }, { key: 'patch', value: function patch(id, data) { var self = this; return this.db.then(function (db) { return new Promise(function (resolve, reject) { if (data.id) { delete data.id; } if (data._id) { delete data._id; } var entry = Object.assign({}, data); db.merge(id, entry, function (err) { if (err) { return reject(err); } resolve(self.get(id)); }); }); }).catch(_errorHandler2.default); } }, { key: 'update', value: function update(id, data, params) { if (!Array.isArray(data)) { return Promise.reject('Not replacing multiple records. Did you mean `patch`?'); } var promises = new Array(data.length); for (var i = 0, N = data.length; i < N; i++) { promises[i] = this.patch(data[i]._id || data[i].id, data[i], params); } return Promise.all(promises).catch(_errorHandler2.default); } }, { key: 'remove', value: function remove(id, params) { var _this = this; var promise = void 0; params = params || {}; if (!params.rev && !params._rev) { promise = this._get(id).then(function (doc) { params.rev = doc.rev || doc._rev; return _this.db; }); } else { promise = this.db; } return promise.then(function (db) { return new Promise(function (resolve, reject) { db.remove(id, params.rev || params._rev, function (err, res) { if (err) { return reject(err); } resolve(res); }); }); }).catch(_errorHandler2.default); } }]); return Service; }(); function init(options) { return new Service(options); } init.Service = Service; module.exports = exports['default'];