diff --git a/lib/dao.js b/lib/dao.js index 658c008b..22d9918b 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1798,174 +1798,174 @@ DataAccessObject.find = function find(query, options, cb) { var near = query && geo.nearFilter(query.where); var supportsGeo = !!connector.buildNearFilter; - if (near) { - if (supportsGeo) { - // convert it - connector.buildNearFilter(query, near); - } else if (query.where) { - // do in memory query - // using all documents - // TODO [fabien] use default scope here? + if (query.where && near && !supportsGeo) { + // do in memory query + // using all documents + // TODO [fabien] use default scope here? - if (options.notify === false) { - queryGeo(query); - } else { - withNotifyGeo(); - } - - function withNotifyGeo() { - var context = { - Model: self, - query: query, - hookState: hookState, - options: options, - }; - self.notifyObserversOf('access', context, function(err, ctx) { - if (err) return cb(err); - queryGeo(ctx.query); - }); - } - function queryGeo(query) { - function geoCallback(err, data) { - var memory = new Memory(); - var modelName = self.modelName; - - if (err) { - cb(err); - } else if (Array.isArray(data)) { - memory.define({ - properties: self.dataSource.definitions[self.modelName].properties, - settings: self.dataSource.definitions[self.modelName].settings, - model: self, - }); - - data.forEach(function(obj) { - memory.create(modelName, obj, options, function() { - // noop - }); - }); - - // FIXME: apply "includes" and other transforms - see allCb below - memory.all(modelName, query, options, cb); - } else { - cb(null, []); - } - } - - if (connector.all.length === 4) { - connector.all(self.modelName, {}, options, geoCallback); - } else { - connector.all(self.modelName, {}, geoCallback); - } - // already handled - return cb.promise; - } + if (options.notify === false) { + queryGeo(query); + } else { + withNotifyGeo(); } - } - var allCb = function(err, data) { - if (!err && Array.isArray(data)) { - async.map(data, function(item, next) { - var Model = self.lookupModel(item); - var obj = new Model(item, { fields: query.fields, applySetters: false, persisted: true }); - - if (query && query.include) { - if (query.collect) { - // The collect property indicates that the query is to return the - // standalone items for a related model, not as child of the parent object - // For example, article.tags - obj = obj.__cachedRelations[query.collect]; - if (obj === null) { - obj = undefined; - } - } else { - // This handles the case to return parent items including the related - // models. For example, Article.find({include: 'tags'}, ...); - // Try to normalize the include - var includes = Inclusion.normalizeInclude(query.include || []); - includes.forEach(function(inc) { - var relationName = inc; - if (utils.isPlainObject(inc)) { - relationName = Object.keys(inc)[0]; - } - - // Promote the included model as a direct property - var included = obj.__cachedRelations[relationName]; - if (Array.isArray(included)) { - included = new List(included, null, obj); - } - if (included) obj.__data[relationName] = included; - }); - delete obj.__data.__cachedRelations; - } - } - if (obj !== undefined) { - if (options.notify === false) { - next(null, obj); - } else { - context = { - Model: Model, - instance: obj, - isNewInstance: false, - hookState: hookState, - options: options, - }; - - Model.notifyObserversOf('loaded', context, function(err) { - if (err) return next(err); - - next(null, obj); - }); - } - } else { - next(); - } - }, - function(err, results) { + function withNotifyGeo() { + var context = { + Model: self, + query: query, + hookState: hookState, + options: options, + }; + self.notifyObserversOf('access', context, function(err, ctx) { if (err) return cb(err); - - // When applying query.collect, some root items may not have - // any related/linked item. We store `undefined` in the results - // array in such case, which is not desirable from API consumer's - // point of view. - results = results.filter(isDefined); - - if (data && data.countBeforeLimit) { - results.countBeforeLimit = data.countBeforeLimit; - } - if (!supportsGeo && near) { - results = geo.filter(results, near); - } - - cb(err, results); + queryGeo(ctx.query); }); - } else { - cb(err, data || []); } - }; - if (options.notify === false) { - if (connector.all.length === 4) { - connector.all(self.modelName, query, options, allCb); - } else { - connector.all(self.modelName, query, allCb); + function queryGeo(query) { + function geoCallback(err, data) { + var memory = new Memory(); + var modelName = self.modelName; + + if (err) { + cb(err); + } else if (Array.isArray(data)) { + memory.define({ + properties: self.dataSource.definitions[self.modelName].properties, + settings: self.dataSource.definitions[self.modelName].settings, + model: self, + }); + + data.forEach(function(obj) { + memory.create(modelName, obj, options, function() { + // noop + }); + }); + + // FIXME: apply "includes" and other transforms - see allCb below + memory.all(modelName, query, options, cb); + } else { + cb(null, []); + } + } + + if (connector.all.length === 4) { + connector.all(self.modelName, {}, options, geoCallback); + } else { + connector.all(self.modelName, {}, geoCallback); + } + // already handled + return cb.promise; } } else { - var context = { - Model: this, - query: query, - hookState: hookState, - options: options, - }; - this.notifyObserversOf('access', context, function(err, ctx) { - if (err) return cb(err); + if (near && supportsGeo) { + connector.buildNearFilter(query, near); + } - connector.all.length === 4 ? - connector.all(self.modelName, ctx.query, options, allCb) : - connector.all(self.modelName, ctx.query, allCb); - }); + var allCb = function(err, data) { + if (!err && Array.isArray(data)) { + async.map(data, function(item, next) { + var Model = self.lookupModel(item); + var obj = new Model(item, { fields: query.fields, applySetters: false, persisted: true }); + + if (query && query.include) { + if (query.collect) { + // The collect property indicates that the query is to return the + // standalone items for a related model, not as child of the parent object + // For example, article.tags + obj = obj.__cachedRelations[query.collect]; + if (obj === null) { + obj = undefined; + } + } else { + // This handles the case to return parent items including the related + // models. For example, Article.find({include: 'tags'}, ...); + // Try to normalize the include + var includes = Inclusion.normalizeInclude(query.include || []); + includes.forEach(function(inc) { + var relationName = inc; + if (utils.isPlainObject(inc)) { + relationName = Object.keys(inc)[0]; + } + + // Promote the included model as a direct property + var included = obj.__cachedRelations[relationName]; + if (Array.isArray(included)) { + included = new List(included, null, obj); + } + if (included) obj.__data[relationName] = included; + }); + delete obj.__data.__cachedRelations; + } + } + if (obj !== undefined) { + if (options.notify === false) { + next(null, obj); + } else { + context = { + Model: Model, + instance: obj, + isNewInstance: false, + hookState: hookState, + options: options, + }; + + Model.notifyObserversOf('loaded', context, function(err) { + if (err) return next(err); + + next(null, obj); + }); + } + } else { + next(); + } + }, + function(err, results) { + if (err) return cb(err); + + // When applying query.collect, some root items may not have + // any related/linked item. We store `undefined` in the results + // array in such case, which is not desirable from API consumer's + // point of view. + results = results.filter(isDefined); + + if (data && data.countBeforeLimit) { + results.countBeforeLimit = data.countBeforeLimit; + } + if (!supportsGeo && near) { + results = geo.filter(results, near); + } + + cb(err, results); + }); + } else { + cb(err, data || []); + } + }; + + if (options.notify === false) { + if (connector.all.length === 4) { + connector.all(self.modelName, query, options, allCb); + } else { + connector.all(self.modelName, query, allCb); + } + } else { + var context = { + Model: this, + query: query, + hookState: hookState, + options: options, + }; + this.notifyObserversOf('access', context, function(err, ctx) { + if (err) return cb(err); + + connector.all.length === 4 ? + connector.all(self.modelName, ctx.query, options, allCb) : + connector.all(self.modelName, ctx.query, allCb); + }); + } + return cb.promise; } - return cb.promise; }; function isDefined(value) { diff --git a/test/persistence-hooks.suite.js b/test/persistence-hooks.suite.js index 4663bb64..e776f58b 100644 --- a/test/persistence-hooks.suite.js +++ b/test/persistence-hooks.suite.js @@ -86,6 +86,19 @@ module.exports = function(dataSource, should, connectorCapabilities) { }); }); + it('triggers correct hooks when near filter is used', function(done) { + monitorHookExecution(); + var query = { where: + { location: { near: '10,20', maxDistance: '10', unit: 'meters' }}, + }; + + TestModel.find(query, function(err, list) { + if (err) return done(err); + hookMonitor.names.should.eql(['access']); + done(); + }); + }); + it('should not trigger hooks, if notify is false', function(done) { monitorHookExecution(); TestModel.find(