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);
|
schema.adapter.connect(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Memory = Memory;
|
||||||
|
|
||||||
function Memory(m) {
|
function Memory(m) {
|
||||||
if (m) {
|
if (m) {
|
||||||
this.isTransaction = true;
|
this.isTransaction = true;
|
||||||
|
@ -84,25 +86,9 @@ Memory.prototype.destroy = function destroy(model, id, callback) {
|
||||||
Memory.prototype.fromDb = function(model, data) {
|
Memory.prototype.fromDb = function(model, data) {
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
|
var ctor = this._models[model].model;
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
Object.keys(data).forEach(function (key) {
|
return ctor(data);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.all = function all(model, filter, callback) {
|
Memory.prototype.all = function all(model, filter, callback) {
|
||||||
|
@ -112,7 +98,6 @@ Memory.prototype.all = function all(model, filter, callback) {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
var isGeo = false;
|
var isGeo = false;
|
||||||
|
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
// do we need some sorting?
|
// do we need some sorting?
|
||||||
if (filter.order) {
|
if (filter.order) {
|
||||||
|
@ -194,6 +179,9 @@ function applyFilter(filter) {
|
||||||
if (typeof example === 'undefined') return undefined;
|
if (typeof example === 'undefined') return undefined;
|
||||||
if (typeof value === 'undefined') return undefined;
|
if (typeof value === 'undefined') return undefined;
|
||||||
if (typeof example === 'object') {
|
if (typeof example === 'object') {
|
||||||
|
// ignore geo near filter
|
||||||
|
if(example.near) return true;
|
||||||
|
|
||||||
if (example.inq) {
|
if (example.inq) {
|
||||||
if (!value) return false;
|
if (!value) return false;
|
||||||
for (var i = 0; i < example.inq.length; i += 1) {
|
for (var i = 0; i < example.inq.length; i += 1) {
|
||||||
|
@ -271,6 +259,10 @@ Memory.prototype.exec = function(callback) {
|
||||||
setTimeout(callback, 50);
|
setTimeout(callback, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Memory.prototype.buildNearFilter = function (filter) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
function merge(base, update) {
|
function merge(base, update) {
|
||||||
if (!base) return update;
|
if (!base) return update;
|
||||||
Object.keys(update).forEach(function (key) {
|
Object.keys(update).forEach(function (key) {
|
||||||
|
@ -278,4 +270,3 @@ function merge(base, update) {
|
||||||
});
|
});
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
54
lib/dao.js
54
lib/dao.js
|
@ -14,6 +14,8 @@ var List = require('./list.js');
|
||||||
require('./relations.js');
|
require('./relations.js');
|
||||||
var Inclusion = require('./include.js');
|
var Inclusion = require('./include.js');
|
||||||
var Relation = require('./relations.js');
|
var Relation = require('./relations.js');
|
||||||
|
var geo = require('./geo');
|
||||||
|
var Memory = require('./adapters/memory').Memory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DAO class - base class for all persist objects
|
* DAO class - base class for all persist objects
|
||||||
|
@ -314,11 +316,55 @@ DataAccessObject.find = function find(params, cb) {
|
||||||
params = null;
|
params = null;
|
||||||
}
|
}
|
||||||
var constr = this;
|
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) {
|
this.schema.adapter.all(this.modelName, params, function (err, data) {
|
||||||
if (data && data.forEach) {
|
if (data && data.forEach) {
|
||||||
data.forEach(function (d, i) {
|
data.forEach(function (d, i) {
|
||||||
var obj = new constr;
|
var obj = new constr(d);
|
||||||
obj._initProperties(d, false);
|
|
||||||
if (params && params.include && params.collect) {
|
if (params && params.include && params.collect) {
|
||||||
data[i] = obj.__cachedRelations[params.collect];
|
data[i] = obj.__cachedRelations[params.collect];
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,6 +374,10 @@ DataAccessObject.find = function find(params, cb) {
|
||||||
if (data && data.countBeforeLimit) {
|
if (data && data.countBeforeLimit) {
|
||||||
data.countBeforeLimit = data.countBeforeLimit;
|
data.countBeforeLimit = data.countBeforeLimit;
|
||||||
}
|
}
|
||||||
|
if(!supportsGeo && near) {
|
||||||
|
data = geo.filter(data, near);
|
||||||
|
}
|
||||||
|
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
39
lib/geo.js
39
lib/geo.js
|
@ -46,6 +46,11 @@ exports.filter = function (arr, filter) {
|
||||||
|
|
||||||
// filter out objects without locations
|
// filter out objects without locations
|
||||||
if(!loc) return;
|
if(!loc) return;
|
||||||
|
|
||||||
|
if(!(loc instanceof GeoPoint)) {
|
||||||
|
loc = GeoPoint(loc);
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof loc.lat !== 'number') return;
|
if(typeof loc.lat !== 'number') return;
|
||||||
if(typeof loc.lng !== 'number') return;
|
if(typeof loc.lng !== 'number') return;
|
||||||
|
|
||||||
|
@ -87,6 +92,20 @@ function GeoPoint(data) {
|
||||||
return new 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 === '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.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');
|
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) {
|
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 x1 = a.lat;
|
||||||
var y1 = a.lng;
|
var y1 = a.lng;
|
||||||
|
|
||||||
|
@ -121,6 +147,18 @@ GeoPoint.prototype.distanceTo = function (point, options) {
|
||||||
return GeoPoint.distanceBetween(this, 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
|
// ratio of a circle's circumference to its diameter
|
||||||
var PI = 3.1415926535897932384626433832795;
|
var PI = 3.1415926535897932384626433832795;
|
||||||
|
|
||||||
|
@ -155,3 +193,4 @@ function geoDistance(x1, y1, x2, y2, options) {
|
||||||
|
|
||||||
return 2 * Math.asin( c ) * EARTH_RADIUS[type];
|
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) {
|
if (value === null || value === undefined) {
|
||||||
this.__data[attr] = value;
|
this.__data[attr] = value;
|
||||||
} else {
|
} else {
|
||||||
if(attr === 'geo') {
|
|
||||||
debugger;
|
|
||||||
}
|
|
||||||
this.__data[attr] = DataType(value);
|
this.__data[attr] = DataType(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue