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,23 +1,23 @@
|
|
|
1
|
-
import { Filter } from 'mongodb';
|
|
2
|
-
export declare class MinimongoMatcher {
|
|
3
|
-
private _paths;
|
|
4
|
-
private _hasGeoQuery;
|
|
5
|
-
private _hasWhere;
|
|
6
|
-
private _isSimple;
|
|
7
|
-
private _matchingDocument;
|
|
8
|
-
private _selector;
|
|
9
|
-
private _docMatcher;
|
|
10
|
-
private _isUpdate;
|
|
11
|
-
constructor(selector: Filter<any>, isUpdate?: boolean);
|
|
12
|
-
documentMatches(doc: any): any;
|
|
13
|
-
hasGeoQuery(): boolean;
|
|
14
|
-
hasWhere(): boolean;
|
|
15
|
-
isSimple(): boolean;
|
|
16
|
-
_compileSelector(selector: Filter<any>): any;
|
|
17
|
-
affectedByModifier(modifier: any): boolean;
|
|
18
|
-
canBecomeTrueByModifier(modifier: any): any;
|
|
19
|
-
matchingDocument(): any;
|
|
20
|
-
combineIntoProjection(projection: any): {};
|
|
21
|
-
_getPaths(): string[];
|
|
22
|
-
_recordPathUsed(path: any): void;
|
|
23
|
-
}
|
|
1
|
+
import { Filter } from 'mongodb';
|
|
2
|
+
export declare class MinimongoMatcher {
|
|
3
|
+
private _paths;
|
|
4
|
+
private _hasGeoQuery;
|
|
5
|
+
private _hasWhere;
|
|
6
|
+
private _isSimple;
|
|
7
|
+
private _matchingDocument;
|
|
8
|
+
private _selector;
|
|
9
|
+
private _docMatcher;
|
|
10
|
+
private _isUpdate;
|
|
11
|
+
constructor(selector: Filter<any>, isUpdate?: boolean);
|
|
12
|
+
documentMatches(doc: any): any;
|
|
13
|
+
hasGeoQuery(): boolean;
|
|
14
|
+
hasWhere(): boolean;
|
|
15
|
+
isSimple(): boolean;
|
|
16
|
+
_compileSelector(selector: Filter<any>): any;
|
|
17
|
+
affectedByModifier(modifier: any): boolean;
|
|
18
|
+
canBecomeTrueByModifier(modifier: any): any;
|
|
19
|
+
matchingDocument(): any;
|
|
20
|
+
combineIntoProjection(projection: any): {};
|
|
21
|
+
_getPaths(): string[];
|
|
22
|
+
_recordPathUsed(path: any): void;
|
|
23
|
+
}
|
|
@@ -1,283 +1,283 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MinimongoMatcher = void 0;
|
|
4
|
-
const minimongo_common_1 = require("./minimongo_common");
|
|
5
|
-
const ejson_1 = require("../ejson/ejson");
|
|
6
|
-
// The minimongo selector compiler!
|
|
7
|
-
// Terminology:
|
|
8
|
-
// - a 'selector' is the EJSON object representing a selector
|
|
9
|
-
// - a 'matcher' is its compiled form (whether a full Minimongo.Matcher
|
|
10
|
-
// object or one of the component lambdas that matches parts of it)
|
|
11
|
-
// - a 'result object' is an object with a 'result' field and maybe
|
|
12
|
-
// distance and arrayIndices.
|
|
13
|
-
// - a 'branched value' is an object with a 'value' field and maybe
|
|
14
|
-
// 'dontIterate' and 'arrayIndices'.
|
|
15
|
-
// - a 'document' is a top-level object that can be stored in a collection.
|
|
16
|
-
// - a 'lookup function' is a function that takes in a document and returns
|
|
17
|
-
// an array of 'branched values'.
|
|
18
|
-
// - a 'branched matcher' maps from an array of branched values to a result
|
|
19
|
-
// object.
|
|
20
|
-
// - an 'element matcher' maps from a single value to a bool.
|
|
21
|
-
// Main entry point.
|
|
22
|
-
// var matcher = new Minimongo.Matcher({a: {$gt: 5}});
|
|
23
|
-
// if (matcher.documentMatches({a: 7})) ...
|
|
24
|
-
class MinimongoMatcher {
|
|
25
|
-
constructor(selector, isUpdate) {
|
|
26
|
-
// A set (object mapping string -> *) of all of the document paths looked
|
|
27
|
-
// at by the selector. Also includes the empty string if it may look at any
|
|
28
|
-
// path (eg, $where).
|
|
29
|
-
this._paths = {};
|
|
30
|
-
// Set to true if compilation finds a $near.
|
|
31
|
-
this._hasGeoQuery = false;
|
|
32
|
-
// Set to true if compilation finds a $where.
|
|
33
|
-
this._hasWhere = false;
|
|
34
|
-
// Set to false if compilation finds anything other than a simple equality
|
|
35
|
-
// or one or more of '$gt', '$gte', '$lt', '$lte', '$ne', '$in', '$nin' used
|
|
36
|
-
// with scalars as operands.
|
|
37
|
-
this._isSimple = true;
|
|
38
|
-
// Set to a dummy document which always matches this Matcher. Or set to null
|
|
39
|
-
// if such document is too hard to find.
|
|
40
|
-
this._matchingDocument = undefined;
|
|
41
|
-
// A clone of the original selector. It may just be a function if the user
|
|
42
|
-
// passed in a function; otherwise is definitely an object (eg, IDs are
|
|
43
|
-
// translated into {_id: ID} first. Used by canBecomeTrueByModifier and
|
|
44
|
-
// Sorter._useWithMatcher.
|
|
45
|
-
this._selector = null;
|
|
46
|
-
this._docMatcher = this._compileSelector(selector);
|
|
47
|
-
// Set to true if selection is done for an update operation
|
|
48
|
-
// Default is false
|
|
49
|
-
// Used for $near array update (issue #3599)
|
|
50
|
-
this._isUpdate = isUpdate;
|
|
51
|
-
}
|
|
52
|
-
documentMatches(doc) {
|
|
53
|
-
if (doc !== Object(doc)) {
|
|
54
|
-
throw Error('documentMatches needs a document');
|
|
55
|
-
}
|
|
56
|
-
return this._docMatcher(doc);
|
|
57
|
-
}
|
|
58
|
-
hasGeoQuery() {
|
|
59
|
-
return this._hasGeoQuery;
|
|
60
|
-
}
|
|
61
|
-
hasWhere() {
|
|
62
|
-
return this._hasWhere;
|
|
63
|
-
}
|
|
64
|
-
isSimple() {
|
|
65
|
-
return this._isSimple;
|
|
66
|
-
}
|
|
67
|
-
// Given a selector, return a function that takes one argument, a
|
|
68
|
-
// document. It returns a result object.
|
|
69
|
-
_compileSelector(selector) {
|
|
70
|
-
// you can pass a literal function instead of a selector
|
|
71
|
-
if (selector instanceof Function) {
|
|
72
|
-
this._isSimple = false;
|
|
73
|
-
this._selector = selector;
|
|
74
|
-
this._recordPathUsed('');
|
|
75
|
-
return doc => ({ result: !!selector.call(doc) });
|
|
76
|
-
}
|
|
77
|
-
// protect against dangerous selectors. falsey and {_id: falsey} are both
|
|
78
|
-
// likely programmer error, and not what you want, particularly for
|
|
79
|
-
// destructive operations.
|
|
80
|
-
if (!selector || minimongo_common_1.hasOwn.call(selector, '_id') && !selector._id) {
|
|
81
|
-
this._isSimple = false;
|
|
82
|
-
return minimongo_common_1.nothingMatcher;
|
|
83
|
-
}
|
|
84
|
-
// Top level can't be an array or true or binary.
|
|
85
|
-
if (Array.isArray(selector) ||
|
|
86
|
-
(0, ejson_1.isBinary)(selector) ||
|
|
87
|
-
typeof selector === 'boolean') {
|
|
88
|
-
throw new Error(`Invalid selector: ${selector}`);
|
|
89
|
-
}
|
|
90
|
-
this._selector = (0, ejson_1.clone)(selector);
|
|
91
|
-
return (0, minimongo_common_1.compileDocumentSelector)(selector, this, { isRoot: true });
|
|
92
|
-
}
|
|
93
|
-
affectedByModifier(modifier) {
|
|
94
|
-
// safe check for $set/$unset being objects
|
|
95
|
-
modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
|
|
96
|
-
const meaningfulPaths = this._getPaths();
|
|
97
|
-
const modifiedPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
|
|
98
|
-
return modifiedPaths.some(path => {
|
|
99
|
-
const mod = path.split('.');
|
|
100
|
-
return meaningfulPaths.some(meaningfulPath => {
|
|
101
|
-
const sel = meaningfulPath.split('.');
|
|
102
|
-
let i = 0, j = 0;
|
|
103
|
-
while (i < sel.length && j < mod.length) {
|
|
104
|
-
if ((0, minimongo_common_1.isNumericKey)(sel[i]) && (0, minimongo_common_1.isNumericKey)(mod[j])) {
|
|
105
|
-
// foo.4.bar selector affected by foo.4 modifier
|
|
106
|
-
// foo.3.bar selector unaffected by foo.4 modifier
|
|
107
|
-
if (sel[i] === mod[j]) {
|
|
108
|
-
i++;
|
|
109
|
-
j++;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if ((0, minimongo_common_1.isNumericKey)(sel[i])) {
|
|
116
|
-
// foo.4.bar selector unaffected by foo.bar modifier
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
else if ((0, minimongo_common_1.isNumericKey)(mod[j])) {
|
|
120
|
-
j++;
|
|
121
|
-
}
|
|
122
|
-
else if (sel[i] === mod[j]) {
|
|
123
|
-
i++;
|
|
124
|
-
j++;
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// One is a prefix of another, taking numeric fields into account
|
|
131
|
-
return true;
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
canBecomeTrueByModifier(modifier) {
|
|
136
|
-
if (!this.affectedByModifier(modifier)) {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
if (!this.isSimple()) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
|
|
143
|
-
const modifierPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
|
|
144
|
-
if (this._getPaths().some(pathHasNumericKeys) ||
|
|
145
|
-
modifierPaths.some(pathHasNumericKeys)) {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
// check if there is a $set or $unset that indicates something is an
|
|
149
|
-
// object rather than a scalar in the actual object where we saw $-operator
|
|
150
|
-
// NOTE: it is correct since we allow only scalars in $-operators
|
|
151
|
-
// Example: for selector {'a.b': {$gt: 5}} the modifier {'a.b.c':7} would
|
|
152
|
-
// definitely set the result to false as 'a.b' appears to be an object.
|
|
153
|
-
const expectedScalarIsObject = Object.keys(this._selector).some(path => {
|
|
154
|
-
if (!(0, minimongo_common_1.isOperatorObject)(this._selector[path])) {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
return modifierPaths.some(modifierPath => modifierPath.startsWith(`${path}.`));
|
|
158
|
-
});
|
|
159
|
-
if (expectedScalarIsObject) {
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
// See if we can apply the modifier on the ideally matching object. If it
|
|
163
|
-
// still matches the selector, then the modifier could have turned the real
|
|
164
|
-
// object in the database into something matching.
|
|
165
|
-
const matchingDocument = (0, ejson_1.clone)(this.matchingDocument());
|
|
166
|
-
// The selector is too complex, anything can happen.
|
|
167
|
-
if (matchingDocument === null) {
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
(0, minimongo_common_1._modify)(matchingDocument, modifier);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
// Couldn't set a property on a field which is a scalar or null in the
|
|
175
|
-
// selector.
|
|
176
|
-
// Example:
|
|
177
|
-
// real document: { 'a.b': 3 }
|
|
178
|
-
// selector: { 'a': 12 }
|
|
179
|
-
// converted selector (ideal document): { 'a': 12 }
|
|
180
|
-
// modifier: { $set: { 'a.b': 4 } }
|
|
181
|
-
// We don't know what real document was like but from the error raised by
|
|
182
|
-
// $set on a scalar field we can reason that the structure of real document
|
|
183
|
-
// is completely different.
|
|
184
|
-
if (error.name === 'MinimongoError' && error.setPropertyError) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
return this.documentMatches(matchingDocument).result;
|
|
190
|
-
}
|
|
191
|
-
matchingDocument() {
|
|
192
|
-
// check if it was computed before
|
|
193
|
-
if (this._matchingDocument !== undefined) {
|
|
194
|
-
return this._matchingDocument;
|
|
195
|
-
}
|
|
196
|
-
// If the analysis of this selector is too hard for our implementation
|
|
197
|
-
// fallback to "YES"
|
|
198
|
-
let fallback = false;
|
|
199
|
-
this._matchingDocument = (0, minimongo_common_1.pathsToTree)(this._getPaths(), path => {
|
|
200
|
-
const valueSelector = this._selector[path];
|
|
201
|
-
if ((0, minimongo_common_1.isOperatorObject)(valueSelector)) {
|
|
202
|
-
// if there is a strict equality, there is a good
|
|
203
|
-
// chance we can use one of those as "matching"
|
|
204
|
-
// dummy value
|
|
205
|
-
if (valueSelector.$eq) {
|
|
206
|
-
return valueSelector.$eq;
|
|
207
|
-
}
|
|
208
|
-
if (valueSelector.$in) {
|
|
209
|
-
const matcher = new MinimongoMatcher({ placeholder: valueSelector });
|
|
210
|
-
// Return anything from $in that matches the whole selector for this
|
|
211
|
-
// path. If nothing matches, returns `undefined` as nothing can make
|
|
212
|
-
// this selector into `true`.
|
|
213
|
-
return valueSelector.$in.find(placeholder => matcher.documentMatches({ placeholder }).result);
|
|
214
|
-
}
|
|
215
|
-
if (onlyContainsKeys(valueSelector, ['$gt', '$gte', '$lt', '$lte'])) {
|
|
216
|
-
let lowerBound = -Infinity;
|
|
217
|
-
let upperBound = Infinity;
|
|
218
|
-
['$lte', '$lt'].forEach(op => {
|
|
219
|
-
if (minimongo_common_1.hasOwn.call(valueSelector, op) &&
|
|
220
|
-
valueSelector[op] < upperBound) {
|
|
221
|
-
upperBound = valueSelector[op];
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
['$gte', '$gt'].forEach(op => {
|
|
225
|
-
if (minimongo_common_1.hasOwn.call(valueSelector, op) &&
|
|
226
|
-
valueSelector[op] > lowerBound) {
|
|
227
|
-
lowerBound = valueSelector[op];
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const middle = (lowerBound + upperBound) / 2;
|
|
231
|
-
const matcher = new MinimongoMatcher({ placeholder: valueSelector });
|
|
232
|
-
if (!matcher.documentMatches({ placeholder: middle }).result &&
|
|
233
|
-
(middle === lowerBound || middle === upperBound)) {
|
|
234
|
-
fallback = true;
|
|
235
|
-
}
|
|
236
|
-
return middle;
|
|
237
|
-
}
|
|
238
|
-
if (onlyContainsKeys(valueSelector, ['$nin', '$ne'])) {
|
|
239
|
-
// Since this._isSimple makes sure $nin and $ne are not combined with
|
|
240
|
-
// objects or arrays, we can confidently return an empty object as it
|
|
241
|
-
// never matches any scalar.
|
|
242
|
-
return {};
|
|
243
|
-
}
|
|
244
|
-
fallback = true;
|
|
245
|
-
}
|
|
246
|
-
return this._selector[path];
|
|
247
|
-
}, x => x);
|
|
248
|
-
if (fallback) {
|
|
249
|
-
this._matchingDocument = null;
|
|
250
|
-
}
|
|
251
|
-
return this._matchingDocument;
|
|
252
|
-
}
|
|
253
|
-
;
|
|
254
|
-
// Knows how to combine a mongo selector and a fields projection to a new fields
|
|
255
|
-
// projection taking into account active fields from the passed selector.
|
|
256
|
-
// @returns Object - projection object (same as fields option of mongo cursor)
|
|
257
|
-
combineIntoProjection(projection) {
|
|
258
|
-
const selectorPaths = (0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths());
|
|
259
|
-
// Special case for $where operator in the selector - projection should depend
|
|
260
|
-
// on all fields of the document. getSelectorPaths returns a list of paths
|
|
261
|
-
// selector depends on. If one of the paths is '' (empty string) representing
|
|
262
|
-
// the root or the whole document, complete projection should be returned.
|
|
263
|
-
if (selectorPaths.includes('')) {
|
|
264
|
-
return {};
|
|
265
|
-
}
|
|
266
|
-
return (0, minimongo_common_1.combineImportantPathsIntoProjection)(selectorPaths, projection);
|
|
267
|
-
}
|
|
268
|
-
// Returns a list of key paths the given selector is looking for. It includes
|
|
269
|
-
// the empty string if there is a $where.
|
|
270
|
-
_getPaths() {
|
|
271
|
-
return Object.keys(this._paths);
|
|
272
|
-
}
|
|
273
|
-
_recordPathUsed(path) {
|
|
274
|
-
this._paths[path] = true;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
exports.MinimongoMatcher = MinimongoMatcher;
|
|
278
|
-
function pathHasNumericKeys(path) {
|
|
279
|
-
return path.split('.').some(minimongo_common_1.isNumericKey);
|
|
280
|
-
}
|
|
281
|
-
function onlyContainsKeys(obj, keys) {
|
|
282
|
-
return Object.keys(obj).every(k => keys.includes(k));
|
|
283
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MinimongoMatcher = void 0;
|
|
4
|
+
const minimongo_common_1 = require("./minimongo_common");
|
|
5
|
+
const ejson_1 = require("../ejson/ejson");
|
|
6
|
+
// The minimongo selector compiler!
|
|
7
|
+
// Terminology:
|
|
8
|
+
// - a 'selector' is the EJSON object representing a selector
|
|
9
|
+
// - a 'matcher' is its compiled form (whether a full Minimongo.Matcher
|
|
10
|
+
// object or one of the component lambdas that matches parts of it)
|
|
11
|
+
// - a 'result object' is an object with a 'result' field and maybe
|
|
12
|
+
// distance and arrayIndices.
|
|
13
|
+
// - a 'branched value' is an object with a 'value' field and maybe
|
|
14
|
+
// 'dontIterate' and 'arrayIndices'.
|
|
15
|
+
// - a 'document' is a top-level object that can be stored in a collection.
|
|
16
|
+
// - a 'lookup function' is a function that takes in a document and returns
|
|
17
|
+
// an array of 'branched values'.
|
|
18
|
+
// - a 'branched matcher' maps from an array of branched values to a result
|
|
19
|
+
// object.
|
|
20
|
+
// - an 'element matcher' maps from a single value to a bool.
|
|
21
|
+
// Main entry point.
|
|
22
|
+
// var matcher = new Minimongo.Matcher({a: {$gt: 5}});
|
|
23
|
+
// if (matcher.documentMatches({a: 7})) ...
|
|
24
|
+
class MinimongoMatcher {
|
|
25
|
+
constructor(selector, isUpdate) {
|
|
26
|
+
// A set (object mapping string -> *) of all of the document paths looked
|
|
27
|
+
// at by the selector. Also includes the empty string if it may look at any
|
|
28
|
+
// path (eg, $where).
|
|
29
|
+
this._paths = {};
|
|
30
|
+
// Set to true if compilation finds a $near.
|
|
31
|
+
this._hasGeoQuery = false;
|
|
32
|
+
// Set to true if compilation finds a $where.
|
|
33
|
+
this._hasWhere = false;
|
|
34
|
+
// Set to false if compilation finds anything other than a simple equality
|
|
35
|
+
// or one or more of '$gt', '$gte', '$lt', '$lte', '$ne', '$in', '$nin' used
|
|
36
|
+
// with scalars as operands.
|
|
37
|
+
this._isSimple = true;
|
|
38
|
+
// Set to a dummy document which always matches this Matcher. Or set to null
|
|
39
|
+
// if such document is too hard to find.
|
|
40
|
+
this._matchingDocument = undefined;
|
|
41
|
+
// A clone of the original selector. It may just be a function if the user
|
|
42
|
+
// passed in a function; otherwise is definitely an object (eg, IDs are
|
|
43
|
+
// translated into {_id: ID} first. Used by canBecomeTrueByModifier and
|
|
44
|
+
// Sorter._useWithMatcher.
|
|
45
|
+
this._selector = null;
|
|
46
|
+
this._docMatcher = this._compileSelector(selector);
|
|
47
|
+
// Set to true if selection is done for an update operation
|
|
48
|
+
// Default is false
|
|
49
|
+
// Used for $near array update (issue #3599)
|
|
50
|
+
this._isUpdate = isUpdate;
|
|
51
|
+
}
|
|
52
|
+
documentMatches(doc) {
|
|
53
|
+
if (doc !== Object(doc)) {
|
|
54
|
+
throw Error('documentMatches needs a document');
|
|
55
|
+
}
|
|
56
|
+
return this._docMatcher(doc);
|
|
57
|
+
}
|
|
58
|
+
hasGeoQuery() {
|
|
59
|
+
return this._hasGeoQuery;
|
|
60
|
+
}
|
|
61
|
+
hasWhere() {
|
|
62
|
+
return this._hasWhere;
|
|
63
|
+
}
|
|
64
|
+
isSimple() {
|
|
65
|
+
return this._isSimple;
|
|
66
|
+
}
|
|
67
|
+
// Given a selector, return a function that takes one argument, a
|
|
68
|
+
// document. It returns a result object.
|
|
69
|
+
_compileSelector(selector) {
|
|
70
|
+
// you can pass a literal function instead of a selector
|
|
71
|
+
if (selector instanceof Function) {
|
|
72
|
+
this._isSimple = false;
|
|
73
|
+
this._selector = selector;
|
|
74
|
+
this._recordPathUsed('');
|
|
75
|
+
return doc => ({ result: !!selector.call(doc) });
|
|
76
|
+
}
|
|
77
|
+
// protect against dangerous selectors. falsey and {_id: falsey} are both
|
|
78
|
+
// likely programmer error, and not what you want, particularly for
|
|
79
|
+
// destructive operations.
|
|
80
|
+
if (!selector || minimongo_common_1.hasOwn.call(selector, '_id') && !selector._id) {
|
|
81
|
+
this._isSimple = false;
|
|
82
|
+
return minimongo_common_1.nothingMatcher;
|
|
83
|
+
}
|
|
84
|
+
// Top level can't be an array or true or binary.
|
|
85
|
+
if (Array.isArray(selector) ||
|
|
86
|
+
(0, ejson_1.isBinary)(selector) ||
|
|
87
|
+
typeof selector === 'boolean') {
|
|
88
|
+
throw new Error(`Invalid selector: ${selector}`);
|
|
89
|
+
}
|
|
90
|
+
this._selector = (0, ejson_1.clone)(selector);
|
|
91
|
+
return (0, minimongo_common_1.compileDocumentSelector)(selector, this, { isRoot: true });
|
|
92
|
+
}
|
|
93
|
+
affectedByModifier(modifier) {
|
|
94
|
+
// safe check for $set/$unset being objects
|
|
95
|
+
modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
|
|
96
|
+
const meaningfulPaths = this._getPaths();
|
|
97
|
+
const modifiedPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
|
|
98
|
+
return modifiedPaths.some(path => {
|
|
99
|
+
const mod = path.split('.');
|
|
100
|
+
return meaningfulPaths.some(meaningfulPath => {
|
|
101
|
+
const sel = meaningfulPath.split('.');
|
|
102
|
+
let i = 0, j = 0;
|
|
103
|
+
while (i < sel.length && j < mod.length) {
|
|
104
|
+
if ((0, minimongo_common_1.isNumericKey)(sel[i]) && (0, minimongo_common_1.isNumericKey)(mod[j])) {
|
|
105
|
+
// foo.4.bar selector affected by foo.4 modifier
|
|
106
|
+
// foo.3.bar selector unaffected by foo.4 modifier
|
|
107
|
+
if (sel[i] === mod[j]) {
|
|
108
|
+
i++;
|
|
109
|
+
j++;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if ((0, minimongo_common_1.isNumericKey)(sel[i])) {
|
|
116
|
+
// foo.4.bar selector unaffected by foo.bar modifier
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
else if ((0, minimongo_common_1.isNumericKey)(mod[j])) {
|
|
120
|
+
j++;
|
|
121
|
+
}
|
|
122
|
+
else if (sel[i] === mod[j]) {
|
|
123
|
+
i++;
|
|
124
|
+
j++;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// One is a prefix of another, taking numeric fields into account
|
|
131
|
+
return true;
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
canBecomeTrueByModifier(modifier) {
|
|
136
|
+
if (!this.affectedByModifier(modifier)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
if (!this.isSimple()) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
|
|
143
|
+
const modifierPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
|
|
144
|
+
if (this._getPaths().some(pathHasNumericKeys) ||
|
|
145
|
+
modifierPaths.some(pathHasNumericKeys)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// check if there is a $set or $unset that indicates something is an
|
|
149
|
+
// object rather than a scalar in the actual object where we saw $-operator
|
|
150
|
+
// NOTE: it is correct since we allow only scalars in $-operators
|
|
151
|
+
// Example: for selector {'a.b': {$gt: 5}} the modifier {'a.b.c':7} would
|
|
152
|
+
// definitely set the result to false as 'a.b' appears to be an object.
|
|
153
|
+
const expectedScalarIsObject = Object.keys(this._selector).some(path => {
|
|
154
|
+
if (!(0, minimongo_common_1.isOperatorObject)(this._selector[path])) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return modifierPaths.some(modifierPath => modifierPath.startsWith(`${path}.`));
|
|
158
|
+
});
|
|
159
|
+
if (expectedScalarIsObject) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
// See if we can apply the modifier on the ideally matching object. If it
|
|
163
|
+
// still matches the selector, then the modifier could have turned the real
|
|
164
|
+
// object in the database into something matching.
|
|
165
|
+
const matchingDocument = (0, ejson_1.clone)(this.matchingDocument());
|
|
166
|
+
// The selector is too complex, anything can happen.
|
|
167
|
+
if (matchingDocument === null) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
(0, minimongo_common_1._modify)(matchingDocument, modifier);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Couldn't set a property on a field which is a scalar or null in the
|
|
175
|
+
// selector.
|
|
176
|
+
// Example:
|
|
177
|
+
// real document: { 'a.b': 3 }
|
|
178
|
+
// selector: { 'a': 12 }
|
|
179
|
+
// converted selector (ideal document): { 'a': 12 }
|
|
180
|
+
// modifier: { $set: { 'a.b': 4 } }
|
|
181
|
+
// We don't know what real document was like but from the error raised by
|
|
182
|
+
// $set on a scalar field we can reason that the structure of real document
|
|
183
|
+
// is completely different.
|
|
184
|
+
if (error.name === 'MinimongoError' && error.setPropertyError) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
return this.documentMatches(matchingDocument).result;
|
|
190
|
+
}
|
|
191
|
+
matchingDocument() {
|
|
192
|
+
// check if it was computed before
|
|
193
|
+
if (this._matchingDocument !== undefined) {
|
|
194
|
+
return this._matchingDocument;
|
|
195
|
+
}
|
|
196
|
+
// If the analysis of this selector is too hard for our implementation
|
|
197
|
+
// fallback to "YES"
|
|
198
|
+
let fallback = false;
|
|
199
|
+
this._matchingDocument = (0, minimongo_common_1.pathsToTree)(this._getPaths(), path => {
|
|
200
|
+
const valueSelector = this._selector[path];
|
|
201
|
+
if ((0, minimongo_common_1.isOperatorObject)(valueSelector)) {
|
|
202
|
+
// if there is a strict equality, there is a good
|
|
203
|
+
// chance we can use one of those as "matching"
|
|
204
|
+
// dummy value
|
|
205
|
+
if (valueSelector.$eq) {
|
|
206
|
+
return valueSelector.$eq;
|
|
207
|
+
}
|
|
208
|
+
if (valueSelector.$in) {
|
|
209
|
+
const matcher = new MinimongoMatcher({ placeholder: valueSelector });
|
|
210
|
+
// Return anything from $in that matches the whole selector for this
|
|
211
|
+
// path. If nothing matches, returns `undefined` as nothing can make
|
|
212
|
+
// this selector into `true`.
|
|
213
|
+
return valueSelector.$in.find(placeholder => matcher.documentMatches({ placeholder }).result);
|
|
214
|
+
}
|
|
215
|
+
if (onlyContainsKeys(valueSelector, ['$gt', '$gte', '$lt', '$lte'])) {
|
|
216
|
+
let lowerBound = -Infinity;
|
|
217
|
+
let upperBound = Infinity;
|
|
218
|
+
['$lte', '$lt'].forEach(op => {
|
|
219
|
+
if (minimongo_common_1.hasOwn.call(valueSelector, op) &&
|
|
220
|
+
valueSelector[op] < upperBound) {
|
|
221
|
+
upperBound = valueSelector[op];
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
['$gte', '$gt'].forEach(op => {
|
|
225
|
+
if (minimongo_common_1.hasOwn.call(valueSelector, op) &&
|
|
226
|
+
valueSelector[op] > lowerBound) {
|
|
227
|
+
lowerBound = valueSelector[op];
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
const middle = (lowerBound + upperBound) / 2;
|
|
231
|
+
const matcher = new MinimongoMatcher({ placeholder: valueSelector });
|
|
232
|
+
if (!matcher.documentMatches({ placeholder: middle }).result &&
|
|
233
|
+
(middle === lowerBound || middle === upperBound)) {
|
|
234
|
+
fallback = true;
|
|
235
|
+
}
|
|
236
|
+
return middle;
|
|
237
|
+
}
|
|
238
|
+
if (onlyContainsKeys(valueSelector, ['$nin', '$ne'])) {
|
|
239
|
+
// Since this._isSimple makes sure $nin and $ne are not combined with
|
|
240
|
+
// objects or arrays, we can confidently return an empty object as it
|
|
241
|
+
// never matches any scalar.
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
fallback = true;
|
|
245
|
+
}
|
|
246
|
+
return this._selector[path];
|
|
247
|
+
}, x => x);
|
|
248
|
+
if (fallback) {
|
|
249
|
+
this._matchingDocument = null;
|
|
250
|
+
}
|
|
251
|
+
return this._matchingDocument;
|
|
252
|
+
}
|
|
253
|
+
;
|
|
254
|
+
// Knows how to combine a mongo selector and a fields projection to a new fields
|
|
255
|
+
// projection taking into account active fields from the passed selector.
|
|
256
|
+
// @returns Object - projection object (same as fields option of mongo cursor)
|
|
257
|
+
combineIntoProjection(projection) {
|
|
258
|
+
const selectorPaths = (0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths());
|
|
259
|
+
// Special case for $where operator in the selector - projection should depend
|
|
260
|
+
// on all fields of the document. getSelectorPaths returns a list of paths
|
|
261
|
+
// selector depends on. If one of the paths is '' (empty string) representing
|
|
262
|
+
// the root or the whole document, complete projection should be returned.
|
|
263
|
+
if (selectorPaths.includes('')) {
|
|
264
|
+
return {};
|
|
265
|
+
}
|
|
266
|
+
return (0, minimongo_common_1.combineImportantPathsIntoProjection)(selectorPaths, projection);
|
|
267
|
+
}
|
|
268
|
+
// Returns a list of key paths the given selector is looking for. It includes
|
|
269
|
+
// the empty string if there is a $where.
|
|
270
|
+
_getPaths() {
|
|
271
|
+
return Object.keys(this._paths);
|
|
272
|
+
}
|
|
273
|
+
_recordPathUsed(path) {
|
|
274
|
+
this._paths[path] = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.MinimongoMatcher = MinimongoMatcher;
|
|
278
|
+
function pathHasNumericKeys(path) {
|
|
279
|
+
return path.split('.').some(minimongo_common_1.isNumericKey);
|
|
280
|
+
}
|
|
281
|
+
function onlyContainsKeys(obj, keys) {
|
|
282
|
+
return Object.keys(obj).every(k => keys.includes(k));
|
|
283
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export default class MinimongoSorter {
|
|
2
|
-
private _sortSpecParts;
|
|
3
|
-
private _sortFunction;
|
|
4
|
-
private _keyComparator;
|
|
5
|
-
private _selectorForAffectedByModifier;
|
|
6
|
-
constructor(spec: any);
|
|
7
|
-
affectedByModifier(modifier: any): boolean;
|
|
8
|
-
getComparator(options?: any): (a: any, b: any) => number;
|
|
9
|
-
_compareKeys(key1: any, key2: any): number;
|
|
10
|
-
_generateKeysFromDoc(doc: any, cb: any): void;
|
|
11
|
-
_getBaseComparator(): (a: any, b: any) => number;
|
|
12
|
-
_getMinKeyFromDoc(doc: any): any;
|
|
13
|
-
_getPaths(): string[];
|
|
14
|
-
_keyFieldComparator(i: number): (key1: any, key2: any) => any;
|
|
15
|
-
combineIntoProjection: (projection: any) => {};
|
|
16
|
-
}
|
|
1
|
+
export default class MinimongoSorter {
|
|
2
|
+
private _sortSpecParts;
|
|
3
|
+
private _sortFunction;
|
|
4
|
+
private _keyComparator;
|
|
5
|
+
private _selectorForAffectedByModifier;
|
|
6
|
+
constructor(spec: any);
|
|
7
|
+
affectedByModifier(modifier: any): boolean;
|
|
8
|
+
getComparator(options?: any): (a: any, b: any) => number;
|
|
9
|
+
_compareKeys(key1: any, key2: any): number;
|
|
10
|
+
_generateKeysFromDoc(doc: any, cb: any): void;
|
|
11
|
+
_getBaseComparator(): (a: any, b: any) => number | null;
|
|
12
|
+
_getMinKeyFromDoc(doc: any): any;
|
|
13
|
+
_getPaths(): string[];
|
|
14
|
+
_keyFieldComparator(i: number): (key1: any, key2: any) => any;
|
|
15
|
+
combineIntoProjection: (projection: any) => {};
|
|
16
|
+
}
|