diff --git a/lib/adapters/memory.js b/lib/adapters/memory.js index 39295986..361bd1c9 100644 --- a/lib/adapters/memory.js +++ b/lib/adapters/memory.js @@ -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; -} - +} \ No newline at end of file diff --git a/lib/dao.js b/lib/dao.js index 3a8a8dd2..3d673103 100644 --- a/lib/dao.js +++ b/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 diff --git a/lib/geo.js b/lib/geo.js index 9b42a895..c1cb1040 100644 --- a/lib/geo.js +++ b/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]; -} \ No newline at end of file +} + diff --git a/lib/model-builder.js b/lib/model-builder.js index 2e9bdf9f..00a2e557 100644 --- a/lib/model-builder.js +++ b/lib/model-builder.js @@ -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); } } diff --git a/lib/model.js b/lib/model.js index a77773cb..3d5ed09a 100644 --- a/lib/model.js +++ b/lib/model.js @@ -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 {