pouchdb-find
Version:
Easy-to-use query language for PouchDB
132 lines (115 loc) • 3.28 kB
JavaScript
;
var localUtils = require('./utils');
var abstractMapReduce = require('../../abstract-mapreduce');
var parseField = localUtils.parseField;
//
// One thing about these mappers:
//
// Per the advice of John-David Dalton (http://youtu.be/NthmeLEhDDM),
// what you want to do in this case is optimize for the smallest possible
// function, since that's the thing that gets run over and over again.
//
// This code would be a lot simpler if all the if/elses were inside
// the function, but it would also be a lot less performant.
//
function createDeepMultiMapper(fields, emit) {
return function (doc) {
var toEmit = [];
for (var i = 0, iLen = fields.length; i < iLen; i++) {
var parsedField = parseField(fields[i]);
var value = doc;
for (var j = 0, jLen = parsedField.length; j < jLen; j++) {
var key = parsedField[j];
value = value[key];
if (!value) {
break;
}
}
toEmit.push(value);
}
emit(toEmit);
};
}
function createDeepSingleMapper(field, emit) {
var parsedField = parseField(field);
return function (doc) {
var value = doc;
for (var i = 0, len = parsedField.length; i < len; i++) {
var key = parsedField[i];
value = value[key];
if (!value) {
return; // do nothing
}
}
emit(value);
};
}
function createShallowSingleMapper(field, emit) {
return function (doc) {
emit(doc[field]);
};
}
function createShallowMultiMapper(fields, emit) {
return function (doc) {
var toEmit = [];
for (var i = 0, len = fields.length; i < len; i++) {
toEmit.push(doc[fields[i]]);
}
emit(toEmit);
};
}
function checkShallow(fields) {
for (var i = 0, len = fields.length; i < len; i++) {
var field = fields[i];
if (field.indexOf('.') !== -1) {
return false;
}
}
return true;
}
function createMapper(fields, emit) {
var isShallow = checkShallow(fields);
var isSingle = fields.length === 1;
// notice we try to optimize for the most common case,
// i.e. single shallow indexes
if (isShallow) {
if (isSingle) {
return createShallowSingleMapper(fields[0], emit);
} else { // multi
return createShallowMultiMapper(fields, emit);
}
} else { // deep
if (isSingle) {
return createDeepSingleMapper(fields[0], emit);
} else { // multi
return createDeepMultiMapper(fields, emit);
}
}
}
function mapper(mapFunDef, emit) {
// mapFunDef is a list of fields
var fields = Object.keys(mapFunDef.fields);
return createMapper(fields, emit);
}
/* istanbul ignore next */
function reducer(/*reduceFunDef*/) {
throw new Error('reduce not supported');
}
function ddocValidator(ddoc, viewName) {
var view = ddoc.views[viewName];
// This doesn't actually need to be here apparently, but
// I feel safer keeping it.
/* istanbul ignore if */
if (!view.map || !view.map.fields) {
throw new Error('ddoc ' + ddoc._id +' with view ' + viewName +
' doesn\'t have map.fields defined. ' +
'maybe it wasn\'t created by this plugin?');
}
}
var abstractMapper = abstractMapReduce({
name: 'indexes',
mapper: mapper,
reducer: reducer,
ddocValidator: ddocValidator
});
module.exports = abstractMapper;