mongodb-livedata-server 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/livedata_server.d.ts +4 -4
- package/dist/livedata_server.js +11 -11
- package/dist/meteor/binary-heap/max_heap.d.ts +31 -31
- package/dist/meteor/binary-heap/max_heap.js +186 -186
- package/dist/meteor/binary-heap/min_heap.d.ts +6 -6
- package/dist/meteor/binary-heap/min_heap.js +17 -17
- package/dist/meteor/binary-heap/min_max_heap.d.ts +11 -11
- package/dist/meteor/binary-heap/min_max_heap.js +48 -48
- package/dist/meteor/callback-hook/hook.d.ts +11 -11
- package/dist/meteor/callback-hook/hook.js +78 -78
- package/dist/meteor/ddp/crossbar.d.ts +15 -15
- package/dist/meteor/ddp/crossbar.js +136 -136
- package/dist/meteor/ddp/heartbeat.d.ts +19 -19
- package/dist/meteor/ddp/heartbeat.js +77 -77
- package/dist/meteor/ddp/livedata_server.d.ts +141 -142
- package/dist/meteor/ddp/livedata_server.js +403 -403
- package/dist/meteor/ddp/method-invocation.d.ts +35 -35
- package/dist/meteor/ddp/method-invocation.js +72 -72
- package/dist/meteor/ddp/random-stream.d.ts +8 -8
- package/dist/meteor/ddp/random-stream.js +100 -100
- package/dist/meteor/ddp/session-collection-view.d.ts +20 -20
- package/dist/meteor/ddp/session-collection-view.js +106 -106
- package/dist/meteor/ddp/session-document-view.d.ts +8 -8
- package/dist/meteor/ddp/session-document-view.js +82 -82
- package/dist/meteor/ddp/session.d.ts +75 -75
- package/dist/meteor/ddp/session.js +590 -590
- package/dist/meteor/ddp/stream_server.d.ts +20 -21
- package/dist/meteor/ddp/stream_server.js +181 -181
- package/dist/meteor/ddp/subscription.d.ts +94 -94
- package/dist/meteor/ddp/subscription.js +370 -370
- package/dist/meteor/ddp/utils.d.ts +8 -8
- package/dist/meteor/ddp/utils.js +104 -104
- package/dist/meteor/ddp/writefence.d.ts +20 -20
- package/dist/meteor/ddp/writefence.js +111 -111
- package/dist/meteor/diff-sequence/diff.d.ts +17 -17
- package/dist/meteor/diff-sequence/diff.js +257 -257
- package/dist/meteor/ejson/ejson.d.ts +82 -82
- package/dist/meteor/ejson/ejson.js +568 -569
- package/dist/meteor/ejson/stringify.d.ts +2 -2
- package/dist/meteor/ejson/stringify.js +119 -119
- package/dist/meteor/ejson/utils.d.ts +12 -12
- package/dist/meteor/ejson/utils.js +42 -42
- package/dist/meteor/mongo/caching_change_observer.d.ts +16 -16
- package/dist/meteor/mongo/caching_change_observer.js +63 -63
- package/dist/meteor/mongo/doc_fetcher.d.ts +7 -7
- package/dist/meteor/mongo/doc_fetcher.js +53 -53
- package/dist/meteor/mongo/geojson_utils.d.ts +3 -3
- package/dist/meteor/mongo/geojson_utils.js +40 -41
- package/dist/meteor/mongo/live_connection.d.ts +28 -28
- package/dist/meteor/mongo/live_connection.js +264 -264
- package/dist/meteor/mongo/live_cursor.d.ts +25 -25
- package/dist/meteor/mongo/live_cursor.js +60 -60
- package/dist/meteor/mongo/minimongo_common.d.ts +84 -84
- package/dist/meteor/mongo/minimongo_common.js +1998 -1998
- package/dist/meteor/mongo/minimongo_matcher.d.ts +23 -23
- package/dist/meteor/mongo/minimongo_matcher.js +283 -283
- package/dist/meteor/mongo/minimongo_sorter.d.ts +16 -16
- package/dist/meteor/mongo/minimongo_sorter.js +268 -268
- package/dist/meteor/mongo/observe_driver_utils.d.ts +9 -9
- package/dist/meteor/mongo/observe_driver_utils.js +72 -73
- package/dist/meteor/mongo/observe_multiplexer.d.ts +46 -46
- package/dist/meteor/mongo/observe_multiplexer.js +203 -203
- package/dist/meteor/mongo/oplog-observe-driver.d.ts +68 -68
- package/dist/meteor/mongo/oplog-observe-driver.js +918 -918
- package/dist/meteor/mongo/oplog_tailing.d.ts +35 -35
- package/dist/meteor/mongo/oplog_tailing.js +352 -352
- package/dist/meteor/mongo/oplog_v2_converter.d.ts +1 -1
- package/dist/meteor/mongo/oplog_v2_converter.js +125 -126
- package/dist/meteor/mongo/polling_observe_driver.d.ts +30 -30
- package/dist/meteor/mongo/polling_observe_driver.js +216 -221
- package/dist/meteor/mongo/synchronous-cursor.d.ts +17 -17
- package/dist/meteor/mongo/synchronous-cursor.js +261 -261
- package/dist/meteor/mongo/synchronous-queue.d.ts +13 -13
- package/dist/meteor/mongo/synchronous-queue.js +110 -110
- package/dist/meteor/ordered-dict/ordered_dict.d.ts +31 -31
- package/dist/meteor/ordered-dict/ordered_dict.js +198 -198
- package/dist/meteor/random/AbstractRandomGenerator.d.ts +42 -42
- package/dist/meteor/random/AbstractRandomGenerator.js +92 -92
- package/dist/meteor/random/AleaRandomGenerator.d.ts +13 -13
- package/dist/meteor/random/AleaRandomGenerator.js +90 -90
- package/dist/meteor/random/NodeRandomGenerator.d.ts +16 -16
- package/dist/meteor/random/NodeRandomGenerator.js +42 -42
- package/dist/meteor/random/createAleaGenerator.d.ts +2 -2
- package/dist/meteor/random/createAleaGenerator.js +32 -32
- package/dist/meteor/random/createRandom.d.ts +1 -1
- package/dist/meteor/random/createRandom.js +22 -22
- package/dist/meteor/random/main.d.ts +1 -1
- package/dist/meteor/random/main.js +12 -12
- package/dist/meteor/types.d.ts +1 -1
- package/dist/meteor/types.js +2 -2
- package/package.json +5 -5
|
@@ -1,268 +1,268 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const minimongo_common_1 = require("./minimongo_common");
|
|
4
|
-
const minimongo_matcher_1 = require("./minimongo_matcher");
|
|
5
|
-
// Give a sort spec, which can be in any of these forms:
|
|
6
|
-
// {"key1": 1, "key2": -1}
|
|
7
|
-
// [["key1", "asc"], ["key2", "desc"]]
|
|
8
|
-
// ["key1", ["key2", "desc"]]
|
|
9
|
-
//
|
|
10
|
-
// (.. with the first form being dependent on the key enumeration
|
|
11
|
-
// behavior of your javascript VM, which usually does what you mean in
|
|
12
|
-
// this case if the key names don't look like integers ..)
|
|
13
|
-
//
|
|
14
|
-
// return a function that takes two objects, and returns -1 if the
|
|
15
|
-
// first object comes first in order, 1 if the second object comes
|
|
16
|
-
// first, or 0 if neither object comes before the other.
|
|
17
|
-
class MinimongoSorter {
|
|
18
|
-
constructor(spec) {
|
|
19
|
-
this.combineIntoProjection = function (projection) {
|
|
20
|
-
return (0, minimongo_common_1.combineImportantPathsIntoProjection)((0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths()), projection);
|
|
21
|
-
};
|
|
22
|
-
this._sortSpecParts = [];
|
|
23
|
-
this._sortFunction = null;
|
|
24
|
-
const addSpecPart = (path, ascending) => {
|
|
25
|
-
if (!path) {
|
|
26
|
-
throw Error('sort keys must be non-empty');
|
|
27
|
-
}
|
|
28
|
-
if (path.charAt(0) === '$') {
|
|
29
|
-
throw Error(`unsupported sort key: ${path}`);
|
|
30
|
-
}
|
|
31
|
-
this._sortSpecParts.push({
|
|
32
|
-
ascending,
|
|
33
|
-
lookup: (0, minimongo_common_1.makeLookupFunction)(path, { forSort: true }),
|
|
34
|
-
path
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
if (spec instanceof Array) {
|
|
38
|
-
spec.forEach(element => {
|
|
39
|
-
if (typeof element === 'string') {
|
|
40
|
-
addSpecPart(element, true);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
addSpecPart(element[0], element[1] !== 'desc');
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
else if (typeof spec === 'object') {
|
|
48
|
-
Object.keys(spec).forEach(key => {
|
|
49
|
-
addSpecPart(key, spec[key] >= 0);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
else if (typeof spec === 'function') {
|
|
53
|
-
this._sortFunction = spec;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
throw Error(`Bad sort specification: ${JSON.stringify(spec)}`);
|
|
57
|
-
}
|
|
58
|
-
// If a function is specified for sorting, we skip the rest.
|
|
59
|
-
if (this._sortFunction) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// To implement affectedByModifier, we piggy-back on top of Matcher's
|
|
63
|
-
// affectedByModifier code; we create a selector that is affected by the
|
|
64
|
-
// same modifiers as this sort order. This is only implemented on the
|
|
65
|
-
// server.
|
|
66
|
-
if (this.affectedByModifier) {
|
|
67
|
-
const selector = {};
|
|
68
|
-
this._sortSpecParts.forEach(spec => {
|
|
69
|
-
selector[spec.path] = 1;
|
|
70
|
-
});
|
|
71
|
-
this._selectorForAffectedByModifier = new minimongo_matcher_1.MinimongoMatcher(selector);
|
|
72
|
-
}
|
|
73
|
-
this._keyComparator = composeComparators(this._sortSpecParts.map((spec, i) => this._keyFieldComparator(i)));
|
|
74
|
-
}
|
|
75
|
-
affectedByModifier(modifier) {
|
|
76
|
-
return this._selectorForAffectedByModifier.affectedByModifier(modifier);
|
|
77
|
-
}
|
|
78
|
-
getComparator(options) {
|
|
79
|
-
// If sort is specified or have no distances, just use the comparator from
|
|
80
|
-
// the source specification (which defaults to "everything is equal".
|
|
81
|
-
// issue #3599
|
|
82
|
-
// https://docs.mongodb.com/manual/reference/operator/query/near/#sort-operation
|
|
83
|
-
// sort effectively overrides $near
|
|
84
|
-
if (this._sortSpecParts.length || !options || !options.distances) {
|
|
85
|
-
return this._getBaseComparator();
|
|
86
|
-
}
|
|
87
|
-
const distances = options.distances;
|
|
88
|
-
// Return a comparator which compares using $near distances.
|
|
89
|
-
return (a, b) => {
|
|
90
|
-
if (!distances.has(a._id)) {
|
|
91
|
-
throw Error(`Missing distance for ${a._id}`);
|
|
92
|
-
}
|
|
93
|
-
if (!distances.has(b._id)) {
|
|
94
|
-
throw Error(`Missing distance for ${b._id}`);
|
|
95
|
-
}
|
|
96
|
-
return distances.get(a._id) - distances.get(b._id);
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
// Takes in two keys: arrays whose lengths match the number of spec
|
|
100
|
-
// parts. Returns negative, 0, or positive based on using the sort spec to
|
|
101
|
-
// compare fields.
|
|
102
|
-
_compareKeys(key1, key2) {
|
|
103
|
-
if (key1.length !== this._sortSpecParts.length ||
|
|
104
|
-
key2.length !== this._sortSpecParts.length) {
|
|
105
|
-
throw Error('Key has wrong length');
|
|
106
|
-
}
|
|
107
|
-
return this._keyComparator(key1, key2);
|
|
108
|
-
}
|
|
109
|
-
// Iterates over each possible "key" from doc (ie, over each branch), calling
|
|
110
|
-
// 'cb' with the key.
|
|
111
|
-
_generateKeysFromDoc(doc, cb) {
|
|
112
|
-
if (this._sortSpecParts.length === 0) {
|
|
113
|
-
throw new Error('can\'t generate keys without a spec');
|
|
114
|
-
}
|
|
115
|
-
const pathFromIndices = indices => `${indices.join(',')},`;
|
|
116
|
-
let knownPaths = null;
|
|
117
|
-
// maps index -> ({'' -> value} or {path -> value})
|
|
118
|
-
const valuesByIndexAndPath = this._sortSpecParts.map(spec => {
|
|
119
|
-
// Expand any leaf arrays that we find, and ignore those arrays
|
|
120
|
-
// themselves. (We never sort based on an array itself.)
|
|
121
|
-
let branches = (0, minimongo_common_1.expandArraysInBranches)(spec.lookup(doc), true);
|
|
122
|
-
// If there are no values for a key (eg, key goes to an empty array),
|
|
123
|
-
// pretend we found one undefined value.
|
|
124
|
-
if (!branches.length) {
|
|
125
|
-
branches = [{ value: void 0 }];
|
|
126
|
-
}
|
|
127
|
-
const element = Object.create(null);
|
|
128
|
-
let usedPaths = false;
|
|
129
|
-
branches.forEach(branch => {
|
|
130
|
-
if (!branch.arrayIndices) {
|
|
131
|
-
// If there are no array indices for a branch, then it must be the
|
|
132
|
-
// only branch, because the only thing that produces multiple branches
|
|
133
|
-
// is the use of arrays.
|
|
134
|
-
if (branches.length > 1) {
|
|
135
|
-
throw Error('multiple branches but no array used?');
|
|
136
|
-
}
|
|
137
|
-
element[''] = branch.value;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
usedPaths = true;
|
|
141
|
-
const path = pathFromIndices(branch.arrayIndices);
|
|
142
|
-
if (minimongo_common_1.hasOwn.call(element, path)) {
|
|
143
|
-
throw Error(`duplicate path: ${path}`);
|
|
144
|
-
}
|
|
145
|
-
element[path] = branch.value;
|
|
146
|
-
// If two sort fields both go into arrays, they have to go into the
|
|
147
|
-
// exact same arrays and we have to find the same paths. This is
|
|
148
|
-
// roughly the same condition that makes MongoDB throw this strange
|
|
149
|
-
// error message. eg, the main thing is that if sort spec is {a: 1,
|
|
150
|
-
// b:1} then a and b cannot both be arrays.
|
|
151
|
-
//
|
|
152
|
-
// (In MongoDB it seems to be OK to have {a: 1, 'a.x.y': 1} where 'a'
|
|
153
|
-
// and 'a.x.y' are both arrays, but we don't allow this for now.
|
|
154
|
-
// #NestedArraySort
|
|
155
|
-
// XXX achieve full compatibility here
|
|
156
|
-
if (knownPaths && !minimongo_common_1.hasOwn.call(knownPaths, path)) {
|
|
157
|
-
throw Error('cannot index parallel arrays');
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
if (knownPaths) {
|
|
161
|
-
// Similarly to above, paths must match everywhere, unless this is a
|
|
162
|
-
// non-array field.
|
|
163
|
-
if (!minimongo_common_1.hasOwn.call(element, '') &&
|
|
164
|
-
Object.keys(knownPaths).length !== Object.keys(element).length) {
|
|
165
|
-
throw Error('cannot index parallel arrays!');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else if (usedPaths) {
|
|
169
|
-
knownPaths = {};
|
|
170
|
-
Object.keys(element).forEach(path => {
|
|
171
|
-
knownPaths[path] = true;
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
return element;
|
|
175
|
-
});
|
|
176
|
-
if (!knownPaths) {
|
|
177
|
-
// Easy case: no use of arrays.
|
|
178
|
-
const soleKey = valuesByIndexAndPath.map(values => {
|
|
179
|
-
if (!minimongo_common_1.hasOwn.call(values, '')) {
|
|
180
|
-
throw Error('no value in sole key case?');
|
|
181
|
-
}
|
|
182
|
-
return values[''];
|
|
183
|
-
});
|
|
184
|
-
cb(soleKey);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
Object.keys(knownPaths).forEach(path => {
|
|
188
|
-
const key = valuesByIndexAndPath.map(values => {
|
|
189
|
-
if (minimongo_common_1.hasOwn.call(values, '')) {
|
|
190
|
-
return values[''];
|
|
191
|
-
}
|
|
192
|
-
if (!minimongo_common_1.hasOwn.call(values, path)) {
|
|
193
|
-
throw Error('missing path?');
|
|
194
|
-
}
|
|
195
|
-
return values[path];
|
|
196
|
-
});
|
|
197
|
-
cb(key);
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
// Returns a comparator that represents the sort specification (but not
|
|
201
|
-
// including a possible geoquery distance tie-breaker).
|
|
202
|
-
_getBaseComparator() {
|
|
203
|
-
if (this._sortFunction) {
|
|
204
|
-
return this._sortFunction;
|
|
205
|
-
}
|
|
206
|
-
// If we're only sorting on geoquery distance and no specs, just say
|
|
207
|
-
// everything is equal.
|
|
208
|
-
if (!this._sortSpecParts.length) {
|
|
209
|
-
return (doc1, doc2) => 0;
|
|
210
|
-
}
|
|
211
|
-
return (doc1, doc2) => {
|
|
212
|
-
const key1 = this._getMinKeyFromDoc(doc1);
|
|
213
|
-
const key2 = this._getMinKeyFromDoc(doc2);
|
|
214
|
-
return this._compareKeys(key1, key2);
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
// Finds the minimum key from the doc, according to the sort specs. (We say
|
|
218
|
-
// "minimum" here but this is with respect to the sort spec, so "descending"
|
|
219
|
-
// sort fields mean we're finding the max for that field.)
|
|
220
|
-
//
|
|
221
|
-
// Note that this is NOT "find the minimum value of the first field, the
|
|
222
|
-
// minimum value of the second field, etc"... it's "choose the
|
|
223
|
-
// lexicographically minimum value of the key vector, allowing only keys which
|
|
224
|
-
// you can find along the same paths". ie, for a doc {a: [{x: 0, y: 5}, {x:
|
|
225
|
-
// 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and
|
|
226
|
-
// [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.
|
|
227
|
-
_getMinKeyFromDoc(doc) {
|
|
228
|
-
let minKey = null;
|
|
229
|
-
this._generateKeysFromDoc(doc, key => {
|
|
230
|
-
if (minKey === null) {
|
|
231
|
-
minKey = key;
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (this._compareKeys(key, minKey) < 0) {
|
|
235
|
-
minKey = key;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
return minKey;
|
|
239
|
-
}
|
|
240
|
-
_getPaths() {
|
|
241
|
-
return this._sortSpecParts.map(part => part.path);
|
|
242
|
-
}
|
|
243
|
-
// Given an index 'i', returns a comparator that compares two key arrays based
|
|
244
|
-
// on field 'i'.
|
|
245
|
-
_keyFieldComparator(i) {
|
|
246
|
-
const invert = !this._sortSpecParts[i].ascending;
|
|
247
|
-
return (key1, key2) => {
|
|
248
|
-
const compare = minimongo_common_1._f._cmp(key1[i], key2[i]);
|
|
249
|
-
return invert ? -compare : compare;
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
exports.default = MinimongoSorter;
|
|
254
|
-
// Given an array of comparators
|
|
255
|
-
// (functions (a,b)->(negative or positive or zero)), returns a single
|
|
256
|
-
// comparator which uses each comparator in order and returns the first
|
|
257
|
-
// non-zero value.
|
|
258
|
-
function composeComparators(comparatorArray) {
|
|
259
|
-
return (a, b) => {
|
|
260
|
-
for (let i = 0; i < comparatorArray.length; ++i) {
|
|
261
|
-
const compare = comparatorArray[i](a, b);
|
|
262
|
-
if (compare !== 0) {
|
|
263
|
-
return compare;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return 0;
|
|
267
|
-
};
|
|
268
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const minimongo_common_1 = require("./minimongo_common");
|
|
4
|
+
const minimongo_matcher_1 = require("./minimongo_matcher");
|
|
5
|
+
// Give a sort spec, which can be in any of these forms:
|
|
6
|
+
// {"key1": 1, "key2": -1}
|
|
7
|
+
// [["key1", "asc"], ["key2", "desc"]]
|
|
8
|
+
// ["key1", ["key2", "desc"]]
|
|
9
|
+
//
|
|
10
|
+
// (.. with the first form being dependent on the key enumeration
|
|
11
|
+
// behavior of your javascript VM, which usually does what you mean in
|
|
12
|
+
// this case if the key names don't look like integers ..)
|
|
13
|
+
//
|
|
14
|
+
// return a function that takes two objects, and returns -1 if the
|
|
15
|
+
// first object comes first in order, 1 if the second object comes
|
|
16
|
+
// first, or 0 if neither object comes before the other.
|
|
17
|
+
class MinimongoSorter {
|
|
18
|
+
constructor(spec) {
|
|
19
|
+
this.combineIntoProjection = function (projection) {
|
|
20
|
+
return (0, minimongo_common_1.combineImportantPathsIntoProjection)((0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths()), projection);
|
|
21
|
+
};
|
|
22
|
+
this._sortSpecParts = [];
|
|
23
|
+
this._sortFunction = null;
|
|
24
|
+
const addSpecPart = (path, ascending) => {
|
|
25
|
+
if (!path) {
|
|
26
|
+
throw Error('sort keys must be non-empty');
|
|
27
|
+
}
|
|
28
|
+
if (path.charAt(0) === '$') {
|
|
29
|
+
throw Error(`unsupported sort key: ${path}`);
|
|
30
|
+
}
|
|
31
|
+
this._sortSpecParts.push({
|
|
32
|
+
ascending,
|
|
33
|
+
lookup: (0, minimongo_common_1.makeLookupFunction)(path, { forSort: true }),
|
|
34
|
+
path
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
if (spec instanceof Array) {
|
|
38
|
+
spec.forEach(element => {
|
|
39
|
+
if (typeof element === 'string') {
|
|
40
|
+
addSpecPart(element, true);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
addSpecPart(element[0], element[1] !== 'desc');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (typeof spec === 'object') {
|
|
48
|
+
Object.keys(spec).forEach(key => {
|
|
49
|
+
addSpecPart(key, spec[key] >= 0);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else if (typeof spec === 'function') {
|
|
53
|
+
this._sortFunction = spec;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw Error(`Bad sort specification: ${JSON.stringify(spec)}`);
|
|
57
|
+
}
|
|
58
|
+
// If a function is specified for sorting, we skip the rest.
|
|
59
|
+
if (this._sortFunction) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// To implement affectedByModifier, we piggy-back on top of Matcher's
|
|
63
|
+
// affectedByModifier code; we create a selector that is affected by the
|
|
64
|
+
// same modifiers as this sort order. This is only implemented on the
|
|
65
|
+
// server.
|
|
66
|
+
if (this.affectedByModifier) {
|
|
67
|
+
const selector = {};
|
|
68
|
+
this._sortSpecParts.forEach(spec => {
|
|
69
|
+
selector[spec.path] = 1;
|
|
70
|
+
});
|
|
71
|
+
this._selectorForAffectedByModifier = new minimongo_matcher_1.MinimongoMatcher(selector);
|
|
72
|
+
}
|
|
73
|
+
this._keyComparator = composeComparators(this._sortSpecParts.map((spec, i) => this._keyFieldComparator(i)));
|
|
74
|
+
}
|
|
75
|
+
affectedByModifier(modifier) {
|
|
76
|
+
return this._selectorForAffectedByModifier.affectedByModifier(modifier);
|
|
77
|
+
}
|
|
78
|
+
getComparator(options) {
|
|
79
|
+
// If sort is specified or have no distances, just use the comparator from
|
|
80
|
+
// the source specification (which defaults to "everything is equal".
|
|
81
|
+
// issue #3599
|
|
82
|
+
// https://docs.mongodb.com/manual/reference/operator/query/near/#sort-operation
|
|
83
|
+
// sort effectively overrides $near
|
|
84
|
+
if (this._sortSpecParts.length || !options || !options.distances) {
|
|
85
|
+
return this._getBaseComparator();
|
|
86
|
+
}
|
|
87
|
+
const distances = options.distances;
|
|
88
|
+
// Return a comparator which compares using $near distances.
|
|
89
|
+
return (a, b) => {
|
|
90
|
+
if (!distances.has(a._id)) {
|
|
91
|
+
throw Error(`Missing distance for ${a._id}`);
|
|
92
|
+
}
|
|
93
|
+
if (!distances.has(b._id)) {
|
|
94
|
+
throw Error(`Missing distance for ${b._id}`);
|
|
95
|
+
}
|
|
96
|
+
return distances.get(a._id) - distances.get(b._id);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Takes in two keys: arrays whose lengths match the number of spec
|
|
100
|
+
// parts. Returns negative, 0, or positive based on using the sort spec to
|
|
101
|
+
// compare fields.
|
|
102
|
+
_compareKeys(key1, key2) {
|
|
103
|
+
if (key1.length !== this._sortSpecParts.length ||
|
|
104
|
+
key2.length !== this._sortSpecParts.length) {
|
|
105
|
+
throw Error('Key has wrong length');
|
|
106
|
+
}
|
|
107
|
+
return this._keyComparator(key1, key2);
|
|
108
|
+
}
|
|
109
|
+
// Iterates over each possible "key" from doc (ie, over each branch), calling
|
|
110
|
+
// 'cb' with the key.
|
|
111
|
+
_generateKeysFromDoc(doc, cb) {
|
|
112
|
+
if (this._sortSpecParts.length === 0) {
|
|
113
|
+
throw new Error('can\'t generate keys without a spec');
|
|
114
|
+
}
|
|
115
|
+
const pathFromIndices = indices => `${indices.join(',')},`;
|
|
116
|
+
let knownPaths = null;
|
|
117
|
+
// maps index -> ({'' -> value} or {path -> value})
|
|
118
|
+
const valuesByIndexAndPath = this._sortSpecParts.map(spec => {
|
|
119
|
+
// Expand any leaf arrays that we find, and ignore those arrays
|
|
120
|
+
// themselves. (We never sort based on an array itself.)
|
|
121
|
+
let branches = (0, minimongo_common_1.expandArraysInBranches)(spec.lookup(doc), true);
|
|
122
|
+
// If there are no values for a key (eg, key goes to an empty array),
|
|
123
|
+
// pretend we found one undefined value.
|
|
124
|
+
if (!branches.length) {
|
|
125
|
+
branches = [{ value: void 0 }];
|
|
126
|
+
}
|
|
127
|
+
const element = Object.create(null);
|
|
128
|
+
let usedPaths = false;
|
|
129
|
+
branches.forEach(branch => {
|
|
130
|
+
if (!branch.arrayIndices) {
|
|
131
|
+
// If there are no array indices for a branch, then it must be the
|
|
132
|
+
// only branch, because the only thing that produces multiple branches
|
|
133
|
+
// is the use of arrays.
|
|
134
|
+
if (branches.length > 1) {
|
|
135
|
+
throw Error('multiple branches but no array used?');
|
|
136
|
+
}
|
|
137
|
+
element[''] = branch.value;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
usedPaths = true;
|
|
141
|
+
const path = pathFromIndices(branch.arrayIndices);
|
|
142
|
+
if (minimongo_common_1.hasOwn.call(element, path)) {
|
|
143
|
+
throw Error(`duplicate path: ${path}`);
|
|
144
|
+
}
|
|
145
|
+
element[path] = branch.value;
|
|
146
|
+
// If two sort fields both go into arrays, they have to go into the
|
|
147
|
+
// exact same arrays and we have to find the same paths. This is
|
|
148
|
+
// roughly the same condition that makes MongoDB throw this strange
|
|
149
|
+
// error message. eg, the main thing is that if sort spec is {a: 1,
|
|
150
|
+
// b:1} then a and b cannot both be arrays.
|
|
151
|
+
//
|
|
152
|
+
// (In MongoDB it seems to be OK to have {a: 1, 'a.x.y': 1} where 'a'
|
|
153
|
+
// and 'a.x.y' are both arrays, but we don't allow this for now.
|
|
154
|
+
// #NestedArraySort
|
|
155
|
+
// XXX achieve full compatibility here
|
|
156
|
+
if (knownPaths && !minimongo_common_1.hasOwn.call(knownPaths, path)) {
|
|
157
|
+
throw Error('cannot index parallel arrays');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
if (knownPaths) {
|
|
161
|
+
// Similarly to above, paths must match everywhere, unless this is a
|
|
162
|
+
// non-array field.
|
|
163
|
+
if (!minimongo_common_1.hasOwn.call(element, '') &&
|
|
164
|
+
Object.keys(knownPaths).length !== Object.keys(element).length) {
|
|
165
|
+
throw Error('cannot index parallel arrays!');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (usedPaths) {
|
|
169
|
+
knownPaths = {};
|
|
170
|
+
Object.keys(element).forEach(path => {
|
|
171
|
+
knownPaths[path] = true;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return element;
|
|
175
|
+
});
|
|
176
|
+
if (!knownPaths) {
|
|
177
|
+
// Easy case: no use of arrays.
|
|
178
|
+
const soleKey = valuesByIndexAndPath.map(values => {
|
|
179
|
+
if (!minimongo_common_1.hasOwn.call(values, '')) {
|
|
180
|
+
throw Error('no value in sole key case?');
|
|
181
|
+
}
|
|
182
|
+
return values[''];
|
|
183
|
+
});
|
|
184
|
+
cb(soleKey);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
Object.keys(knownPaths).forEach(path => {
|
|
188
|
+
const key = valuesByIndexAndPath.map(values => {
|
|
189
|
+
if (minimongo_common_1.hasOwn.call(values, '')) {
|
|
190
|
+
return values[''];
|
|
191
|
+
}
|
|
192
|
+
if (!minimongo_common_1.hasOwn.call(values, path)) {
|
|
193
|
+
throw Error('missing path?');
|
|
194
|
+
}
|
|
195
|
+
return values[path];
|
|
196
|
+
});
|
|
197
|
+
cb(key);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// Returns a comparator that represents the sort specification (but not
|
|
201
|
+
// including a possible geoquery distance tie-breaker).
|
|
202
|
+
_getBaseComparator() {
|
|
203
|
+
if (this._sortFunction) {
|
|
204
|
+
return this._sortFunction;
|
|
205
|
+
}
|
|
206
|
+
// If we're only sorting on geoquery distance and no specs, just say
|
|
207
|
+
// everything is equal.
|
|
208
|
+
if (!this._sortSpecParts.length) {
|
|
209
|
+
return (doc1, doc2) => 0;
|
|
210
|
+
}
|
|
211
|
+
return (doc1, doc2) => {
|
|
212
|
+
const key1 = this._getMinKeyFromDoc(doc1);
|
|
213
|
+
const key2 = this._getMinKeyFromDoc(doc2);
|
|
214
|
+
return this._compareKeys(key1, key2);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// Finds the minimum key from the doc, according to the sort specs. (We say
|
|
218
|
+
// "minimum" here but this is with respect to the sort spec, so "descending"
|
|
219
|
+
// sort fields mean we're finding the max for that field.)
|
|
220
|
+
//
|
|
221
|
+
// Note that this is NOT "find the minimum value of the first field, the
|
|
222
|
+
// minimum value of the second field, etc"... it's "choose the
|
|
223
|
+
// lexicographically minimum value of the key vector, allowing only keys which
|
|
224
|
+
// you can find along the same paths". ie, for a doc {a: [{x: 0, y: 5}, {x:
|
|
225
|
+
// 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and
|
|
226
|
+
// [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.
|
|
227
|
+
_getMinKeyFromDoc(doc) {
|
|
228
|
+
let minKey = null;
|
|
229
|
+
this._generateKeysFromDoc(doc, key => {
|
|
230
|
+
if (minKey === null) {
|
|
231
|
+
minKey = key;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (this._compareKeys(key, minKey) < 0) {
|
|
235
|
+
minKey = key;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return minKey;
|
|
239
|
+
}
|
|
240
|
+
_getPaths() {
|
|
241
|
+
return this._sortSpecParts.map(part => part.path);
|
|
242
|
+
}
|
|
243
|
+
// Given an index 'i', returns a comparator that compares two key arrays based
|
|
244
|
+
// on field 'i'.
|
|
245
|
+
_keyFieldComparator(i) {
|
|
246
|
+
const invert = !this._sortSpecParts[i].ascending;
|
|
247
|
+
return (key1, key2) => {
|
|
248
|
+
const compare = minimongo_common_1._f._cmp(key1[i], key2[i]);
|
|
249
|
+
return invert ? -compare : compare;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.default = MinimongoSorter;
|
|
254
|
+
// Given an array of comparators
|
|
255
|
+
// (functions (a,b)->(negative or positive or zero)), returns a single
|
|
256
|
+
// comparator which uses each comparator in order and returns the first
|
|
257
|
+
// non-zero value.
|
|
258
|
+
function composeComparators(comparatorArray) {
|
|
259
|
+
return (a, b) => {
|
|
260
|
+
for (let i = 0; i < comparatorArray.length; ++i) {
|
|
261
|
+
const compare = comparatorArray[i](a, b);
|
|
262
|
+
if (compare !== 0) {
|
|
263
|
+
return compare;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return 0;
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as MongoDB from "mongodb";
|
|
2
|
-
import { CursorDescription } from "./live_cursor";
|
|
3
|
-
export declare function listenAll(cursorDescription: CursorDescription<any>, listenCallback: Function): {
|
|
4
|
-
stop: () => void;
|
|
5
|
-
};
|
|
6
|
-
export declare function forEachTrigger(cursorDescription: CursorDescription<any>, triggerCallback: Function): void;
|
|
7
|
-
export declare function _idsMatchedBySelector(selector: MongoDB.Filter<{
|
|
8
|
-
_id: string;
|
|
9
|
-
}>): any;
|
|
1
|
+
import * as MongoDB from "mongodb";
|
|
2
|
+
import { CursorDescription } from "./live_cursor";
|
|
3
|
+
export declare function listenAll(cursorDescription: CursorDescription<any>, listenCallback: Function): {
|
|
4
|
+
stop: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function forEachTrigger(cursorDescription: CursorDescription<any>, triggerCallback: Function): void;
|
|
7
|
+
export declare function _idsMatchedBySelector(selector: MongoDB.Filter<{
|
|
8
|
+
_id: string;
|
|
9
|
+
}>): any;
|