mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Separated.
This commit is contained in:
5
packages/minimongo/common_main.js
Normal file
5
packages/minimongo/common_main.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import {LocalCollection} from './local_collection.js';
|
||||
import {Matcher} from './matcher.js';
|
||||
import {Sorter} from './sorter.js';
|
||||
|
||||
Minimongo = {LocalCollection, Matcher, Sorter};
|
||||
@@ -1,5 +1,7 @@
|
||||
import {LocalCollection} from './local_collection.js';
|
||||
|
||||
// Cursor: a specification for a particular subset of documents, w/
|
||||
// a defined order, limit, and offset. creating a Cursor with LocalCollection.find(),
|
||||
export class Cursor {
|
||||
// don't call this ctor directly. use LocalCollection.find().
|
||||
constructor (collection, selector, options) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import {Cursor} from './cursor.js';
|
||||
import {ObserveHandle} from './observe_handle.js';
|
||||
|
||||
// XXX type checking on selectors (graceful error if malformed)
|
||||
|
||||
// LocalCollection: a set of documents that supports queries and modifiers.
|
||||
export class LocalCollection {
|
||||
static Cursor = Cursor;
|
||||
|
||||
|
||||
@@ -1186,7 +1186,18 @@ function makeLookupFunction (key, options) {
|
||||
};
|
||||
}
|
||||
|
||||
MinimongoTest.makeLookupFunction = makeLookupFunction;
|
||||
// Object exported only for unit testing.
|
||||
// Use it to export private functions to test in Tinytest.
|
||||
MinimongoTest = {makeLookupFunction};
|
||||
MinimongoError = function (message, options = {}) {
|
||||
if (typeof message === "string" && options.field) {
|
||||
message += ` for field '${options.field}'`;
|
||||
}
|
||||
|
||||
var e = new Error(message);
|
||||
e.name = "MinimongoError";
|
||||
return e;
|
||||
};
|
||||
|
||||
function nothingMatcher (docOrBranchedValues) {
|
||||
return {result: false};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// the handle that comes back from observe.
|
||||
// ObserveHandle: the return value of a live query.
|
||||
export class ObserveHandle {}
|
||||
|
||||
@@ -22,11 +22,8 @@ Package.onUse(api => {
|
||||
'tracker'
|
||||
]);
|
||||
|
||||
api.addFiles('minimongo.js');
|
||||
api.addFiles('minimongo_server.js', 'server');
|
||||
|
||||
// api.mainModule('client_main.js', 'client');
|
||||
// api.mainModule('server_main.js', 'server');
|
||||
api.mainModule('common_main.js', 'client');
|
||||
api.mainModule('server_main.js', 'server');
|
||||
});
|
||||
|
||||
Package.onTest(api => {
|
||||
|
||||
@@ -1,19 +1,4 @@
|
||||
// Knows how to combine a mongo selector and a fields projection to a new fields
|
||||
// projection taking into account active fields from the passed selector.
|
||||
// @returns Object - projection object (same as fields option of mongo cursor)
|
||||
Minimongo.Matcher.prototype.combineIntoProjection = function (projection) {
|
||||
var self = this;
|
||||
var selectorPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());
|
||||
|
||||
// Special case for $where operator in the selector - projection should depend
|
||||
// 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 (selectorPaths.includes(''))
|
||||
return {};
|
||||
|
||||
return combineImportantPathsIntoProjection(selectorPaths, projection);
|
||||
};
|
||||
import './common_main.js';
|
||||
|
||||
Minimongo._pathsElidingNumericKeys = function (paths) {
|
||||
var self = this;
|
||||
@@ -22,53 +7,6 @@ Minimongo._pathsElidingNumericKeys = function (paths) {
|
||||
});
|
||||
};
|
||||
|
||||
combineImportantPathsIntoProjection = function (paths, projection) {
|
||||
var prjDetails = projectionDetails(projection);
|
||||
var tree = prjDetails.tree;
|
||||
var mergedProjection = {};
|
||||
|
||||
// merge the paths to include
|
||||
tree = pathsToTree(paths,
|
||||
function (path) { return true; },
|
||||
function (node, path, fullPath) { return true; },
|
||||
tree);
|
||||
mergedProjection = treeToPaths(tree);
|
||||
if (prjDetails.including) {
|
||||
// both selector and projection are pointing on fields to include
|
||||
// so we can just return the merged tree
|
||||
return mergedProjection;
|
||||
} else {
|
||||
// selector is pointing at fields to include
|
||||
// projection is pointing at fields to exclude
|
||||
// make sure we don't exclude important paths
|
||||
var mergedExclProjection = {};
|
||||
Object.keys(mergedProjection).forEach(function (path) {
|
||||
var incl = mergedProjection[path];
|
||||
if (!incl)
|
||||
mergedExclProjection[path] = false;
|
||||
});
|
||||
|
||||
return mergedExclProjection;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a set of key paths similar to
|
||||
// { 'foo.bar': 1, 'a.b.c': 1 }
|
||||
var treeToPaths = function (tree, prefix) {
|
||||
prefix = prefix || '';
|
||||
var result = {};
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Returns true if the modifier applied to some document may change the result
|
||||
// of matching the document by selector
|
||||
// The modifier is always in a form of Object:
|
||||
@@ -115,13 +53,6 @@ Minimongo.Matcher.prototype.affectedByModifier = function (modifier) {
|
||||
});
|
||||
};
|
||||
|
||||
// Minimongo.Sorter gets a similar method, which delegates to a Matcher it made
|
||||
// for this exact purpose.
|
||||
Minimongo.Sorter.prototype.affectedByModifier = function (modifier) {
|
||||
var self = this;
|
||||
return self._selectorForAffectedByModifier.affectedByModifier(modifier);
|
||||
};
|
||||
|
||||
// @param modifier - Object: MongoDB-styled modifier with `$set`s and `$unsets`
|
||||
// only. (assumed to come from oplog)
|
||||
// @returns - Boolean: if after applying the modifier, selector can start
|
||||
@@ -192,6 +123,23 @@ Minimongo.Matcher.prototype.canBecomeTrueByModifier = function (modifier) {
|
||||
return self.documentMatches(matchingDocument).result;
|
||||
};
|
||||
|
||||
// Knows how to combine a mongo selector and a fields projection to a new fields
|
||||
// projection taking into account active fields from the passed selector.
|
||||
// @returns Object - projection object (same as fields option of mongo cursor)
|
||||
Minimongo.Matcher.prototype.combineIntoProjection = function (projection) {
|
||||
var self = this;
|
||||
var selectorPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());
|
||||
|
||||
// Special case for $where operator in the selector - projection should depend
|
||||
// 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 (selectorPaths.includes(''))
|
||||
return {};
|
||||
|
||||
return combineImportantPathsIntoProjection(selectorPaths, projection);
|
||||
};
|
||||
|
||||
// Returns an object that would match the selector if possible or null if the
|
||||
// selector is too complex for us to analyze
|
||||
// { 'a.b': { ans: 42 }, 'foo.bar': null, 'foo.baz': "something" }
|
||||
@@ -261,7 +209,50 @@ Minimongo.Matcher.prototype.matchingDocument = function () {
|
||||
return self._matchingDocument;
|
||||
};
|
||||
|
||||
var getPaths = function (sel) {
|
||||
// Minimongo.Sorter gets a similar method, which delegates to a Matcher it made
|
||||
// for this exact purpose.
|
||||
Minimongo.Sorter.prototype.affectedByModifier = function (modifier) {
|
||||
var self = this;
|
||||
return self._selectorForAffectedByModifier.affectedByModifier(modifier);
|
||||
};
|
||||
|
||||
Minimongo.Sorter.prototype.combineIntoProjection = function (projection) {
|
||||
var self = this;
|
||||
var specPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());
|
||||
return combineImportantPathsIntoProjection(specPaths, projection);
|
||||
};
|
||||
|
||||
function combineImportantPathsIntoProjection (paths, projection) {
|
||||
var prjDetails = projectionDetails(projection);
|
||||
var tree = prjDetails.tree;
|
||||
var mergedProjection = {};
|
||||
|
||||
// merge the paths to include
|
||||
tree = pathsToTree(paths,
|
||||
function (path) { return true; },
|
||||
function (node, path, fullPath) { return true; },
|
||||
tree);
|
||||
mergedProjection = treeToPaths(tree);
|
||||
if (prjDetails.including) {
|
||||
// both selector and projection are pointing on fields to include
|
||||
// so we can just return the merged tree
|
||||
return mergedProjection;
|
||||
} else {
|
||||
// selector is pointing at fields to include
|
||||
// projection is pointing at fields to exclude
|
||||
// make sure we don't exclude important paths
|
||||
var mergedExclProjection = {};
|
||||
Object.keys(mergedProjection).forEach(function (path) {
|
||||
var incl = mergedProjection[path];
|
||||
if (!incl)
|
||||
mergedExclProjection[path] = false;
|
||||
});
|
||||
|
||||
return mergedExclProjection;
|
||||
}
|
||||
}
|
||||
|
||||
function getPaths (sel) {
|
||||
return Object.keys(new Minimongo.Matcher(sel)._paths);
|
||||
return Object.keys(sel).map(function (k) {
|
||||
var v = sel[k];
|
||||
@@ -276,26 +267,37 @@ var getPaths = function (sel) {
|
||||
})
|
||||
.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) {
|
||||
function onlyContainsKeys (obj, keys) {
|
||||
return Object.keys(obj).every(function (k) {
|
||||
return keys.includes(k);
|
||||
});
|
||||
};
|
||||
|
||||
var pathHasNumericKeys = function (path) {
|
||||
return path.split('.').some(isNumericKey);
|
||||
}
|
||||
|
||||
function pathHasNumericKeys (path) {
|
||||
return path.split('.').some(isNumericKey);
|
||||
|
||||
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)
|
||||
var startsWith = function(str, starts) {
|
||||
function startsWith(str, starts) {
|
||||
return str.length >= starts.length &&
|
||||
str.substring(0, starts.length) === starts;
|
||||
};
|
||||
Minimongo.Sorter.prototype.combineIntoProjection = function (projection) {
|
||||
var self = this;
|
||||
var specPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());
|
||||
return combineImportantPathsIntoProjection(specPaths, projection);
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a set of key paths similar to
|
||||
// { 'foo.bar': 1, 'a.b.c': 1 }
|
||||
function treeToPaths (tree, prefix) {
|
||||
prefix = prefix || '';
|
||||
var result = {};
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,37 +1,3 @@
|
||||
import {LocalCollection} from './local_collection.js';
|
||||
import {Matcher} from './matcher.js';
|
||||
import {
|
||||
isIndexable,
|
||||
isNumericKey,
|
||||
isOperatorObject,
|
||||
} from './common.js';
|
||||
|
||||
|
||||
// XXX type checking on selectors (graceful error if malformed)
|
||||
|
||||
// LocalCollection: a set of documents that supports queries and modifiers.
|
||||
|
||||
// Cursor: a specification for a particular subset of documents, w/
|
||||
// a defined order, limit, and offset. creating a Cursor with LocalCollection.find(),
|
||||
|
||||
// ObserveHandle: the return value of a live query.
|
||||
|
||||
Minimongo = {LocalCollection, Matcher};
|
||||
|
||||
// Object exported only for unit testing.
|
||||
// Use it to export private functions to test in Tinytest.
|
||||
MinimongoTest = {};
|
||||
|
||||
MinimongoError = function (message, options={}) {
|
||||
if (typeof message === "string" && options.field) {
|
||||
message += ` for field '${options.field}'`;
|
||||
}
|
||||
|
||||
var e = new Error(message);
|
||||
e.name = "MinimongoError";
|
||||
return e;
|
||||
};
|
||||
|
||||
// Give a sort spec, which can be in any of these forms:
|
||||
// {"key1": 1, "key2": -1}
|
||||
// [["key1", "asc"], ["key2", "desc"]]
|
||||
@@ -45,75 +11,73 @@ MinimongoError = function (message, options={}) {
|
||||
// first object comes first in order, 1 if the second object comes
|
||||
// first, or 0 if neither object comes before the other.
|
||||
|
||||
Minimongo.Sorter = function (spec, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
export class Sorter {
|
||||
constructor (spec, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
self._sortSpecParts = [];
|
||||
self._sortFunction = null;
|
||||
self._sortSpecParts = [];
|
||||
self._sortFunction = null;
|
||||
|
||||
var addSpecPart = function (path, ascending) {
|
||||
if (!path)
|
||||
throw Error("sort keys must be non-empty");
|
||||
if (path.charAt(0) === '$')
|
||||
throw Error("unsupported sort key: " + path);
|
||||
self._sortSpecParts.push({
|
||||
path: path,
|
||||
lookup: makeLookupFunction(path, {forSort: true}),
|
||||
ascending: ascending
|
||||
});
|
||||
};
|
||||
var addSpecPart = function (path, ascending) {
|
||||
if (!path)
|
||||
throw Error("sort keys must be non-empty");
|
||||
if (path.charAt(0) === '$')
|
||||
throw Error("unsupported sort key: " + path);
|
||||
self._sortSpecParts.push({
|
||||
path: path,
|
||||
lookup: makeLookupFunction(path, {forSort: true}),
|
||||
ascending: ascending
|
||||
});
|
||||
};
|
||||
|
||||
if (spec instanceof Array) {
|
||||
for (var i = 0; i < spec.length; i++) {
|
||||
if (typeof spec[i] === "string") {
|
||||
addSpecPart(spec[i], true);
|
||||
} else {
|
||||
addSpecPart(spec[i][0], spec[i][1] !== "desc");
|
||||
if (spec instanceof Array) {
|
||||
for (var i = 0; i < spec.length; i++) {
|
||||
if (typeof spec[i] === "string") {
|
||||
addSpecPart(spec[i], true);
|
||||
} else {
|
||||
addSpecPart(spec[i][0], spec[i][1] !== "desc");
|
||||
}
|
||||
}
|
||||
} else if (typeof spec === "object") {
|
||||
Object.keys(spec).forEach(function (key) {
|
||||
var value = spec[key];
|
||||
addSpecPart(key, value >= 0);
|
||||
});
|
||||
} else if (typeof spec === "function") {
|
||||
self._sortFunction = spec;
|
||||
} else {
|
||||
throw Error("Bad sort specification: " + JSON.stringify(spec));
|
||||
}
|
||||
} else if (typeof spec === "object") {
|
||||
Object.keys(spec).forEach(function (key) {
|
||||
var value = spec[key];
|
||||
addSpecPart(key, value >= 0);
|
||||
});
|
||||
} else if (typeof spec === "function") {
|
||||
self._sortFunction = spec;
|
||||
} else {
|
||||
throw Error("Bad sort specification: " + JSON.stringify(spec));
|
||||
|
||||
// If a function is specified for sorting, we skip the rest.
|
||||
if (self._sortFunction)
|
||||
return;
|
||||
|
||||
// To implement affectedByModifier, we piggy-back on top of Matcher's
|
||||
// affectedByModifier code; we create a selector that is affected by the same
|
||||
// modifiers as this sort order. This is only implemented on the server.
|
||||
if (self.affectedByModifier) {
|
||||
var selector = {};
|
||||
self._sortSpecParts.forEach(function (spec) {
|
||||
selector[spec.path] = 1;
|
||||
});
|
||||
self._selectorForAffectedByModifier = new Minimongo.Matcher(selector);
|
||||
}
|
||||
|
||||
self._keyComparator = composeComparators(
|
||||
self._sortSpecParts.map(function (spec, i) {
|
||||
return self._keyFieldComparator(i);
|
||||
}));
|
||||
|
||||
// If you specify a matcher for this Sorter, _keyFilter may be set to a
|
||||
// function which selects whether or not a given "sort key" (tuple of values
|
||||
// for the different sort spec fields) is compatible with the selector.
|
||||
self._keyFilter = null;
|
||||
options.matcher && self._useWithMatcher(options.matcher);
|
||||
}
|
||||
|
||||
// If a function is specified for sorting, we skip the rest.
|
||||
if (self._sortFunction)
|
||||
return;
|
||||
|
||||
// To implement affectedByModifier, we piggy-back on top of Matcher's
|
||||
// affectedByModifier code; we create a selector that is affected by the same
|
||||
// modifiers as this sort order. This is only implemented on the server.
|
||||
if (self.affectedByModifier) {
|
||||
var selector = {};
|
||||
self._sortSpecParts.forEach(function (spec) {
|
||||
selector[spec.path] = 1;
|
||||
});
|
||||
self._selectorForAffectedByModifier = new Minimongo.Matcher(selector);
|
||||
}
|
||||
|
||||
self._keyComparator = composeComparators(
|
||||
self._sortSpecParts.map(function (spec, i) {
|
||||
return self._keyFieldComparator(i);
|
||||
}));
|
||||
|
||||
// If you specify a matcher for this Sorter, _keyFilter may be set to a
|
||||
// function which selects whether or not a given "sort key" (tuple of values
|
||||
// for the different sort spec fields) is compatible with the selector.
|
||||
self._keyFilter = null;
|
||||
options.matcher && self._useWithMatcher(options.matcher);
|
||||
};
|
||||
|
||||
// In addition to these methods, sorter_project.js defines combineIntoProjection
|
||||
// on the server only.
|
||||
Object.assign(Minimongo.Sorter.prototype, {
|
||||
getComparator: function (options) {
|
||||
getComparator (options) {
|
||||
var self = this;
|
||||
|
||||
// If sort is specified or have no distances, just use the comparator from
|
||||
@@ -135,55 +99,24 @@ Object.assign(Minimongo.Sorter.prototype, {
|
||||
throw Error("Missing distance for " + b._id);
|
||||
return distances.get(a._id) - distances.get(b._id);
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
_getPaths: function () {
|
||||
// Takes in two keys: arrays whose lengths match the number of spec
|
||||
// parts. Returns negative, 0, or positive based on using the sort spec to
|
||||
// compare fields.
|
||||
_compareKeys (key1, key2) {
|
||||
var self = this;
|
||||
return self._sortSpecParts.map(function (part) { return part.path; });
|
||||
},
|
||||
if (key1.length !== self._sortSpecParts.length ||
|
||||
key2.length !== self._sortSpecParts.length) {
|
||||
throw Error("Key has wrong length");
|
||||
}
|
||||
|
||||
// Finds the minimum key from the doc, according to the sort specs. (We say
|
||||
// "minimum" here but this is with respect to the sort spec, so "descending"
|
||||
// sort fields mean we're finding the max for that field.)
|
||||
//
|
||||
// Note that this is NOT "find the minimum value of the first field, the
|
||||
// minimum value of the second field, etc"... it's "choose the
|
||||
// lexicographically minimum value of the key vector, allowing only keys which
|
||||
// you can find along the same paths". ie, for a doc {a: [{x: 0, y: 5}, {x:
|
||||
// 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and
|
||||
// [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.
|
||||
_getMinKeyFromDoc: function (doc) {
|
||||
var self = this;
|
||||
var minKey = null;
|
||||
|
||||
self._generateKeysFromDoc(doc, function (key) {
|
||||
if (!self._keyCompatibleWithSelector(key))
|
||||
return;
|
||||
|
||||
if (minKey === null) {
|
||||
minKey = key;
|
||||
return;
|
||||
}
|
||||
if (self._compareKeys(key, minKey) < 0) {
|
||||
minKey = key;
|
||||
}
|
||||
});
|
||||
|
||||
// This could happen if our key filter somehow filters out all the keys even
|
||||
// though somehow the selector matches.
|
||||
if (minKey === null)
|
||||
throw Error("sort selector found no keys in doc?");
|
||||
return minKey;
|
||||
},
|
||||
|
||||
_keyCompatibleWithSelector: function (key) {
|
||||
var self = this;
|
||||
return !self._keyFilter || self._keyFilter(key);
|
||||
},
|
||||
return self._keyComparator(key1, key2);
|
||||
}
|
||||
|
||||
// Iterates over each possible "key" from doc (ie, over each branch), calling
|
||||
// 'cb' with the key.
|
||||
_generateKeysFromDoc: function (doc, cb) {
|
||||
_generateKeysFromDoc (doc, cb) {
|
||||
var self = this;
|
||||
|
||||
if (self._sortSpecParts.length === 0)
|
||||
@@ -278,37 +211,11 @@ Object.assign(Minimongo.Sorter.prototype, {
|
||||
});
|
||||
cb(key);
|
||||
});
|
||||
},
|
||||
|
||||
// Takes in two keys: arrays whose lengths match the number of spec
|
||||
// parts. Returns negative, 0, or positive based on using the sort spec to
|
||||
// compare fields.
|
||||
_compareKeys: function (key1, key2) {
|
||||
var self = this;
|
||||
if (key1.length !== self._sortSpecParts.length ||
|
||||
key2.length !== self._sortSpecParts.length) {
|
||||
throw Error("Key has wrong length");
|
||||
}
|
||||
|
||||
return self._keyComparator(key1, key2);
|
||||
},
|
||||
|
||||
// Given an index 'i', returns a comparator that compares two key arrays based
|
||||
// on field 'i'.
|
||||
_keyFieldComparator: function (i) {
|
||||
var self = this;
|
||||
var invert = !self._sortSpecParts[i].ascending;
|
||||
return function (key1, key2) {
|
||||
var compare = LocalCollection._f._cmp(key1[i], key2[i]);
|
||||
if (invert)
|
||||
compare = -compare;
|
||||
return compare;
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// Returns a comparator that represents the sort specification (but not
|
||||
// including a possible geoquery distance tie-breaker).
|
||||
_getBaseComparator: function () {
|
||||
_getBaseComparator () {
|
||||
var self = this;
|
||||
|
||||
if (self._sortFunction)
|
||||
@@ -327,7 +234,64 @@ Object.assign(Minimongo.Sorter.prototype, {
|
||||
var key2 = self._getMinKeyFromDoc(doc2);
|
||||
return self._compareKeys(key1, key2);
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// Finds the minimum key from the doc, according to the sort specs. (We say
|
||||
// "minimum" here but this is with respect to the sort spec, so "descending"
|
||||
// sort fields mean we're finding the max for that field.)
|
||||
//
|
||||
// Note that this is NOT "find the minimum value of the first field, the
|
||||
// minimum value of the second field, etc"... it's "choose the
|
||||
// lexicographically minimum value of the key vector, allowing only keys which
|
||||
// you can find along the same paths". ie, for a doc {a: [{x: 0, y: 5}, {x:
|
||||
// 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and
|
||||
// [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.
|
||||
_getMinKeyFromDoc (doc) {
|
||||
var self = this;
|
||||
var minKey = null;
|
||||
|
||||
self._generateKeysFromDoc(doc, function (key) {
|
||||
if (!self._keyCompatibleWithSelector(key))
|
||||
return;
|
||||
|
||||
if (minKey === null) {
|
||||
minKey = key;
|
||||
return;
|
||||
}
|
||||
if (self._compareKeys(key, minKey) < 0) {
|
||||
minKey = key;
|
||||
}
|
||||
});
|
||||
|
||||
// This could happen if our key filter somehow filters out all the keys even
|
||||
// though somehow the selector matches.
|
||||
if (minKey === null)
|
||||
throw Error("sort selector found no keys in doc?");
|
||||
return minKey;
|
||||
}
|
||||
|
||||
_getPaths () {
|
||||
var self = this;
|
||||
return self._sortSpecParts.map(function (part) { return part.path; });
|
||||
}
|
||||
|
||||
_keyCompatibleWithSelector (key) {
|
||||
var self = this;
|
||||
return !self._keyFilter || self._keyFilter(key);
|
||||
}
|
||||
|
||||
// Given an index 'i', returns a comparator that compares two key arrays based
|
||||
// on field 'i'.
|
||||
_keyFieldComparator (i) {
|
||||
var self = this;
|
||||
var invert = !self._sortSpecParts[i].ascending;
|
||||
return function (key1, key2) {
|
||||
var compare = LocalCollection._f._cmp(key1[i], key2[i]);
|
||||
if (invert)
|
||||
compare = -compare;
|
||||
return compare;
|
||||
};
|
||||
}
|
||||
|
||||
// In MongoDB, if you have documents
|
||||
// {_id: 'x', a: [1, 10]} and
|
||||
@@ -348,7 +312,7 @@ Object.assign(Minimongo.Sorter.prototype, {
|
||||
// skip sort keys that don't match the selector. The logic here is pretty
|
||||
// subtle and undocumented; we've gotten as close as we can figure out based
|
||||
// on our understanding of Mongo's behavior.
|
||||
_useWithMatcher: function (matcher) {
|
||||
_useWithMatcher (matcher) {
|
||||
var self = this;
|
||||
|
||||
if (self._keyFilter)
|
||||
@@ -438,7 +402,7 @@ Object.assign(Minimongo.Sorter.prototype, {
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Given an array of comparators
|
||||
// (functions (a,b)->(negative or positive or zero)), returns a single
|
||||
Reference in New Issue
Block a user