nqm-minimongo
Version:
Client-side mongo database with server sync over http
303 lines (284 loc) • 8.63 kB
JavaScript
var async, compileDocumentSelector, compileSort, deg2rad, getDistanceFromLatLngInM, isLocalStorageSupported, pointInPolygon, processGeoIntersectsOperator, processNearOperator, _;
_ = require('lodash');
async = require('async');
compileDocumentSelector = require('./selector').compileDocumentSelector;
compileSort = require('./selector').compileSort;
isLocalStorageSupported = function() {
var e;
if (!window.localStorage) {
return false;
}
try {
window.localStorage.setItem("test", "test");
window.localStorage.removeItem("test");
return true;
} catch (_error) {
e = _error;
return false;
}
};
exports.compileDocumentSelector = compileDocumentSelector;
exports.autoselectLocalDb = function(options, success, error) {
var MemoryDb;
MemoryDb = require('./MemoryDb');
return new MemoryDb(options, success);
};
exports.migrateLocalDb = function(fromDb, toDb, success, error) {
var HybridDb, col, hybridDb, name, _ref;
HybridDb = require('./HybridDb');
hybridDb = new HybridDb(fromDb, toDb);
_ref = fromDb.collections;
for (name in _ref) {
col = _ref[name];
if (toDb[name]) {
hybridDb.addCollection(name);
}
}
return hybridDb.upload(success, error);
};
exports.cloneLocalDb = function(fromDb, toDb, success, error) {
var col, name, _ref;
_ref = fromDb.collections;
for (name in _ref) {
col = _ref[name];
if (!toDb[name]) {
toDb.addCollection(name);
}
}
return async.each(_.values(fromDb.collections), (function(_this) {
return function(fromCol, cb) {
var toCol;
toCol = toDb[fromCol.name];
return fromCol.find({}).fetch(function(items) {
return toCol.seed(items, function() {
return fromCol.pendingUpserts(function(upserts) {
return toCol.upsert(_.pluck(upserts, "doc"), _.pluck(upserts, "base"), function() {
return fromCol.pendingRemoves(function(removes) {
return async.eachSeries(removes, function(remove, cb2) {
return toCol.remove(remove, function() {
return cb2();
}, cb2);
}, cb);
}, cb);
}, cb);
}, cb);
}, cb);
}, cb);
};
})(this), (function(_this) {
return function(err) {
if (err) {
return error(err);
}
return success();
};
})(this));
};
exports.processFind = function(items, selector, options) {
var filtered;
filtered = _.filter(items, compileDocumentSelector(selector));
filtered = processNearOperator(selector, filtered);
filtered = processGeoIntersectsOperator(selector, filtered);
if (options && options.sort) {
filtered.sort(compileSort(options.sort));
}
if (options && options.skip) {
filtered = _.slice(filtered, options.skip);
}
if (options && options.limit) {
filtered = _.take(filtered, options.limit);
}
if (options && options.fields) {
filtered = exports.filterFields(filtered, options.fields);
}
return filtered;
};
exports.filterFields = function(items, fields) {
if (fields == null) {
fields = {};
}
if (_.keys(fields).length === 0) {
return items;
}
return _.map(items, function(item) {
var field, from, newItem, obj, path, pathElem, to, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3;
newItem = {};
if (_.first(_.values(fields)) === 1) {
_ref = _.keys(fields).concat(["_id"]);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
field = _ref[_i];
path = field.split(".");
obj = item;
for (_j = 0, _len1 = path.length; _j < _len1; _j++) {
pathElem = path[_j];
if (obj) {
obj = obj[pathElem];
}
}
if (obj == null) {
continue;
}
from = item;
to = newItem;
_ref1 = _.initial(path);
for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
pathElem = _ref1[_k];
to[pathElem] = to[pathElem] || {};
to = to[pathElem];
from = from[pathElem];
}
to[_.last(path)] = from[_.last(path)];
}
return newItem;
} else {
item = _.cloneDeep(item);
_ref2 = _.keys(fields);
for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) {
field = _ref2[_l];
path = field.split(".");
obj = item;
_ref3 = _.initial(path);
for (_m = 0, _len4 = _ref3.length; _m < _len4; _m++) {
pathElem = _ref3[_m];
if (obj) {
obj = obj[pathElem];
}
}
if (obj == null) {
continue;
}
delete obj[_.last(path)];
}
return item;
}
});
};
exports.createUid = function() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r, v;
r = Math.random() * 16 | 0;
v = c === 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
};
processNearOperator = function(selector, list) {
var distances, geo, key, value;
for (key in selector) {
value = selector[key];
if ((value != null) && value['$near']) {
geo = value['$near']['$geometry'];
if (geo.type !== 'Point') {
break;
}
list = _.filter(list, function(doc) {
return doc[key] && doc[key].type === 'Point';
});
distances = _.map(list, function(doc) {
return {
doc: doc,
distance: getDistanceFromLatLngInM(geo.coordinates[1], geo.coordinates[0], doc[key].coordinates[1], doc[key].coordinates[0])
};
});
distances = _.filter(distances, function(item) {
return item.distance >= 0;
});
distances = _.sortBy(distances, 'distance');
if (value['$near']['$maxDistance']) {
distances = _.filter(distances, function(item) {
return item.distance <= value['$near']['$maxDistance'];
});
}
list = _.pluck(distances, 'doc');
}
}
return list;
};
pointInPolygon = function(point, polygon) {
if (!_.isEqual(_.first(polygon.coordinates[0]), _.last(polygon.coordinates[0]))) {
throw new Error("First must equal last");
}
if (point.coordinates[0] < Math.min.apply(this, _.map(polygon.coordinates[0], function(coord) {
return coord[0];
}))) {
return false;
}
if (point.coordinates[1] < Math.min.apply(this, _.map(polygon.coordinates[0], function(coord) {
return coord[1];
}))) {
return false;
}
if (point.coordinates[0] > Math.max.apply(this, _.map(polygon.coordinates[0], function(coord) {
return coord[0];
}))) {
return false;
}
if (point.coordinates[1] > Math.max.apply(this, _.map(polygon.coordinates[0], function(coord) {
return coord[1];
}))) {
return false;
}
return true;
};
getDistanceFromLatLngInM = function(lat1, lng1, lat2, lng2) {
var R, a, c, d, dLat, dLng;
R = 6370986;
dLat = deg2rad(lat2 - lat1);
dLng = deg2rad(lng2 - lng1);
a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
d = R * c;
return d;
};
deg2rad = function(deg) {
return deg * (Math.PI / 180);
};
processGeoIntersectsOperator = function(selector, list) {
var geo, key, value;
for (key in selector) {
value = selector[key];
if ((value != null) && value['$geoIntersects']) {
geo = value['$geoIntersects']['$geometry'];
if (geo.type !== 'Polygon') {
break;
}
list = _.filter(list, function(doc) {
if (!doc[key] || doc[key].type !== 'Point') {
return false;
}
return pointInPolygon(doc[key], geo);
});
}
}
return list;
};
exports.regularizeUpsert = function(docs, bases, success, error) {
var item, items, _i, _len, _ref;
if (_.isFunction(bases)) {
_ref = [void 0, bases, success], bases = _ref[0], success = _ref[1], error = _ref[2];
}
if (!_.isArray(docs)) {
docs = [docs];
bases = [bases];
} else {
bases = bases || [];
}
items = _.map(docs, function(doc, i) {
return {
doc: doc,
base: i < bases.length ? bases[i] : void 0
};
});
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
if (!item.doc._id) {
item.doc._id = exports.createUid();
}
if (item.base && !item.base._id) {
throw new Error("Base needs _id");
}
if (item.base && item.base._id !== item.doc._id) {
throw new Error("Base needs same _id");
}
}
return [items, success, error];
};