Add in memory geo filtering.
This commit is contained in:
parent
d730aab674
commit
6cb53e5001
|
@ -5,6 +5,8 @@ exports.initialize = function initializeSchema(schema, callback) {
|
|||
schema.adapter.connect(callback);
|
||||
};
|
||||
|
||||
exports.Memory = Memory;
|
||||
|
||||
function Memory(m) {
|
||||
if (m) {
|
||||
this.isTransaction = true;
|
||||
|
@ -84,25 +86,9 @@ Memory.prototype.destroy = function destroy(model, id, callback) {
|
|||
Memory.prototype.fromDb = function(model, data) {
|
||||
if (!data) return null;
|
||||
data = JSON.parse(data);
|
||||
var ctor = this._models[model].model;
|
||||
var props = this._models[model].properties;
|
||||
Object.keys(data).forEach(function (key) {
|
||||
var val = data[key];
|
||||
if (typeof val === 'undefined' || val === null) {
|
||||
return;
|
||||
}
|
||||
if (props[key]) {
|
||||
switch(props[key].type.name) {
|
||||
case 'Date':
|
||||
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
|
||||
break;
|
||||
case 'Boolean':
|
||||
val = new Boolean(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
data[key] = val;
|
||||
});
|
||||
return data;
|
||||
return ctor(data);
|
||||
};
|
||||
|
||||
Memory.prototype.all = function all(model, filter, callback) {
|
||||
|
@ -112,7 +98,6 @@ Memory.prototype.all = function all(model, filter, callback) {
|
|||
}.bind(this));
|
||||
var isGeo = false;
|
||||
|
||||
|
||||
if (filter) {
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
|
@ -137,9 +122,9 @@ Memory.prototype.all = function all(model, filter, callback) {
|
|||
|
||||
// geo sorting
|
||||
if(nearFilter) {
|
||||
nodes = geo.filter(nodes, nearFilter);
|
||||
nodes = geo.filter(nodes, nearFilter);
|
||||
}
|
||||
|
||||
|
||||
// do we need some filtration?
|
||||
if (filter.where) {
|
||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||
|
@ -194,6 +179,9 @@ function applyFilter(filter) {
|
|||
if (typeof example === 'undefined') return undefined;
|
||||
if (typeof value === 'undefined') return undefined;
|
||||
if (typeof example === 'object') {
|
||||
// ignore geo near filter
|
||||
if(example.near) return true;
|
||||
|
||||
if (example.inq) {
|
||||
if (!value) return false;
|
||||
for (var i = 0; i < example.inq.length; i += 1) {
|
||||
|
@ -271,11 +259,14 @@ Memory.prototype.exec = function(callback) {
|
|||
setTimeout(callback, 50);
|
||||
};
|
||||
|
||||
Memory.prototype.buildNearFilter = function (filter) {
|
||||
// noop
|
||||
}
|
||||
|
||||
function merge(base, update) {
|
||||
if (!base) return update;
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
return base;
|
||||
}
|
||||
|
||||
}
|
54
lib/dao.js
54
lib/dao.js
|
@ -14,6 +14,8 @@ var List = require('./list.js');
|
|||
require('./relations.js');
|
||||
var Inclusion = require('./include.js');
|
||||
var Relation = require('./relations.js');
|
||||
var geo = require('./geo');
|
||||
var Memory = require('./adapters/memory').Memory;
|
||||
|
||||
/**
|
||||
* DAO class - base class for all persist objects
|
||||
|
@ -314,11 +316,55 @@ DataAccessObject.find = function find(params, cb) {
|
|||
params = null;
|
||||
}
|
||||
var constr = this;
|
||||
|
||||
var near = params && geo.nearFilter(params.where);
|
||||
var supportsGeo = !!this.schema.adapter.buildNearFilter;
|
||||
|
||||
if(near) {
|
||||
if(supportsGeo) {
|
||||
// convert it
|
||||
this.schema.adapter.buildNearFilter(filter, near);
|
||||
} else if(params.where) {
|
||||
// do in memory query
|
||||
// using all documents
|
||||
|
||||
|
||||
|
||||
|
||||
this.schema.adapter.all(this.modelName, {}, function (err, data) {
|
||||
var memory = new Memory();
|
||||
var modelName = constr.modelName;
|
||||
|
||||
if(err) {
|
||||
cb(err);
|
||||
} else if(Array.isArray(data)) {
|
||||
memory.define({
|
||||
properties: constr.schema.definitions[constr.modelName].properties,
|
||||
settings: constr.schema.definitions[constr.modelName].settings,
|
||||
model: constr
|
||||
});
|
||||
|
||||
data.forEach(function (obj) {
|
||||
memory.create(modelName, obj, function () {
|
||||
// noop
|
||||
});
|
||||
});
|
||||
|
||||
memory.all(modelName, params, cb);
|
||||
} else {
|
||||
cb(null, []);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
// already handled
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.schema.adapter.all(this.modelName, params, function (err, data) {
|
||||
if (data && data.forEach) {
|
||||
data.forEach(function (d, i) {
|
||||
var obj = new constr;
|
||||
obj._initProperties(d, false);
|
||||
var obj = new constr(d);
|
||||
if (params && params.include && params.collect) {
|
||||
data[i] = obj.__cachedRelations[params.collect];
|
||||
} else {
|
||||
|
@ -328,6 +374,10 @@ DataAccessObject.find = function find(params, cb) {
|
|||
if (data && data.countBeforeLimit) {
|
||||
data.countBeforeLimit = data.countBeforeLimit;
|
||||
}
|
||||
if(!supportsGeo && near) {
|
||||
data = geo.filter(data, near);
|
||||
}
|
||||
|
||||
cb(err, data);
|
||||
}
|
||||
else
|
||||
|
|
41
lib/geo.js
41
lib/geo.js
|
@ -46,6 +46,11 @@ exports.filter = function (arr, filter) {
|
|||
|
||||
// filter out objects without locations
|
||||
if(!loc) return;
|
||||
|
||||
if(!(loc instanceof GeoPoint)) {
|
||||
loc = GeoPoint(loc);
|
||||
}
|
||||
|
||||
if(typeof loc.lat !== 'number') return;
|
||||
if(typeof loc.lng !== 'number') return;
|
||||
|
||||
|
@ -87,6 +92,20 @@ function GeoPoint(data) {
|
|||
return new GeoPoint(data);
|
||||
}
|
||||
|
||||
if(typeof data === 'string') {
|
||||
data = data.split(/,\s*/);
|
||||
assert(data.length === 2, 'must provide a string "lng,lat" creating a GeoPoint with a string');
|
||||
}
|
||||
if(Array.isArray(data)) {
|
||||
data = {
|
||||
lng: Number(data[0]),
|
||||
lat: Number(data[1])
|
||||
};
|
||||
} else {
|
||||
data.lng = Number(data.lng);
|
||||
data.lat = Number(data.lat);
|
||||
}
|
||||
|
||||
assert(typeof data === 'object', 'must provide a lat and lng object when creating a GeoPoint');
|
||||
assert(typeof data.lat === 'number', 'lat must be a number when creating a GeoPoint');
|
||||
assert(typeof data.lng === 'number', 'lng must be a number when creating a GeoPoint');
|
||||
|
@ -104,6 +123,13 @@ function GeoPoint(data) {
|
|||
*/
|
||||
|
||||
GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
||||
if(!(a instanceof GeoPoint)) {
|
||||
a = GeoPoint(a);
|
||||
}
|
||||
if(!(b instanceof GeoPoint)) {
|
||||
b = GeoPoint(b);
|
||||
}
|
||||
|
||||
var x1 = a.lat;
|
||||
var y1 = a.lng;
|
||||
|
||||
|
@ -121,6 +147,18 @@ GeoPoint.prototype.distanceTo = function (point, options) {
|
|||
return GeoPoint.distanceBetween(this, point, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple serialization.
|
||||
*/
|
||||
|
||||
GeoPoint.prototype.toString = function () {
|
||||
return this.lng + ',' + this.lat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Si
|
||||
*/
|
||||
|
||||
// ratio of a circle's circumference to its diameter
|
||||
var PI = 3.1415926535897932384626433832795;
|
||||
|
||||
|
@ -154,4 +192,5 @@ function geoDistance(x1, y1, x2, y2, options) {
|
|||
var type = (options && options.type) || 'miles';
|
||||
|
||||
return 2 * Math.asin( c ) * EARTH_RADIUS[type];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -217,9 +217,6 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
if (value === null || value === undefined) {
|
||||
this.__data[attr] = value;
|
||||
} else {
|
||||
if(attr === 'geo') {
|
||||
debugger;
|
||||
}
|
||||
this.__data[attr] = DataType(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
|||
ctor.forEachProperty(function (attr) {
|
||||
|
||||
var type = properties[attr].type;
|
||||
|
||||
|
||||
if (BASE_TYPES.indexOf(type.name) === -1) {
|
||||
if (typeof self.__data[attr] !== 'object' && self.__data[attr]) {
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue