Removed underscore from minimongo.

This commit is contained in:
Radosław Miernik
2017-07-10 20:03:34 +02:00
parent 6bde360b9c
commit 53b3e59b3e
15 changed files with 234 additions and 220 deletions

View File

@@ -2,7 +2,7 @@
// arrays.
// XXX maybe this should be EJSON.isArray
isArray = function (x) {
return _.isArray(x) && !EJSON.isBinary(x);
return Array.isArray(x) && !EJSON.isBinary(x);
};
// XXX maybe this should be EJSON.isObject, though EJSON doesn't know about
@@ -24,7 +24,7 @@ isOperatorObject = function (valueSelector, inconsistentOK) {
return false;
var theseAreOperators = undefined;
_.each(valueSelector, function (value, selKey) {
Object.keys(valueSelector).forEach(function (selKey) {
var thisIsOperator = selKey.substr(0, 1) === '$';
if (theseAreOperators === undefined) {
theseAreOperators = thisIsOperator;
@@ -42,4 +42,4 @@ isOperatorObject = function (valueSelector, inconsistentOK) {
// string can be converted to integer
isNumericKey = function (s) {
return /^[0-9]+$/.test(s);
};
};

View File

@@ -170,7 +170,7 @@ LocalCollection.Cursor.prototype.forEach = function (callback, thisArg) {
movedBefore: true});
}
_.each(objects, function (elt, i) {
objects.forEach(function (elt, i) {
// This doubles as a clone operation.
elt = self._projectionFn(elt);
@@ -298,7 +298,7 @@ LocalCollection.ObserveHandle = function () {};
// XXX maybe callbacks should take a list of objects, to expose transactions?
// XXX maybe support field limiting (to limit what you're notified on)
_.extend(LocalCollection.Cursor.prototype, {
Object.assign(LocalCollection.Cursor.prototype, {
/**
* @summary Watch a query. Receive callbacks as the result set changes.
* @locus Anywhere
@@ -388,11 +388,9 @@ _.extend(LocalCollection.Cursor.prototype, {
}
if (!options._suppress_initial && !self.collection.paused) {
// XXX unify ordered and unordered interface
var each = ordered
? _.bind(_.each, null, query.results)
: _.bind(query.results.forEach, query.results);
each(function (doc) {
var results = query.results._map || query.results;
Object.keys(results).forEach(function (key) {
var doc = results[key];
var fields = EJSON.clone(doc);
delete fields._id;
@@ -403,7 +401,7 @@ _.extend(LocalCollection.Cursor.prototype, {
}
var handle = new LocalCollection.ObserveHandle;
_.extend(handle, {
Object.assign(handle, {
collection: self.collection,
stop: function () {
if (self.reactive)
@@ -525,17 +523,16 @@ LocalCollection.Cursor.prototype._depend = function (changers, _allow_unordered)
if (Tracker.active) {
var v = new Tracker.Dependency;
v.depend();
var notifyChange = _.bind(v.changed, v);
var notifyChange = v.changed.bind(v);
var options = {
_suppress_initial: true,
_allow_unordered: _allow_unordered
};
_.each(['added', 'changed', 'removed', 'addedBefore', 'movedBefore'],
function (fnName) {
if (changers[fnName])
options[fnName] = notifyChange;
});
['added', 'changed', 'removed', 'addedBefore', 'movedBefore'].forEach(function (fnName) {
if (changers[fnName])
options[fnName] = notifyChange;
});
// observeChanges will stop() when this computation is invalidated
self.observeChanges(options);
@@ -550,7 +547,7 @@ LocalCollection.prototype.insert = function (doc, callback) {
assertHasValidFieldNames(doc);
if (!_.has(doc, '_id')) {
if (!doc.hasOwnProperty('_id')) {
// if you really want to use ObjectIDs, set this global.
// Mongo.Collection specifies its own ids and does not use this code.
doc._id = LocalCollection._useOID ? new MongoID.ObjectID()
@@ -580,7 +577,7 @@ LocalCollection.prototype.insert = function (doc, callback) {
}
}
_.each(queriesToRecompute, function (qid) {
queriesToRecompute.forEach(function (qid) {
if (self.queries[qid])
self._recomputeResults(self.queries[qid]);
});
@@ -626,7 +623,8 @@ LocalCollection.prototype.remove = function (selector, callback) {
if (self.paused && !self._savedOriginals && EJSON.equals(selector, {})) {
var result = self._docs.size();
self._docs.clear();
_.each(self.queries, function (query) {
Object.keys(self.queries).forEach(function (qid) {
var query = self.queries[qid];
if (query.ordered) {
query.results = [];
} else {
@@ -653,7 +651,8 @@ LocalCollection.prototype.remove = function (selector, callback) {
for (var i = 0; i < remove.length; i++) {
var removeId = remove[i];
var removeDoc = self._docs.get(removeId);
_.each(self.queries, function (query, qid) {
Object.keys(self.queries).forEach(function (qid) {
var query = self.queries[qid];
if (query.dirty) return;
if (query.matcher.documentMatches(removeDoc).result) {
@@ -668,14 +667,14 @@ LocalCollection.prototype.remove = function (selector, callback) {
}
// run live query callbacks _after_ we've removed the documents.
_.each(queryRemove, function (remove) {
queryRemove.forEach(function (remove) {
var query = self.queries[remove.qid];
if (query) {
query.distances && query.distances.remove(remove.doc._id);
LocalCollection._removeFromResults(query, remove.doc);
}
});
_.each(queriesToRecompute, function (qid) {
queriesToRecompute.forEach(function (qid) {
var query = self.queries[qid];
if (query)
self._recomputeResults(query);
@@ -711,7 +710,8 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
var docMap = new LocalCollection._IdMap;
var idsMatchedBySelector = LocalCollection._idsMatchedBySelector(selector);
_.each(self.queries, function (query, qid) {
Object.keys(self.queries).forEach(function (qid) {
var query = self.queries[qid];
if ((query.cursor.skip || query.cursor.limit) && ! self.paused) {
// Catch the case of a reactive `count()` on a cursor with skip
// or limit, which registers an unordered observe. This is a
@@ -737,7 +737,7 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
} else {
var docToMemoize;
if (idsMatchedBySelector && !_.any(idsMatchedBySelector, function(id) {
if (idsMatchedBySelector && !idsMatchedBySelector.some(function(id) {
return EJSON.equals(id, doc._id);
})) {
docToMemoize = doc;
@@ -770,7 +770,7 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
return true;
});
_.each(recomputeQids, function (dummy, qid) {
Object.keys(recomputeQids).forEach(function (qid) {
var query = self.queries[qid];
if (query)
self._recomputeResults(query, qidToOriginalResults[qid]);
@@ -783,8 +783,8 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
var insertedId;
if (updateCount === 0 && options.upsert) {
let selectorModifier = LocalCollection._selectorIsId(selector)
? { _id: selector }
let selectorModifier = LocalCollection._selectorIsId(selector)
? { _id: selector }
: selector;
selectorModifier = LocalCollection._removeDollarOperators(selectorModifier);
@@ -795,7 +795,7 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
delete selectorModifier._id;
}
// This double _modify call is made to help work around an issue where collection
// This double _modify call is made to help work around an issue where collection
// upserts won't work properly, with nested properties (see issue #8631).
LocalCollection._modify(newDoc, {$set: selectorModifier});
LocalCollection._modify(newDoc, mod, {isInsert: true});
@@ -836,7 +836,7 @@ LocalCollection.prototype.upsert = function (selector, mod, options, callback) {
callback = options;
options = {};
}
return self.update(selector, mod, _.extend({}, options, {
return self.update(selector, mod, Object.assign({}, options, {
upsert: true,
_returnObject: true
}), callback);
@@ -945,7 +945,7 @@ LocalCollection._updateInResults = function (query, doc, old_doc) {
projectionFn(doc), projectionFn(old_doc));
if (!query.ordered) {
if (!_.isEmpty(changedFields)) {
if (Object.keys(changedFields).length) {
query.changed(doc._id, changedFields);
query.results.set(doc._id, doc);
}
@@ -954,7 +954,7 @@ LocalCollection._updateInResults = function (query, doc, old_doc) {
var orig_idx = LocalCollection._findInOrderedResults(query, doc);
if (!_.isEmpty(changedFields))
if (Object.keys(changedFields).length)
query.changed(doc._id, changedFields);
if (!query.sorter)
return;

View File

@@ -241,7 +241,11 @@ Tinytest.add("minimongo - transform", function (test) {
});
// transformed documents get _id field transplanted if not present
var transformWithoutId = function (doc) { return _.omit(doc, '_id'); };
var transformWithoutId = function (doc) {
var docWithoutId = Object.assign({}, doc);
delete docWithoutId._id;
return docWithoutId;
};
test.equal(c.findOne({}, {transform: transformWithoutId})._id,
c.findOne()._id);
});
@@ -333,8 +337,8 @@ Tinytest.add("minimongo - selector_compiler", function (test) {
}
};
var match = _.bind(matches, null, true);
var nomatch = _.bind(matches, null, false);
var match = matches.bind(null, true);
var nomatch = matches.bind(null, false);
// XXX blog post about what I learned while writing these tests (weird
// mongo edge cases)
@@ -513,14 +517,14 @@ Tinytest.add("minimongo - selector_compiler", function (test) {
nomatch({a: {$mod: [10, 1]}}, {a: 12});
match({a: {$mod: [10, 1]}}, {a: [10, 11, 12]});
nomatch({a: {$mod: [10, 1]}}, {a: [10, 12]});
_.each([
[
5,
[10],
[10, 1, 2],
"foo",
{bar: 1},
[]
], function (badMod) {
].forEach(function (badMod) {
test.throws(function () {
match({a: {$mod: badMod}}, {a: 11});
});
@@ -866,9 +870,9 @@ Tinytest.add("minimongo - selector_compiler", function (test) {
nomatch({a: {$bitsAllSet: 1}}, {a: ['a', 'b']})
nomatch({a: {$bitsAllSet: 1}}, {a: {foo: 'bar'}})
nomatch({a: {$bitsAllSet: 1}}, {a: 1.2})
nomatch({a: {$bitsAllSet: 1}}, {a: "1"})
nomatch({a: {$bitsAllSet: 1}}, {a: "1"});
_.each([
[
false,
NaN,
Infinity,
@@ -879,7 +883,7 @@ Tinytest.add("minimongo - selector_compiler", function (test) {
1.2,
"1",
[0, -1]
], function (badValue) {
].forEach(function (badValue) {
test.throws(function () {
match({a: {$bitsAllSet: badValue}}, {a: 42});
});
@@ -1441,10 +1445,10 @@ Tinytest.add("minimongo - projection_compiler", function (test) {
var testProjection = function (projection, tests) {
var projection_f = LocalCollection._compileProjection(projection);
var equalNonStrict = function (a, b, desc) {
test.isTrue(_.isEqual(a, b), desc);
test.isTrue(EJSON.equals(a, b), desc);
};
_.each(tests, function (testCase) {
tests.forEach(function (testCase) {
equalNonStrict(projection_f(testCase[0]), testCase[1], testCase[2]);
});
};
@@ -1570,7 +1574,7 @@ Tinytest.add("minimongo - projection_compiler", function (test) {
Tinytest.add("minimongo - fetch with fields", function (test) {
var c = new LocalCollection();
_.times(30, function (i) {
Array.from({length: 30}, function (_, i) {
c.insert({
something: Random.id(),
anything: {
@@ -1588,14 +1592,14 @@ Tinytest.add("minimongo - fetch with fields", function (test) {
'anything.foo': 1
} }).fetch();
test.isTrue(_.all(fetchResults, function (x) {
test.isTrue(fetchResults.every(function (x) {
return x &&
x.something &&
x.anything &&
x.anything.foo &&
x.anything.foo === "bar" &&
!_.has(x, 'nothing') &&
!_.has(x.anything, 'cool');
!x.hasOwnProperty('nothing') &&
!x.anything.hasOwnProperty('cool');
}));
// Test with a selector, even field used in the selector is excluded in the
@@ -1606,13 +1610,13 @@ Tinytest.add("minimongo - fetch with fields", function (test) {
fields: { nothing: 0 }
}).fetch();
test.isTrue(_.all(fetchResults, function (x) {
test.isTrue(fetchResults.every(function (x) {
return x &&
x.something &&
x.anything &&
x.anything.foo === "bar" &&
x.anything.cool === "hot" &&
!_.has(x, 'nothing') &&
!x.hasOwnProperty('nothing') &&
x.i &&
x.i >= 5;
}));
@@ -1634,13 +1638,13 @@ Tinytest.add("minimongo - fetch with fields", function (test) {
}
}).fetch();
test.isTrue(_.all(fetchResults, function (x) {
test.isTrue(fetchResults.every(function (x) {
return x &&
x.something &&
x.i >= 10 && x.i < 20;
}));
_.each(fetchResults, function (x, i, arr) {
fetchResults.forEach(function (x, i, arr) {
if (!i) return;
test.isTrue(x.i === arr[i-1].i + 1);
});
@@ -1685,7 +1689,7 @@ Tinytest.add("minimongo - fetch with projection, subarrays", function (test) {
});
var equalNonStrict = function (a, b, desc) {
test.isTrue(_.isEqual(a, b), desc);
test.isTrue(EJSON.equals(a, b), desc);
};
var testForProjection = function (projection, expected) {
@@ -1796,7 +1800,7 @@ Tinytest.add("minimongo - observe ordered with projection", function (test) {
handle.stop();
// test _suppress_initial
handle = c.find({}, {sort: {a: -1}, fields: { a: 1 }}).observe(_.extend(cbs, {_suppress_initial: true}));
handle = c.find({}, {sort: {a: -1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_suppress_initial: true}));
test.equal(operations.shift(), undefined);
c.insert({a:100, b: { foo: "bar" }});
test.equal(operations.shift(), ['added', {a:100}, 0, idA2]);
@@ -1826,7 +1830,7 @@ Tinytest.add("minimongo - observe ordered with projection", function (test) {
// test _no_indices
c.remove({});
handle = c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(_.extend(cbs, {_no_indices: true}));
handle = c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_no_indices: true}));
c.insert({_id: 'foo', a:1, zoo: "crazy"});
test.equal(operations.shift(), ['added', {a:1}, -1, null]);
c.update({a:1}, {$set: {a: 2, foobar: "player"}});
@@ -1875,7 +1879,7 @@ Tinytest.add("minimongo - ordering", function (test) {
// document ordering under a sort specification
var verify = function (sorts, docs) {
_.each(_.isArray(sorts) ? sorts : [sorts], function (sort) {
(Array.isArray(sorts) ? sorts : [sorts]).forEach(function (sort) {
var sorter = new Minimongo.Sorter(sort);
assert_ordering(test, sorter.getComparator(), docs);
});
@@ -2050,25 +2054,25 @@ Tinytest.add("minimongo - subkey sort", function (test) {
c.insert({a: {b: 1}});
c.insert({a: {b: 3}});
test.equal(
_.pluck(c.find({}, {sort: {'a.b': -1}}).fetch(), 'a'),
c.find({}, {sort: {'a.b': -1}}).fetch().map(function (doc) { return doc.a; }),
[{b: 3}, {b: 2}, {b: 1}]);
// isn't an object
c.insert({a: 1});
test.equal(
_.pluck(c.find({}, {sort: {'a.b': 1}}).fetch(), 'a'),
c.find({}, {sort: {'a.b': 1}}).fetch().map(function (doc) { return doc.a; }),
[1, {b: 1}, {b: 2}, {b: 3}]);
// complex object
c.insert({a: {b: {c: 1}}});
test.equal(
_.pluck(c.find({}, {sort: {'a.b': -1}}).fetch(), 'a'),
c.find({}, {sort: {'a.b': -1}}).fetch().map(function (doc) { return doc.a; }),
[{b: {c: 1}}, {b: 3}, {b: 2}, {b: 1}, 1]);
// no such top level prop
c.insert({c: 1});
test.equal(
_.pluck(c.find({}, {sort: {'a.b': -1}}).fetch(), 'a'),
c.find({}, {sort: {'a.b': -1}}).fetch().map(function (doc) { return doc.a; }),
[{b: {c: 1}}, {b: 3}, {b: 2}, {b: 1}, 1, undefined]);
// no such mid level prop. just test that it doesn't throw.
@@ -2099,11 +2103,11 @@ Tinytest.add("minimongo - array sort", function (test) {
var testCursorMatchesField = function (cursor, field) {
var fieldValues = [];
c.find().forEach(function (doc) {
if (_.has(doc, field))
if (doc.hasOwnProperty(field))
fieldValues.push(doc[field]);
});
test.equal(_.pluck(cursor.fetch(), field),
_.range(_.max(fieldValues) + 1));
test.equal(cursor.fetch().map(function (doc) { return doc[field]; }),
Array.from({length: Math.max.apply(null, fieldValues) + 1}, function (_, i) { return i; }));
};
testCursorMatchesField(c.find({}, {sort: {'a.x': 1}}), 'up');
@@ -2115,7 +2119,7 @@ Tinytest.add("minimongo - array sort", function (test) {
Tinytest.add("minimongo - sort keys", function (test) {
var keyListToObject = function (keyList) {
var obj = {};
_.each(keyList, function (key) {
keyList.forEach(function (key) {
obj[EJSON.stringify(key)] = true;
});
return obj;
@@ -2961,7 +2965,7 @@ Tinytest.add("minimongo - observe ordered", function (test) {
handle.stop();
// test _suppress_initial
handle = c.find({}, {sort: {a: -1}}).observe(_.extend({
handle = c.find({}, {sort: {a: -1}}).observe(Object.assign({
_suppress_initial: true}, cbs));
test.equal(operations.shift(), undefined);
c.insert({a:100});
@@ -3007,7 +3011,7 @@ Tinytest.add("minimongo - observe ordered", function (test) {
// test _no_indices
c.remove({});
handle = c.find({}, {sort: {a: 1}}).observe(_.extend(cbs, {_no_indices: true}));
handle = c.find({}, {sort: {a: 1}}).observe(Object.assign(cbs, {_no_indices: true}));
c.insert({_id: 'foo', a:1});
test.equal(operations.shift(), ['added', {a:1}, -1, null]);
c.update({a:1}, {$set: {a: 2}});
@@ -3027,14 +3031,14 @@ Tinytest.add("minimongo - observe ordered", function (test) {
handle.stop();
});
_.each([true, false], function (ordered) {
[true, false].forEach(function (ordered) {
Tinytest.add("minimongo - observe ordered: " + ordered, function (test) {
var c = new LocalCollection();
var ev = "";
var makecb = function (tag) {
var ret = {};
_.each(["added", "changed", "removed"], function (fn) {
["added", "changed", "removed"].forEach(function (fn) {
var fnName = ordered ? fn + "At" : fn;
ret[fnName] = function (doc) {
ev = (ev + fn.substr(0, 1) + tag + doc._id + "_");
@@ -3112,8 +3116,8 @@ Tinytest.add("minimongo - saveOriginals", function (test) {
// Verify the originals.
var originals = c.retrieveOriginals();
var affected = ['bar', 'baz', 'quux', 'whoa', 'hooray'];
test.equal(originals.size(), _.size(affected));
_.each(affected, function (id) {
test.equal(originals.size(), affected.length);
affected.forEach(function (id) {
test.isTrue(originals.has(id));
});
test.equal(originals.get('bar'), {_id: 'bar', x: 'updateme'});
@@ -3378,7 +3382,7 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
test.equal(coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 30 } }).count(), 3);
test.equal(coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 4 } }).count(), 1);
var points = coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 6 } }).fetch();
_.each(points, function (point, i, points) {
points.forEach(function (point, i, points) {
test.isTrue(!i || distance([0, 0], point.rest.loc) >= distance([0, 0], points[i - 1].rest.loc));
});
@@ -3397,7 +3401,7 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
{ "category" : "OTHER OFFENSES", "descript" : "POSSESSION OF BURGLARY TOOLS", "address" : "900 Block of MINNA ST", "location" : { "type" : "Point", "coordinates" : [ -122.415386041221, 37.7747879734156 ] } }
];
_.each(data, function (x, i) { coll.insert(_.extend(x, { x: i })); });
data.forEach(function (x, i) { coll.insert(Object.assign(x, { x: i })); });
var close15 = coll.find({ location: { $near: {
$geometry: { type: "Point",
@@ -3478,8 +3482,7 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
a: {b: [5, 5]}});
var testNear = function (near, md, expected) {
test.equal(
_.pluck(
coll.find({'a.b': {$near: near, $maxDistance: md}}).fetch(), '_id'),
coll.find({'a.b': {$near: near, $maxDistance: md}}).fetch().map(function (doc) { return doc._id }),
expected);
};
testNear([149, 149], 4, ['x']);
@@ -3492,10 +3495,10 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
// issue #3599
// Ensure that distance is not used as a tie-breaker for sort.
test.equal(
_.pluck(coll.find({'a.b': {$near: [1, 1]}}, {sort: {k: 1}}).fetch(), '_id'),
coll.find({'a.b': {$near: [1, 1]}}, {sort: {k: 1}}).fetch().map(function (doc) { return doc._id; }),
['x', 'y']);
test.equal(
_.pluck(coll.find({'a.b': {$near: [5, 5]}}, {sort: {k: 1}}).fetch(), '_id'),
coll.find({'a.b': {$near: [5, 5]}}, {sort: {k: 1}}).fetch().map(function (doc) { return doc._id; }),
['x', 'y']);
var operations = [];

View File

@@ -35,14 +35,16 @@ LocalCollection._modify = function (doc, mod, options) {
// apply modifiers to the doc.
newDoc = EJSON.clone(doc);
_.each(mod, function (operand, op) {
Object.keys(mod).forEach(function (op) {
var operand = mod[op];
var modFunc = MODIFIERS[op];
// Treat $setOnInsert as $set if this is an insert.
if (options.isInsert && op === '$setOnInsert')
modFunc = MODIFIERS['$set'];
if (!modFunc)
throw MinimongoError("Invalid modifier specified " + op);
_.each(operand, function (arg, keypath) {
Object.keys(operand).forEach(function (keypath) {
var arg = operand[keypath];
if (keypath === '') {
throw MinimongoError("An empty update path is not valid.");
}
@@ -53,13 +55,13 @@ LocalCollection._modify = function (doc, mod, options) {
var keyparts = keypath.split('.');
if (! _.all(keyparts, _.identity)) {
if (!keyparts.every(Boolean)) {
throw MinimongoError(
"The update path '" + keypath +
"' contains an empty field name, which is not allowed.");
}
var noCreate = _.has(NO_CREATE_MODIFIERS, op);
var noCreate = NO_CREATE_MODIFIERS.hasOwnProperty(op);
var forbidArray = (op === "$rename");
var target = findModTarget(newDoc, keyparts, {
noCreate: NO_CREATE_MODIFIERS[op],
@@ -73,15 +75,15 @@ LocalCollection._modify = function (doc, mod, options) {
}
// move new document into place.
_.each(_.keys(doc), function (k) {
Object.keys(doc).forEach(function (k) {
// Note: this used to be for (var k in doc) however, this does not
// work right in Opera. Deleting from a doc while iterating over it
// would sometimes cause opera to skip some keys.
if (k !== '_id')
delete doc[k];
});
_.each(newDoc, function (v, k) {
doc[k] = v;
Object.keys(newDoc).forEach(function (k) {
doc[k] = newDoc[k];
});
};
@@ -237,7 +239,7 @@ var MODIFIERS = {
}
},
$set: function (target, field, arg) {
if (!_.isObject(target)) { // not an array or an object
if (target !== Object(target)) { // not an array or an object
var e = MinimongoError(
"Cannot set property on non-object field", { field });
e.setPropertyError = true;
@@ -343,8 +345,8 @@ var MODIFIERS = {
target[field] = []; // differs from Array.slice!
else if (slice < 0)
target[field] = target[field].slice(slice);
else
target[field] = target[field].slice(0, slice);
else
target[field] = target[field].slice(0, slice);
}
},
$pushAll: function (target, field, arg) {
@@ -380,7 +382,7 @@ var MODIFIERS = {
throw MinimongoError(
"Cannot apply $addToSet modifier to non-array", { field });
else {
_.each(values, function (value) {
values.forEach(function (value) {
for (var i = 0; i < x.length; i++)
if (LocalCollection._f._equal(value, x[i]))
return;

View File

@@ -10,7 +10,7 @@ LocalCollection._selectorIsIdPerhapsAsObject = function (selector) {
return LocalCollection._selectorIsId(selector) ||
(selector && typeof selector === "object" &&
selector._id && LocalCollection._selectorIsId(selector._id) &&
_.size(selector) === 1);
Object.keys(selector).length === 1);
};
// If this is a selector which explicitly constrains the match by ID to a finite
@@ -26,15 +26,15 @@ LocalCollection._idsMatchedBySelector = function (selector) {
return null;
// Do we have an _id clause?
if (_.has(selector, '_id')) {
if (selector.hasOwnProperty('_id')) {
// Is the _id clause just an ID?
if (LocalCollection._selectorIsId(selector._id))
return [selector._id];
// Is the _id clause {_id: {$in: ["x", "y", "z"]}}?
if (selector._id && selector._id.$in
&& _.isArray(selector._id.$in)
&& !_.isEmpty(selector._id.$in)
&& _.all(selector._id.$in, LocalCollection._selectorIsId)) {
&& Array.isArray(selector._id.$in)
&& selector._id.$in.length
&& selector._id.$in.every(LocalCollection._selectorIsId)) {
return selector._id.$in;
}
return null;
@@ -43,7 +43,7 @@ LocalCollection._idsMatchedBySelector = function (selector) {
// If this is a top-level $and, and any of the clauses constrain their
// documents, then the whole selector is constrained by any one clause's
// constraint. (Well, by their intersection, but that seems unlikely.)
if (selector.$and && _.isArray(selector.$and)) {
if (selector.$and && Array.isArray(selector.$and)) {
for (var i = 0; i < selector.$and.length; ++i) {
var subIds = LocalCollection._idsMatchedBySelector(selector.$and[i]);
if (subIds)

View File

@@ -13,7 +13,7 @@ LocalCollection._CachingChangeObserver = function (options) {
var orderedFromCallbacks = options.callbacks &&
LocalCollection._observeChangesCallbacksAreOrdered(options.callbacks);
if (_.has(options, 'ordered')) {
if (options.hasOwnProperty('ordered')) {
self.ordered = options.ordered;
if (options.callbacks && options.ordered !== orderedFromCallbacks)
throw Error("ordered option doesn't match callbacks");
@@ -89,7 +89,7 @@ LocalCollection._observeFromObserveChanges = function (cursor, observeCallbacks)
var self = this;
if (suppressed || !(observeCallbacks.addedAt || observeCallbacks.added))
return;
var doc = transform(_.extend(fields, {_id: id}));
var doc = transform(Object.assign(fields, {_id: id}));
if (observeCallbacks.addedAt) {
var index = indices
? (before ? self.docs.indexOf(before) : self.docs.size()) : -1;
@@ -149,7 +149,7 @@ LocalCollection._observeFromObserveChanges = function (cursor, observeCallbacks)
observeChangesCallbacks = {
added: function (id, fields) {
if (!suppressed && observeCallbacks.added) {
var doc = _.extend(fields, {_id: id});
var doc = Object.assign(fields, {_id: id});
observeCallbacks.added(transform(doc));
}
},

View File

@@ -9,7 +9,6 @@ Package.onUse(function (api) {
api.export('MinimongoTest', { testOnly: true });
api.export('MinimongoError', { testOnly: true });
api.use([
'underscore',
'ejson',
'id-map',
'ordered-dict',
@@ -51,7 +50,6 @@ Package.onTest(function (api) {
api.use('test-helpers', 'client');
api.use([
'tinytest',
'underscore',
'ejson',
'ordered-dict',
'random',

View File

@@ -8,22 +8,23 @@
LocalCollection._compileProjection = function (fields) {
LocalCollection._checkSupportedProjection(fields);
var _idProjection = _.isUndefined(fields._id) ? true : fields._id;
var _idProjection = fields._id === undefined ? true : fields._id;
var details = projectionDetails(fields);
// returns transformed doc according to ruleTree
var transform = function (doc, ruleTree) {
// Special case for "sets"
if (_.isArray(doc))
return _.map(doc, function (subdoc) { return transform(subdoc, ruleTree); });
if (Array.isArray(doc))
return doc.map(function (subdoc) { return transform(subdoc, ruleTree); });
var res = details.including ? {} : EJSON.clone(doc);
_.each(ruleTree, function (rule, key) {
if (!_.has(doc, key))
Object.keys(ruleTree).forEach(function (key) {
var rule = ruleTree[key];
if (!doc.hasOwnProperty(key))
return;
if (_.isObject(rule)) {
if (rule === Object(rule)) {
// For sub-objects/subsets we branch
if (_.isObject(doc[key]))
if (doc[key] === Object(doc[key]))
res[key] = transform(doc[key], rule);
// Otherwise we don't even touch this subfield
} else if (details.including)
@@ -38,9 +39,9 @@ LocalCollection._compileProjection = function (fields) {
return function (obj) {
var res = transform(obj, details.tree);
if (_idProjection && _.has(obj, '_id'))
if (_idProjection && obj.hasOwnProperty('_id'))
res._id = obj._id;
if (!_idProjection && _.has(res, '_id'))
if (!_idProjection && res.hasOwnProperty('_id'))
delete res._id;
return res;
};
@@ -56,7 +57,7 @@ projectionDetails = function (fields) {
// Find the non-_id keys (_id is handled specially because it is included unless
// explicitly excluded). Sort the keys, so that our code to detect overlaps
// like 'foo' and 'foo.bar' can assume that 'foo' comes first.
var fieldsKeys = _.keys(fields).sort();
var fieldsKeys = Object.keys(fields).sort();
// If _id is the only field in the projection, do not remove it, since it is
// required to determine if this is an exclusion or exclusion. Also keep an
@@ -66,12 +67,12 @@ projectionDetails = function (fields) {
// special case, since exclusive _id is always allowed.
if (fieldsKeys.length > 0 &&
!(fieldsKeys.length === 1 && fieldsKeys[0] === '_id') &&
!(_.contains(fieldsKeys, '_id') && fields['_id']))
fieldsKeys = _.reject(fieldsKeys, function (key) { return key === '_id'; });
!(fieldsKeys.includes('_id') && fields['_id']))
fieldsKeys = fieldsKeys.filter(function (key) { return key !== '_id'; });
var including = null; // Unknown
_.each(fieldsKeys, function (keyPath) {
fieldsKeys.forEach(function (keyPath) {
var rule = !!fields[keyPath];
if (including === null)
including = rule;
@@ -126,20 +127,20 @@ projectionDetails = function (fields) {
// @returns - Object: tree represented as a set of nested objects
pathsToTree = function (paths, newLeafFn, conflictFn, tree) {
tree = tree || {};
_.each(paths, function (keyPath) {
paths.forEach(function (keyPath) {
var treePos = tree;
var pathArr = keyPath.split('.');
// use _.all just for iteration with break
var success = _.all(pathArr.slice(0, -1), function (key, idx) {
if (!_.has(treePos, key))
// use .every just for iteration with break
var success = pathArr.slice(0, -1).every(function (key, idx) {
if (!treePos.hasOwnProperty(key))
treePos[key] = {};
else if (!_.isObject(treePos[key])) {
else if (treePos[key] !== Object(treePos[key])) {
treePos[key] = conflictFn(treePos[key],
pathArr.slice(0, idx + 1).join('.'),
keyPath);
// break out of loop if we are failing for this path
if (!_.isObject(treePos[key]))
if (treePos[key] !== Object(treePos[key]))
return false;
}
@@ -148,8 +149,8 @@ pathsToTree = function (paths, newLeafFn, conflictFn, tree) {
});
if (success) {
var lastKey = _.last(pathArr);
if (!_.has(treePos, lastKey))
var lastKey = pathArr[pathArr.length - 1];
if (!treePos.hasOwnProperty(lastKey))
treePos[lastKey] = newLeafFn(keyPath);
else
treePos[lastKey] = conflictFn(treePos[lastKey], keyPath, keyPath);
@@ -160,15 +161,16 @@ pathsToTree = function (paths, newLeafFn, conflictFn, tree) {
};
LocalCollection._checkSupportedProjection = function (fields) {
if (!_.isObject(fields) || _.isArray(fields))
if (fields !== Object(fields) || Array.isArray(fields))
throw MinimongoError("fields option must be an object");
_.each(fields, function (val, keyPath) {
if (_.contains(keyPath.split('.'), '$'))
Object.keys(fields).forEach(function (keyPath) {
var val = fields[keyPath];
if (keyPath.split('.').includes('$'))
throw MinimongoError("Minimongo doesn't support $ operator in projections yet.");
if (typeof val === 'object' && _.intersection(['$elemMatch', '$meta', '$slice'], _.keys(val)).length > 0)
if (typeof val === 'object' && ['$elemMatch', '$meta', '$slice'].some(key => Object.keys(val).includes(key)))
throw MinimongoError("Minimongo doesn't support operators in projections yet.");
if (_.indexOf([1, 0, true, false], val) === -1)
if (![1, 0, true, false].includes(val))
throw MinimongoError("Projection values should be one of 1, 0, true, or false");
});
};

View File

@@ -47,7 +47,7 @@ Minimongo.Matcher = function (selector, isUpdate = false) {
self._isUpdate = isUpdate;
};
_.extend(Minimongo.Matcher.prototype, {
Object.assign(Minimongo.Matcher.prototype, {
documentMatches: function (doc) {
if (!doc || typeof doc !== "object") {
throw Error("documentMatches needs a document");
@@ -109,7 +109,7 @@ _.extend(Minimongo.Matcher.prototype, {
// Returns a list of key paths the given selector is looking for. It includes
// the empty string if there is a $where.
_getPaths: function () {
return _.keys(this._paths);
return Object.keys(this._paths);
}
});
@@ -124,11 +124,12 @@ _.extend(Minimongo.Matcher.prototype, {
var compileDocumentSelector = function (docSelector, matcher, options) {
options = options || {};
var docMatchers = [];
_.each(docSelector, function (subSelector, key) {
Object.keys(docSelector).forEach(function (key) {
var subSelector = docSelector[key];
if (key.substr(0, 1) === '$') {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
if (!_.has(LOGICAL_OPERATORS, key))
if (!LOGICAL_OPERATORS.hasOwnProperty(key))
throw new Error("Unrecognized logical operator: " + key);
matcher._isSimple = false;
docMatchers.push(LOGICAL_OPERATORS[key](subSelector, matcher,
@@ -182,7 +183,7 @@ var convertElementMatcherToBranchedMatcher = function (
branches, options.dontIncludeLeafArrays);
}
var ret = {};
ret.result = _.any(expanded, function (element) {
ret.result = expanded.some(function (element) {
var matched = elementMatcher(element.value);
// Special case for $elemMatch: it means "true, and use this as an array
@@ -211,9 +212,7 @@ var convertElementMatcherToBranchedMatcher = function (
regexpElementMatcher = function (regexp) {
return function (value) {
if (value instanceof RegExp) {
// Comparing two regexps means seeing if the regexps are identical
// (really!). Underscore knows how.
return _.isEqual(value, regexp);
return value.toString() === regexp.toString();
}
// Regexps only work against strings.
if (typeof value !== 'string')
@@ -258,21 +257,22 @@ var operatorBranchedMatcher = function (valueSelector, matcher, isRoot) {
// is OK.
var operatorMatchers = [];
_.each(valueSelector, function (operand, operator) {
var simpleRange = _.contains(['$lt', '$lte', '$gt', '$gte'], operator) &&
_.isNumber(operand);
var simpleEquality = _.contains(['$ne', '$eq'], operator) && !_.isObject(operand);
var simpleInclusion = _.contains(['$in', '$nin'], operator) &&
_.isArray(operand) && !_.any(operand, _.isObject);
Object.keys(valueSelector).forEach(function (operator) {
var operand = valueSelector[operator];
var simpleRange = ['$lt', '$lte', '$gt', '$gte'].includes(operator) &&
typeof operand === 'number';
var simpleEquality = ['$ne', '$eq'].includes(operator) && operand !== Object(operand);
var simpleInclusion = ['$in', '$nin'].includes(operator) &&
Array.isArray(operand) && !operand.some(function (x) { return x === Object(x); });
if (! (simpleRange || simpleInclusion || simpleEquality)) {
matcher._isSimple = false;
}
if (_.has(VALUE_OPERATORS, operator)) {
if (VALUE_OPERATORS.hasOwnProperty(operator)) {
operatorMatchers.push(
VALUE_OPERATORS[operator](operand, valueSelector, matcher, isRoot));
} else if (_.has(ELEMENT_OPERATORS, operator)) {
} else if (ELEMENT_OPERATORS.hasOwnProperty(operator)) {
var options = ELEMENT_OPERATORS[operator];
operatorMatchers.push(
convertElementMatcherToBranchedMatcher(
@@ -289,9 +289,9 @@ var operatorBranchedMatcher = function (valueSelector, matcher, isRoot) {
var compileArrayOfDocumentSelectors = function (
selectors, matcher, inElemMatch) {
if (!isArray(selectors) || _.isEmpty(selectors))
if (!isArray(selectors) || selectors.length === 0)
throw Error("$and/$or/$nor must be nonempty array");
return _.map(selectors, function (subSelector) {
return selectors.map(function (subSelector) {
if (!isPlainObject(subSelector))
throw Error("$or/$and/$nor entries need to be full objects");
return compileDocumentSelector(
@@ -317,7 +317,7 @@ var LOGICAL_OPERATORS = {
return matchers[0];
return function (doc) {
var result = _.any(matchers, function (f) {
var result = matchers.some(function (f) {
return f(doc).result;
});
// $or does NOT set arrayIndices when it has multiple
@@ -330,7 +330,7 @@ var LOGICAL_OPERATORS = {
var matchers = compileArrayOfDocumentSelectors(
subSelector, matcher, inElemMatch);
return function (doc) {
var result = _.all(matchers, function (f) {
var result = matchers.every(function (f) {
return !f(doc).result;
});
// Never set arrayIndices, because we only match if nothing in particular
@@ -405,7 +405,7 @@ var VALUE_OPERATORS = {
},
// $options just provides options for $regex; its logic is inside $regex
$options: function (operand, valueSelector) {
if (!_.has(valueSelector, '$regex'))
if (!valueSelector.hasOwnProperty('$regex'))
throw Error("$options needs a $regex");
return everythingMatcher;
},
@@ -419,11 +419,11 @@ var VALUE_OPERATORS = {
if (!isArray(operand))
throw Error("$all requires array");
// Not sure why, but this seems to be what MongoDB does.
if (_.isEmpty(operand))
if (operand.length === 0)
return nothingMatcher;
var branchedMatchers = [];
_.each(operand, function (criterion) {
operand.forEach(function (criterion) {
// XXX handle $all/$elemMatch combination
if (isOperatorObject(criterion))
throw Error("no $ expressions in $all");
@@ -445,7 +445,7 @@ var VALUE_OPERATORS = {
// matched using $geometry.
var maxDistance, point, distance;
if (isPlainObject(operand) && _.has(operand, '$geometry')) {
if (isPlainObject(operand) && operand.hasOwnProperty('$geometry')) {
// GeoJSON "2dsphere" mode.
maxDistance = operand.$maxDistance;
point = operand.$geometry;
@@ -488,7 +488,7 @@ var VALUE_OPERATORS = {
// each within-$maxDistance branching point.
branchedValues = expandArraysInBranches(branchedValues);
var result = {result: false};
_.every(branchedValues, function (branch) {
branchedValues.every(function (branch) {
// if operation is an update, don't skip branches, just return the first one (#3599)
if (!matcher._isUpdate){
if (!(typeof branch.value === "object")){
@@ -523,7 +523,7 @@ var distanceCoordinatePairs = function (a, b) {
b = pointToArray(b);
var x = a[0] - b[0];
var y = a[1] - b[1];
if (_.isNaN(x) || _.isNaN(y))
if (Number.isNaN(x) || Number.isNaN(y))
return null;
return Math.sqrt(x * x + y * y);
};
@@ -531,7 +531,7 @@ var distanceCoordinatePairs = function (a, b) {
// the second one to y no matter what user passes.
// In case user passes { lon: x, lat: y } returns [x, y]
var pointToArray = function (point) {
return _.map(point, _.identity);
return Array.isArray(point) ? point.slice() : [point.x, point.y];
};
// Helper for $lt/$gt/$lte/$gte.
@@ -671,7 +671,7 @@ ELEMENT_OPERATORS = {
throw Error("$in needs an array");
var elementMatchers = [];
_.each(operand, function (option) {
operand.forEach(function (option) {
if (option instanceof RegExp)
elementMatchers.push(regexpElementMatcher(option));
else if (isOperatorObject(option))
@@ -684,7 +684,7 @@ ELEMENT_OPERATORS = {
// Allow {a: {$in: [null]}} to match when 'a' does not exist.
if (value === undefined)
value = null;
return _.any(elementMatchers, function (e) {
return elementMatchers.some(function (e) {
return e(value);
});
};
@@ -801,7 +801,7 @@ ELEMENT_OPERATORS = {
throw Error("$elemMatch need an object");
var subMatcher, isDocMatcher;
if (isOperatorObject(_.omit(operand, _.keys(LOGICAL_OPERATORS)), true)) {
if (isOperatorObject(Object.keys(operand).filter(key => !Object.keys(LOGICAL_OPERATORS).includes(key)).reduce(function (a, b) { return Object.assign(a, {[b]: operand[b]}); }, {}), true)) {
subMatcher = compileValueSelector(operand, matcher);
isDocMatcher = false;
} else {
@@ -993,7 +993,7 @@ makeLookupFunction = function (key, options) {
// "look up this index" in that case, not "also look up this index in all
// the elements of the array".
if (isArray(firstLevel) && !(nextPartIsNumeric && options.forSort)) {
_.each(firstLevel, function (branch, arrayIndex) {
firstLevel.forEach(function (branch, arrayIndex) {
if (isPlainObject(branch)) {
appendToResult(lookupRest(
branch,
@@ -1009,7 +1009,7 @@ MinimongoTest.makeLookupFunction = makeLookupFunction;
expandArraysInBranches = function (branches, skipTheArrays) {
var branchesOut = [];
_.each(branches, function (branch) {
branches.forEach(function (branch) {
var thisIsArray = isArray(branch.value);
// We include the branch itself, *UNLESS* we it's an array that we're going
// to iterate and we're told to skip arrays. (That's right, we include some
@@ -1022,7 +1022,7 @@ expandArraysInBranches = function (branches, skipTheArrays) {
});
}
if (thisIsArray && !branch.dontIterate) {
_.each(branch.value, function (leaf, i) {
branch.value.forEach(function (leaf, i) {
branchesOut.push({
value: leaf,
arrayIndices: (branch.arrayIndices || []).concat(i)
@@ -1054,7 +1054,7 @@ var andSomeMatchers = function (subMatchers) {
return function (docOrBranches) {
var ret = {};
ret.result = _.all(subMatchers, function (f) {
ret.result = subMatchers.every(function (f) {
var subResult = f(docOrBranches);
// Copy a 'distance' number out of the first sub-matcher that has
// one. Yes, this means that if there are multiple $near fields in a

View File

@@ -9,13 +9,13 @@
Minimongo.Matcher.prototype.affectedByModifier = function (modifier) {
var self = this;
// safe check for $set/$unset being objects
modifier = _.extend({ $set: {}, $unset: {} }, modifier);
var modifiedPaths = _.keys(modifier.$set).concat(_.keys(modifier.$unset));
modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
var modifiedPaths = Object.keys(modifier.$set).concat(Object.keys(modifier.$unset));
var meaningfulPaths = self._getPaths();
return _.any(modifiedPaths, function (path) {
return modifiedPaths.some(function (path) {
var mod = path.split('.');
return _.any(meaningfulPaths, function (meaningfulPath) {
return meaningfulPaths.some(function (meaningfulPath) {
var sel = meaningfulPath.split('.');
var i = 0, j = 0;
@@ -64,14 +64,14 @@ Minimongo.Matcher.prototype.canBecomeTrueByModifier = function (modifier) {
if (!this.affectedByModifier(modifier))
return false;
modifier = _.extend({$set:{}, $unset:{}}, modifier);
var modifierPaths = _.keys(modifier.$set).concat(_.keys(modifier.$unset));
modifier = Object.assign({$set:{}, $unset:{}}, modifier);
var modifierPaths = Object.keys(modifier.$set).concat(Object.keys(modifier.$unset));
if (!self.isSimple())
return true;
if (_.any(self._getPaths(), pathHasNumericKeys) ||
_.any(modifierPaths, pathHasNumericKeys))
if (self._getPaths().some(pathHasNumericKeys) ||
modifierPaths.some(pathHasNumericKeys))
return true;
// check if there is a $set or $unset that indicates something is an
@@ -79,10 +79,11 @@ Minimongo.Matcher.prototype.canBecomeTrueByModifier = function (modifier) {
// NOTE: it is correct since we allow only scalars in $-operators
// Example: for selector {'a.b': {$gt: 5}} the modifier {'a.b.c':7} would
// definitely set the result to false as 'a.b' appears to be an object.
var expectedScalarIsObject = _.any(self._selector, function (sel, path) {
var expectedScalarIsObject = Object.keys(self._selector).some(function (path) {
var sel = self._selector[path];
if (! isOperatorObject(sel))
return false;
return _.any(modifierPaths, function (modifierPath) {
return modifierPaths.some(function (modifierPath) {
return startsWith(modifierPath, path + '.');
});
});
@@ -149,17 +150,17 @@ Minimongo.Matcher.prototype.matchingDocument = function () {
// Return anything from $in that matches the whole selector for this
// path. If nothing matches, returns `undefined` as nothing can make
// this selector into `true`.
return _.find(valueSelector.$in, function (x) {
return valueSelector.$in.find(function (x) {
return matcher.documentMatches({ placeholder: x }).result;
});
} else if (onlyContainsKeys(valueSelector, ['$gt', '$gte', '$lt', '$lte'])) {
var lowerBound = -Infinity, upperBound = Infinity;
_.each(['$lte', '$lt'], function (op) {
if (_.has(valueSelector, op) && valueSelector[op] < upperBound)
['$lte', '$lt'].forEach(function (op) {
if (valueSelector.hasOwnProperty(op) && valueSelector[op] < upperBound)
upperBound = valueSelector[op];
});
_.each(['$gte', '$gt'], function (op) {
if (_.has(valueSelector, op) && valueSelector[op] > lowerBound)
['$gte', '$gt'].forEach(function (op) {
if (valueSelector.hasOwnProperty(op) && valueSelector[op] > lowerBound)
lowerBound = valueSelector[op];
});
@@ -181,7 +182,7 @@ Minimongo.Matcher.prototype.matchingDocument = function () {
}
return self._selector[path];
},
_.identity /*conflict resolution is no resolution*/);
function (x) { return x; } /*conflict resolution is no resolution*/);
if (fallback)
self._matchingDocument = null;
@@ -190,28 +191,31 @@ Minimongo.Matcher.prototype.matchingDocument = function () {
};
var getPaths = function (sel) {
return _.keys(new Minimongo.Matcher(sel)._paths);
return _.chain(sel).map(function (v, k) {
return Object.keys(new Minimongo.Matcher(sel)._paths);
return Object.keys(sel).map(function (k) {
var v = sel[k];
// we don't know how to handle $where because it can be anything
if (k === "$where")
return ''; // matches everything
// we branch from $or/$and/$nor operator
if (_.contains(['$or', '$and', '$nor'], k))
return _.map(v, getPaths);
if (['$or', '$and', '$nor'].includes(k))
return v.map(getPaths);
// the value is a literal or some comparison operator
return k;
}).flatten().uniq().value();
})
.reduce(function (a, b) { return a.concat(b); }, [])
.filter(function (a, b, c) { return c.indexOf(a) === b; });
};
// A helper to ensure object has only certain keys
var onlyContainsKeys = function (obj, keys) {
return _.all(obj, function (v, k) {
return _.contains(keys, k);
return Object.keys(obj).every(function (k) {
return keys.includes(k);
});
};
var pathHasNumericKeys = function (path) {
return _.any(path.split('.'), isNumericKey);
return path.split('.').some(isNumericKey);
}
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)

View File

@@ -9,7 +9,7 @@ Minimongo.Matcher.prototype.combineIntoProjection = function (projection) {
// on all fields of the document. getSelectorPaths returns a list of paths
// selector depends on. If one of the paths is '' (empty string) representing
// the root or the whole document, complete projection should be returned.
if (_.contains(selectorPaths, ''))
if (selectorPaths.includes(''))
return {};
return combineImportantPathsIntoProjection(selectorPaths, projection);
@@ -17,8 +17,8 @@ Minimongo.Matcher.prototype.combineIntoProjection = function (projection) {
Minimongo._pathsElidingNumericKeys = function (paths) {
var self = this;
return _.map(paths, function (path) {
return _.reject(path.split('.'), isNumericKey).join('.');
return paths.map(function (path) {
return path.split('.').filter(function (part) { return !isNumericKey(part); }).join('.');
});
};
@@ -42,7 +42,8 @@ combineImportantPathsIntoProjection = function (paths, projection) {
// projection is pointing at fields to exclude
// make sure we don't exclude important paths
var mergedExclProjection = {};
_.each(mergedProjection, function (incl, path) {
Object.keys(mergedProjection).forEach(function (path) {
var incl = mergedProjection[path];
if (!incl)
mergedExclProjection[path] = false;
});
@@ -57,9 +58,10 @@ var treeToPaths = function (tree, prefix) {
prefix = prefix || '';
var result = {};
_.each(tree, function (val, key) {
if (_.isObject(val))
_.extend(result, treeToPaths(val, prefix + key + '.'));
Object.keys(tree).forEach(function (key) {
var val = tree[key];
if (val === Object(val))
Object.assign(result, treeToPaths(val, prefix + key + '.'));
else
result[prefix + key] = val;
});

View File

@@ -39,7 +39,8 @@ Minimongo.Sorter = function (spec, options) {
}
}
} else if (typeof spec === "object") {
_.each(spec, function (value, key) {
Object.keys(spec).forEach(function (key) {
var value = spec[key];
addSpecPart(key, value >= 0);
});
} else if (typeof spec === "function") {
@@ -57,14 +58,14 @@ Minimongo.Sorter = function (spec, options) {
// modifiers as this sort order. This is only implemented on the server.
if (self.affectedByModifier) {
var selector = {};
_.each(self._sortSpecParts, function (spec) {
self._sortSpecParts.forEach(function (spec) {
selector[spec.path] = 1;
});
self._selectorForAffectedByModifier = new Minimongo.Matcher(selector);
}
self._keyComparator = composeComparators(
_.map(self._sortSpecParts, function (spec, i) {
self._sortSpecParts.map(function (spec, i) {
return self._keyFieldComparator(i);
}));
@@ -77,11 +78,11 @@ Minimongo.Sorter = function (spec, options) {
// In addition to these methods, sorter_project.js defines combineIntoProjection
// on the server only.
_.extend(Minimongo.Sorter.prototype, {
Object.assign(Minimongo.Sorter.prototype, {
getComparator: function (options) {
var self = this;
// If sort is specified or have no distances, just use the comparator from
// If sort is specified or have no distances, just use the comparator from
// the source specification (which defaults to "everything is equal".
// issue #3599
// https://docs.mongodb.com/manual/reference/operator/query/near/#sort-operation
@@ -104,7 +105,7 @@ _.extend(Minimongo.Sorter.prototype, {
_getPaths: function () {
var self = this;
return _.pluck(self._sortSpecParts, 'path');
return self._sortSpecParts.map(function (part) { return part.path; });
},
// Finds the minimum key from the doc, according to the sort specs. (We say
@@ -163,7 +164,7 @@ _.extend(Minimongo.Sorter.prototype, {
var knownPaths = null;
_.each(self._sortSpecParts, function (spec, whichField) {
self._sortSpecParts.forEach(function (spec, whichField) {
// Expand any leaf arrays that we find, and ignore those arrays
// themselves. (We never sort based on an array itself.)
var branches = expandArraysInBranches(spec.lookup(doc), true);
@@ -175,7 +176,7 @@ _.extend(Minimongo.Sorter.prototype, {
var usedPaths = false;
valuesByIndexAndPath[whichField] = {};
_.each(branches, function (branch) {
branches.forEach(function (branch) {
if (!branch.arrayIndices) {
// If there are no array indices for a branch, then it must be the
// only branch, because the only thing that produces multiple branches
@@ -188,7 +189,7 @@ _.extend(Minimongo.Sorter.prototype, {
usedPaths = true;
var path = pathFromIndices(branch.arrayIndices);
if (_.has(valuesByIndexAndPath[whichField], path))
if (valuesByIndexAndPath[whichField].hasOwnProperty(path))
throw Error("duplicate path: " + path);
valuesByIndexAndPath[whichField][path] = branch.value;
@@ -202,7 +203,7 @@ _.extend(Minimongo.Sorter.prototype, {
// and 'a.x.y' are both arrays, but we don't allow this for now.
// #NestedArraySort
// XXX achieve full compatibility here
if (knownPaths && !_.has(knownPaths, path)) {
if (knownPaths && !knownPaths.hasOwnProperty(path)) {
throw Error("cannot index parallel arrays");
}
});
@@ -210,13 +211,13 @@ _.extend(Minimongo.Sorter.prototype, {
if (knownPaths) {
// Similarly to above, paths must match everywhere, unless this is a
// non-array field.
if (!_.has(valuesByIndexAndPath[whichField], '') &&
_.size(knownPaths) !== _.size(valuesByIndexAndPath[whichField])) {
if (!valuesByIndexAndPath[whichField].hasOwnProperty('') &&
Object.keys(knownPaths).length !== Object.keys(valuesByIndexAndPath[whichField]).length) {
throw Error("cannot index parallel arrays!");
}
} else if (usedPaths) {
knownPaths = {};
_.each(valuesByIndexAndPath[whichField], function (x, path) {
Object.keys(valuesByIndexAndPath[whichField]).forEach(function (path) {
knownPaths[path] = true;
});
}
@@ -224,8 +225,8 @@ _.extend(Minimongo.Sorter.prototype, {
if (!knownPaths) {
// Easy case: no use of arrays.
var soleKey = _.map(valuesByIndexAndPath, function (values) {
if (!_.has(values, ''))
var soleKey = valuesByIndexAndPath.map(function (values) {
if (!values.hasOwnProperty(''))
throw Error("no value in sole key case?");
return values[''];
});
@@ -233,11 +234,11 @@ _.extend(Minimongo.Sorter.prototype, {
return;
}
_.each(knownPaths, function (x, path) {
var key = _.map(valuesByIndexAndPath, function (values) {
if (_.has(values, ''))
Object.keys(knownPaths).forEach(function (path) {
var key = valuesByIndexAndPath.map(function (values) {
if (values.hasOwnProperty(''))
return values[''];
if (!_.has(values, path))
if (!values.hasOwnProperty(path))
throw Error("missing path?");
return values[path];
});
@@ -322,7 +323,7 @@ _.extend(Minimongo.Sorter.prototype, {
// If we are only sorting by distance, then we're not going to bother to
// build a key filter.
// XXX figure out how geoqueries interact with this stuff
if (_.isEmpty(self._sortSpecParts))
if (!self._sortSpecParts.length)
return;
var selector = matcher._selector;
@@ -333,11 +334,12 @@ _.extend(Minimongo.Sorter.prototype, {
return;
var constraintsByPath = {};
_.each(self._sortSpecParts, function (spec, i) {
self._sortSpecParts.forEach(function (spec, i) {
constraintsByPath[spec.path] = [];
});
_.each(selector, function (subSelector, key) {
Object.keys(selector).forEach(function (key) {
var subSelector = selector[key];
// XXX support $and and $or
var constraints = constraintsByPath[key];
@@ -362,8 +364,9 @@ _.extend(Minimongo.Sorter.prototype, {
}
if (isOperatorObject(subSelector)) {
_.each(subSelector, function (operand, operator) {
if (_.contains(['$lt', '$lte', '$gt', '$gte'], operator)) {
Object.keys(subSelector).forEach(function (operator) {
var operand = subSelector[operator];
if (['$lt', '$lte', '$gt', '$gte'].includes(operator)) {
// XXX this depends on us knowing that these operators don't use any
// of the arguments to compileElementSelector other than operand.
constraints.push(
@@ -390,12 +393,12 @@ _.extend(Minimongo.Sorter.prototype, {
// others; we shouldn't create a key filter unless the first sort field is
// restricted, though after that point we can restrict the other sort fields
// or not as we wish.
if (_.isEmpty(constraintsByPath[self._sortSpecParts[0].path]))
if (!constraintsByPath[self._sortSpecParts[0].path].length)
return;
self._keyFilter = function (key) {
return _.all(self._sortSpecParts, function (specPart, index) {
return _.all(constraintsByPath[specPart.path], function (f) {
return self._sortSpecParts.every(function (specPart, index) {
return constraintsByPath[specPart.path].every(function (f) {
return f(key[index]);
});
});

View File

@@ -8,7 +8,7 @@ const invalidCharMsg = {
};
export function assertIsValidFieldName(key) {
let match;
if (_.isString(key) && (match = key.match(/^\$|\.|\0/))) {
if (typeof key === 'string' && (match = key.match(/^\$|\.|\0/))) {
throw MinimongoError(`Key ${key} must not ${invalidCharMsg[match[0]]}`);
}
};
@@ -21,4 +21,4 @@ export function assertHasValidFieldNames(doc){
return value;
});
}
};
};

View File

@@ -16,7 +16,7 @@ LocalCollection.wrapTransform = function (transform) {
return transform;
var wrapped = function (doc) {
if (!_.has(doc, '_id')) {
if (!doc.hasOwnProperty('_id')) {
// XXX do we ever have a transform on the oplog's collection? because that
// collection has no _id.
throw new Error("can only transform documents with _id");
@@ -32,7 +32,7 @@ LocalCollection.wrapTransform = function (transform) {
throw new Error("transform must return object");
}
if (_.has(transformed, '_id')) {
if (transformed.hasOwnProperty('_id')) {
if (!EJSON.equals(transformed._id, id)) {
throw new Error("transformed document can't have different _id");
}

View File

@@ -13,7 +13,7 @@ Tinytest.add("minimongo - wrapTransform", function (test) {
return doc;
};
var transformed = wrap(validTransform)({_id: "asdf", x: 54});
test.equal(_.keys(transformed), ['_id', 'y', 'z']);
test.equal(Object.keys(transformed), ['_id', 'y', 'z']);
test.equal(transformed.y, 42);
test.equal(transformed.z(), 43);
@@ -28,7 +28,7 @@ Tinytest.add("minimongo - wrapTransform", function (test) {
"asdf", new MongoID.ObjectID(), false, null, true,
27, [123], /adsf/, new Date, function () {}, undefined
];
_.each(invalidObjects, function (invalidObject) {
invalidObjects.forEach(function (invalidObject) {
var wrapped = wrap(function () { return invalidObject; });
test.throws(function () {
wrapped({_id: "asdf"});