blob: 46ff5eb1d2c5f612e97d0af536f92aae4bb11968 [file] [log] [blame]
import abstractMapReduce from 'pouchdb-abstract-mapreduce';
import { parseField } from 'pouchdb-selector-core';
//
// 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 (typeof value === 'undefined') {
return; // don't emit
}
}
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 (typeof value === 'undefined') {
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(
/* localDocName */ 'indexes',
mapper,
reducer,
ddocValidator
);
export default abstractMapper;