| 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; |