UNPKG

pouchdb-find

Version:
135 lines (112 loc) 3.88 kB
'use strict'; var utils = require('../../../utils'); var clone = utils.clone; var getIndexes = require('../get-indexes'); var collate = require('pouchdb-collate').collate; var abstractMapper = require('../abstract-mapper'); var planQuery = require('./query-planner'); var localUtils = require('../utils'); var filterInMemoryFields = require('./in-memory-filter'); var massageSelector = localUtils.massageSelector; var massageSort = localUtils.massageSort; var getValue = localUtils.getValue; var validateFindRequest = localUtils.validateFindRequest; var reverseOptions = localUtils.reverseOptions; var filterInclusiveStart = localUtils.filterInclusiveStart; var Promise = utils.Promise; function indexToSignature(index) { // remove '_design/' return index.ddoc.substring(8) + '/' + index.name; } function doAllDocs(db, originalOpts) { var opts = clone(originalOpts); // CouchDB responds in weird ways when you provide a non-string to _id; // we mimic the behavior for consistency. See issue66 tests for details. if (opts.descending) { if ('endkey' in opts && typeof opts.endkey !== 'string') { opts.endkey = ''; } if ('startkey' in opts && typeof opts.startkey !== 'string') { opts.limit = 0; } } else { if ('startkey' in opts && typeof opts.startkey !== 'string') { opts.startkey = ''; } if ('endkey' in opts && typeof opts.endkey !== 'string') { opts.limit = 0; } } if ('key' in opts && typeof opts.key !== 'string') { opts.limit = 0; } return db.allDocs(opts); } function find(db, requestDef) { if (requestDef.selector) { requestDef.selector = massageSelector(requestDef.selector); } if (requestDef.sort) { requestDef.sort = massageSort(requestDef.sort); } validateFindRequest(requestDef); return getIndexes(db).then(function (getIndexesRes) { var queryPlan = planQuery(requestDef, getIndexesRes.indexes); var indexToUse = queryPlan.index; var opts = utils.extend(true, { include_docs: true, reduce: false }, queryPlan.queryOpts); if ('startkey' in opts && 'endkey' in opts && collate(opts.startkey, opts.endkey) > 0) { // can't possibly return any results, startkey > endkey return {docs: []}; } var isDescending = requestDef.sort && typeof requestDef.sort[0] !== 'string' && getValue(requestDef.sort[0]) === 'desc'; if (isDescending) { // either all descending or all ascending opts.descending = true; opts = reverseOptions(opts); } if (!queryPlan.inMemoryFields.length) { // no in-memory filtering necessary, so we can let the // database do the limit/skip for us if ('limit' in requestDef) { opts.limit = requestDef.limit; } if ('skip' in requestDef) { opts.skip = requestDef.skip; } } return Promise.resolve().then(function () { if (indexToUse.name === '_all_docs') { return doAllDocs(db, opts); } else { var signature = indexToSignature(indexToUse); return abstractMapper.query.call(db, signature, opts); } }).then(function (res) { if (opts.inclusive_start === false) { // may have to manually filter the first one, // since couchdb has no true inclusive_start option res.rows = filterInclusiveStart(res.rows, opts.startkey, indexToUse); } if (queryPlan.inMemoryFields.length) { // need to filter some stuff in-memory res.rows = filterInMemoryFields(res.rows, requestDef, queryPlan.inMemoryFields); } return { docs: res.rows.map(function (row) { var doc = row.doc; if (requestDef.fields) { return utils.pick(doc, requestDef.fields); } return doc; }) }; }); }); } module.exports = find;