mongodb-livedata-server 0.1.2 → 0.1.4
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/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 -74
- package/dist/meteor/ddp/session.js +590 -589
- 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 -2002
- 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 +6 -5
|
@@ -1,257 +1,257 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DiffSequence = void 0;
|
|
4
|
-
const ejson_1 = require("../ejson/ejson");
|
|
5
|
-
exports.DiffSequence = {};
|
|
6
|
-
const hasOwn = Object.prototype.hasOwnProperty;
|
|
7
|
-
function isObjEmpty(obj) {
|
|
8
|
-
for (let key in Object(obj)) {
|
|
9
|
-
if (hasOwn.call(obj, key)) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
// ordered: bool.
|
|
16
|
-
// old_results and new_results: collections of documents.
|
|
17
|
-
// if ordered, they are arrays.
|
|
18
|
-
// if unordered, they are IdMaps
|
|
19
|
-
exports.DiffSequence.diffQueryChanges = function (ordered, oldResults, newResults, observer, options) {
|
|
20
|
-
if (ordered)
|
|
21
|
-
exports.DiffSequence.diffQueryOrderedChanges(oldResults, newResults, observer, options);
|
|
22
|
-
else
|
|
23
|
-
exports.DiffSequence.diffQueryUnorderedChanges(oldResults, newResults, observer, options);
|
|
24
|
-
};
|
|
25
|
-
exports.DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, observer, options) {
|
|
26
|
-
options = options || {};
|
|
27
|
-
var projectionFn = options.projectionFn || ejson_1.clone;
|
|
28
|
-
if (observer.movedBefore) {
|
|
29
|
-
throw new Error("_diffQueryUnordered called with a movedBefore observer!");
|
|
30
|
-
}
|
|
31
|
-
newResults.forEach(function (newDoc, id) {
|
|
32
|
-
var oldDoc = oldResults.get(id);
|
|
33
|
-
if (oldDoc) {
|
|
34
|
-
if (observer.changed && !(0, ejson_1.equals)(oldDoc, newDoc)) {
|
|
35
|
-
var projectedNew = projectionFn(newDoc);
|
|
36
|
-
var projectedOld = projectionFn(oldDoc);
|
|
37
|
-
var changedFields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
38
|
-
if (!isObjEmpty(changedFields)) {
|
|
39
|
-
observer.changed(id, changedFields);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
else if (observer.added) {
|
|
44
|
-
var fields = projectionFn(newDoc);
|
|
45
|
-
delete fields._id;
|
|
46
|
-
observer.added(newDoc._id, fields);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
if (observer.removed) {
|
|
50
|
-
oldResults.forEach(function (oldDoc, id) {
|
|
51
|
-
if (!newResults.has(id))
|
|
52
|
-
observer.removed(id);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
exports.DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, observer, options) {
|
|
57
|
-
options = options || {};
|
|
58
|
-
var projectionFn = options.projectionFn || ejson_1.clone;
|
|
59
|
-
var new_presence_of_id = {};
|
|
60
|
-
new_results.forEach(function (doc) {
|
|
61
|
-
if (new_presence_of_id[doc._id])
|
|
62
|
-
console.log("Duplicate _id in new_results");
|
|
63
|
-
new_presence_of_id[doc._id] = true;
|
|
64
|
-
});
|
|
65
|
-
var old_index_of_id = {};
|
|
66
|
-
old_results.forEach(function (doc, i) {
|
|
67
|
-
if (doc._id in old_index_of_id)
|
|
68
|
-
console.log("Duplicate _id in old_results");
|
|
69
|
-
old_index_of_id[doc._id] = i;
|
|
70
|
-
});
|
|
71
|
-
// ALGORITHM:
|
|
72
|
-
//
|
|
73
|
-
// To determine which docs should be considered "moved" (and which
|
|
74
|
-
// merely change position because of other docs moving) we run
|
|
75
|
-
// a "longest common subsequence" (LCS) algorithm. The LCS of the
|
|
76
|
-
// old doc IDs and the new doc IDs gives the docs that should NOT be
|
|
77
|
-
// considered moved.
|
|
78
|
-
// To actually call the appropriate callbacks to get from the old state to the
|
|
79
|
-
// new state:
|
|
80
|
-
// First, we call removed() on all the items that only appear in the old
|
|
81
|
-
// state.
|
|
82
|
-
// Then, once we have the items that should not move, we walk through the new
|
|
83
|
-
// results array group-by-group, where a "group" is a set of items that have
|
|
84
|
-
// moved, anchored on the end by an item that should not move. One by one, we
|
|
85
|
-
// move each of those elements into place "before" the anchoring end-of-group
|
|
86
|
-
// item, and fire changed events on them if necessary. Then we fire a changed
|
|
87
|
-
// event on the anchor, and move on to the next group. There is always at
|
|
88
|
-
// least one group; the last group is anchored by a virtual "null" id at the
|
|
89
|
-
// end.
|
|
90
|
-
// Asymptotically: O(N k) where k is number of ops, or potentially
|
|
91
|
-
// O(N log N) if inner loop of LCS were made to be binary search.
|
|
92
|
-
//////// LCS (longest common sequence, with respect to _id)
|
|
93
|
-
// (see Wikipedia article on Longest Increasing Subsequence,
|
|
94
|
-
// where the LIS is taken of the sequence of old indices of the
|
|
95
|
-
// docs in new_results)
|
|
96
|
-
//
|
|
97
|
-
// unmoved: the output of the algorithm; members of the LCS,
|
|
98
|
-
// in the form of indices into new_results
|
|
99
|
-
var unmoved = [];
|
|
100
|
-
// max_seq_len: length of LCS found so far
|
|
101
|
-
var max_seq_len = 0;
|
|
102
|
-
// seq_ends[i]: the index into new_results of the last doc in a
|
|
103
|
-
// common subsequence of length of i+1 <= max_seq_len
|
|
104
|
-
var N = new_results.length;
|
|
105
|
-
var seq_ends = new Array(N);
|
|
106
|
-
// ptrs: the common subsequence ending with new_results[n] extends
|
|
107
|
-
// a common subsequence ending with new_results[ptr[n]], unless
|
|
108
|
-
// ptr[n] is -1.
|
|
109
|
-
var ptrs = new Array(N);
|
|
110
|
-
// virtual sequence of old indices of new results
|
|
111
|
-
var old_idx_seq = function (i_new) {
|
|
112
|
-
return old_index_of_id[new_results[i_new]._id];
|
|
113
|
-
};
|
|
114
|
-
// for each item in new_results, use it to extend a common subsequence
|
|
115
|
-
// of length j <= max_seq_len
|
|
116
|
-
for (var i = 0; i < N; i++) {
|
|
117
|
-
if (old_index_of_id[new_results[i]._id] !== undefined) {
|
|
118
|
-
var j = max_seq_len;
|
|
119
|
-
// this inner loop would traditionally be a binary search,
|
|
120
|
-
// but scanning backwards we will likely find a subseq to extend
|
|
121
|
-
// pretty soon, bounded for example by the total number of ops.
|
|
122
|
-
// If this were to be changed to a binary search, we'd still want
|
|
123
|
-
// to scan backwards a bit as an optimization.
|
|
124
|
-
while (j > 0) {
|
|
125
|
-
if (old_idx_seq(seq_ends[j - 1]) < old_idx_seq(i))
|
|
126
|
-
break;
|
|
127
|
-
j--;
|
|
128
|
-
}
|
|
129
|
-
ptrs[i] = (j === 0 ? -1 : seq_ends[j - 1]);
|
|
130
|
-
seq_ends[j] = i;
|
|
131
|
-
if (j + 1 > max_seq_len)
|
|
132
|
-
max_seq_len = j + 1;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// pull out the LCS/LIS into unmoved
|
|
136
|
-
var idx = (max_seq_len === 0 ? -1 : seq_ends[max_seq_len - 1]);
|
|
137
|
-
while (idx >= 0) {
|
|
138
|
-
unmoved.push(idx);
|
|
139
|
-
idx = ptrs[idx];
|
|
140
|
-
}
|
|
141
|
-
// the unmoved item list is built backwards, so fix that
|
|
142
|
-
unmoved.reverse();
|
|
143
|
-
// the last group is always anchored by the end of the result list, which is
|
|
144
|
-
// an id of "null"
|
|
145
|
-
unmoved.push(new_results.length);
|
|
146
|
-
old_results.forEach(function (doc) {
|
|
147
|
-
if (!new_presence_of_id[doc._id])
|
|
148
|
-
observer.removed && observer.removed(doc._id);
|
|
149
|
-
});
|
|
150
|
-
// for each group of things in the new_results that is anchored by an unmoved
|
|
151
|
-
// element, iterate through the things before it.
|
|
152
|
-
var startOfGroup = 0;
|
|
153
|
-
unmoved.forEach(function (endOfGroup) {
|
|
154
|
-
var groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null;
|
|
155
|
-
var oldDoc, newDoc, fields, projectedNew, projectedOld;
|
|
156
|
-
for (var i = startOfGroup; i < endOfGroup; i++) {
|
|
157
|
-
newDoc = new_results[i];
|
|
158
|
-
if (!hasOwn.call(old_index_of_id, newDoc._id)) {
|
|
159
|
-
fields = projectionFn(newDoc);
|
|
160
|
-
delete fields._id;
|
|
161
|
-
observer.addedBefore && observer.addedBefore(newDoc._id, fields, groupId);
|
|
162
|
-
observer.added && observer.added(newDoc._id, fields);
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
// moved
|
|
166
|
-
oldDoc = old_results[old_index_of_id[newDoc._id]];
|
|
167
|
-
projectedNew = projectionFn(newDoc);
|
|
168
|
-
projectedOld = projectionFn(oldDoc);
|
|
169
|
-
fields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
170
|
-
if (!isObjEmpty(fields)) {
|
|
171
|
-
observer.changed && observer.changed(newDoc._id, fields);
|
|
172
|
-
}
|
|
173
|
-
observer.movedBefore && observer.movedBefore(newDoc._id, groupId);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (groupId) {
|
|
177
|
-
newDoc = new_results[endOfGroup];
|
|
178
|
-
oldDoc = old_results[old_index_of_id[newDoc._id]];
|
|
179
|
-
projectedNew = projectionFn(newDoc);
|
|
180
|
-
projectedOld = projectionFn(oldDoc);
|
|
181
|
-
fields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
182
|
-
if (!isObjEmpty(fields)) {
|
|
183
|
-
observer.changed && observer.changed(newDoc._id, fields);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
startOfGroup = endOfGroup + 1;
|
|
187
|
-
});
|
|
188
|
-
};
|
|
189
|
-
// General helper for diff-ing two objects.
|
|
190
|
-
// callbacks is an object like so:
|
|
191
|
-
// { leftOnly: function (key, leftValue) {...},
|
|
192
|
-
// rightOnly: function (key, rightValue) {...},
|
|
193
|
-
// both: function (key, leftValue, rightValue) {...},
|
|
194
|
-
// }
|
|
195
|
-
exports.DiffSequence.diffObjects = function (left, right, callbacks) {
|
|
196
|
-
Object.keys(left).forEach(key => {
|
|
197
|
-
const leftValue = left[key];
|
|
198
|
-
if (hasOwn.call(right, key)) {
|
|
199
|
-
callbacks.both && callbacks.both(key, leftValue, right[key]);
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
callbacks.leftOnly && callbacks.leftOnly(key, leftValue);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
if (callbacks.rightOnly) {
|
|
206
|
-
Object.keys(right).forEach(key => {
|
|
207
|
-
const rightValue = right[key];
|
|
208
|
-
if (!hasOwn.call(left, key)) {
|
|
209
|
-
callbacks.rightOnly(key, rightValue);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
exports.DiffSequence.diffMaps = function (left, right, callbacks) {
|
|
215
|
-
left.forEach(function (leftValue, key) {
|
|
216
|
-
if (right.has(key)) {
|
|
217
|
-
callbacks.both && callbacks.both(key, leftValue, right.get(key));
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
callbacks.leftOnly && callbacks.leftOnly(key, leftValue);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
if (callbacks.rightOnly) {
|
|
224
|
-
right.forEach(function (rightValue, key) {
|
|
225
|
-
if (!left.has(key)) {
|
|
226
|
-
callbacks.rightOnly(key, rightValue);
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
exports.DiffSequence.makeChangedFields = function (newDoc, oldDoc) {
|
|
232
|
-
var fields = {};
|
|
233
|
-
exports.DiffSequence.diffObjects(oldDoc, newDoc, {
|
|
234
|
-
leftOnly: function (key, value) {
|
|
235
|
-
fields[key] = undefined;
|
|
236
|
-
},
|
|
237
|
-
rightOnly: function (key, value) {
|
|
238
|
-
fields[key] = value;
|
|
239
|
-
},
|
|
240
|
-
both: function (key, leftValue, rightValue) {
|
|
241
|
-
if (!(0, ejson_1.equals)(leftValue, rightValue))
|
|
242
|
-
fields[key] = rightValue;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
return fields;
|
|
246
|
-
};
|
|
247
|
-
exports.DiffSequence.applyChanges = function (doc, changeFields) {
|
|
248
|
-
Object.keys(changeFields).forEach(key => {
|
|
249
|
-
const value = changeFields[key];
|
|
250
|
-
if (typeof value === "undefined") {
|
|
251
|
-
delete doc[key];
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
doc[key] = value;
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DiffSequence = void 0;
|
|
4
|
+
const ejson_1 = require("../ejson/ejson");
|
|
5
|
+
exports.DiffSequence = {};
|
|
6
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
7
|
+
function isObjEmpty(obj) {
|
|
8
|
+
for (let key in Object(obj)) {
|
|
9
|
+
if (hasOwn.call(obj, key)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
// ordered: bool.
|
|
16
|
+
// old_results and new_results: collections of documents.
|
|
17
|
+
// if ordered, they are arrays.
|
|
18
|
+
// if unordered, they are IdMaps
|
|
19
|
+
exports.DiffSequence.diffQueryChanges = function (ordered, oldResults, newResults, observer, options) {
|
|
20
|
+
if (ordered)
|
|
21
|
+
exports.DiffSequence.diffQueryOrderedChanges(oldResults, newResults, observer, options);
|
|
22
|
+
else
|
|
23
|
+
exports.DiffSequence.diffQueryUnorderedChanges(oldResults, newResults, observer, options);
|
|
24
|
+
};
|
|
25
|
+
exports.DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, observer, options) {
|
|
26
|
+
options = options || {};
|
|
27
|
+
var projectionFn = options.projectionFn || ejson_1.clone;
|
|
28
|
+
if (observer.movedBefore) {
|
|
29
|
+
throw new Error("_diffQueryUnordered called with a movedBefore observer!");
|
|
30
|
+
}
|
|
31
|
+
newResults.forEach(function (newDoc, id) {
|
|
32
|
+
var oldDoc = oldResults.get(id);
|
|
33
|
+
if (oldDoc) {
|
|
34
|
+
if (observer.changed && !(0, ejson_1.equals)(oldDoc, newDoc)) {
|
|
35
|
+
var projectedNew = projectionFn(newDoc);
|
|
36
|
+
var projectedOld = projectionFn(oldDoc);
|
|
37
|
+
var changedFields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
38
|
+
if (!isObjEmpty(changedFields)) {
|
|
39
|
+
observer.changed(id, changedFields);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (observer.added) {
|
|
44
|
+
var fields = projectionFn(newDoc);
|
|
45
|
+
delete fields._id;
|
|
46
|
+
observer.added(newDoc._id, fields);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (observer.removed) {
|
|
50
|
+
oldResults.forEach(function (oldDoc, id) {
|
|
51
|
+
if (!newResults.has(id))
|
|
52
|
+
observer.removed(id);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, observer, options) {
|
|
57
|
+
options = options || {};
|
|
58
|
+
var projectionFn = options.projectionFn || ejson_1.clone;
|
|
59
|
+
var new_presence_of_id = {};
|
|
60
|
+
new_results.forEach(function (doc) {
|
|
61
|
+
if (new_presence_of_id[doc._id])
|
|
62
|
+
console.log("Duplicate _id in new_results");
|
|
63
|
+
new_presence_of_id[doc._id] = true;
|
|
64
|
+
});
|
|
65
|
+
var old_index_of_id = {};
|
|
66
|
+
old_results.forEach(function (doc, i) {
|
|
67
|
+
if (doc._id in old_index_of_id)
|
|
68
|
+
console.log("Duplicate _id in old_results");
|
|
69
|
+
old_index_of_id[doc._id] = i;
|
|
70
|
+
});
|
|
71
|
+
// ALGORITHM:
|
|
72
|
+
//
|
|
73
|
+
// To determine which docs should be considered "moved" (and which
|
|
74
|
+
// merely change position because of other docs moving) we run
|
|
75
|
+
// a "longest common subsequence" (LCS) algorithm. The LCS of the
|
|
76
|
+
// old doc IDs and the new doc IDs gives the docs that should NOT be
|
|
77
|
+
// considered moved.
|
|
78
|
+
// To actually call the appropriate callbacks to get from the old state to the
|
|
79
|
+
// new state:
|
|
80
|
+
// First, we call removed() on all the items that only appear in the old
|
|
81
|
+
// state.
|
|
82
|
+
// Then, once we have the items that should not move, we walk through the new
|
|
83
|
+
// results array group-by-group, where a "group" is a set of items that have
|
|
84
|
+
// moved, anchored on the end by an item that should not move. One by one, we
|
|
85
|
+
// move each of those elements into place "before" the anchoring end-of-group
|
|
86
|
+
// item, and fire changed events on them if necessary. Then we fire a changed
|
|
87
|
+
// event on the anchor, and move on to the next group. There is always at
|
|
88
|
+
// least one group; the last group is anchored by a virtual "null" id at the
|
|
89
|
+
// end.
|
|
90
|
+
// Asymptotically: O(N k) where k is number of ops, or potentially
|
|
91
|
+
// O(N log N) if inner loop of LCS were made to be binary search.
|
|
92
|
+
//////// LCS (longest common sequence, with respect to _id)
|
|
93
|
+
// (see Wikipedia article on Longest Increasing Subsequence,
|
|
94
|
+
// where the LIS is taken of the sequence of old indices of the
|
|
95
|
+
// docs in new_results)
|
|
96
|
+
//
|
|
97
|
+
// unmoved: the output of the algorithm; members of the LCS,
|
|
98
|
+
// in the form of indices into new_results
|
|
99
|
+
var unmoved = [];
|
|
100
|
+
// max_seq_len: length of LCS found so far
|
|
101
|
+
var max_seq_len = 0;
|
|
102
|
+
// seq_ends[i]: the index into new_results of the last doc in a
|
|
103
|
+
// common subsequence of length of i+1 <= max_seq_len
|
|
104
|
+
var N = new_results.length;
|
|
105
|
+
var seq_ends = new Array(N);
|
|
106
|
+
// ptrs: the common subsequence ending with new_results[n] extends
|
|
107
|
+
// a common subsequence ending with new_results[ptr[n]], unless
|
|
108
|
+
// ptr[n] is -1.
|
|
109
|
+
var ptrs = new Array(N);
|
|
110
|
+
// virtual sequence of old indices of new results
|
|
111
|
+
var old_idx_seq = function (i_new) {
|
|
112
|
+
return old_index_of_id[new_results[i_new]._id];
|
|
113
|
+
};
|
|
114
|
+
// for each item in new_results, use it to extend a common subsequence
|
|
115
|
+
// of length j <= max_seq_len
|
|
116
|
+
for (var i = 0; i < N; i++) {
|
|
117
|
+
if (old_index_of_id[new_results[i]._id] !== undefined) {
|
|
118
|
+
var j = max_seq_len;
|
|
119
|
+
// this inner loop would traditionally be a binary search,
|
|
120
|
+
// but scanning backwards we will likely find a subseq to extend
|
|
121
|
+
// pretty soon, bounded for example by the total number of ops.
|
|
122
|
+
// If this were to be changed to a binary search, we'd still want
|
|
123
|
+
// to scan backwards a bit as an optimization.
|
|
124
|
+
while (j > 0) {
|
|
125
|
+
if (old_idx_seq(seq_ends[j - 1]) < old_idx_seq(i))
|
|
126
|
+
break;
|
|
127
|
+
j--;
|
|
128
|
+
}
|
|
129
|
+
ptrs[i] = (j === 0 ? -1 : seq_ends[j - 1]);
|
|
130
|
+
seq_ends[j] = i;
|
|
131
|
+
if (j + 1 > max_seq_len)
|
|
132
|
+
max_seq_len = j + 1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// pull out the LCS/LIS into unmoved
|
|
136
|
+
var idx = (max_seq_len === 0 ? -1 : seq_ends[max_seq_len - 1]);
|
|
137
|
+
while (idx >= 0) {
|
|
138
|
+
unmoved.push(idx);
|
|
139
|
+
idx = ptrs[idx];
|
|
140
|
+
}
|
|
141
|
+
// the unmoved item list is built backwards, so fix that
|
|
142
|
+
unmoved.reverse();
|
|
143
|
+
// the last group is always anchored by the end of the result list, which is
|
|
144
|
+
// an id of "null"
|
|
145
|
+
unmoved.push(new_results.length);
|
|
146
|
+
old_results.forEach(function (doc) {
|
|
147
|
+
if (!new_presence_of_id[doc._id])
|
|
148
|
+
observer.removed && observer.removed(doc._id);
|
|
149
|
+
});
|
|
150
|
+
// for each group of things in the new_results that is anchored by an unmoved
|
|
151
|
+
// element, iterate through the things before it.
|
|
152
|
+
var startOfGroup = 0;
|
|
153
|
+
unmoved.forEach(function (endOfGroup) {
|
|
154
|
+
var groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null;
|
|
155
|
+
var oldDoc, newDoc, fields, projectedNew, projectedOld;
|
|
156
|
+
for (var i = startOfGroup; i < endOfGroup; i++) {
|
|
157
|
+
newDoc = new_results[i];
|
|
158
|
+
if (!hasOwn.call(old_index_of_id, newDoc._id)) {
|
|
159
|
+
fields = projectionFn(newDoc);
|
|
160
|
+
delete fields._id;
|
|
161
|
+
observer.addedBefore && observer.addedBefore(newDoc._id, fields, groupId);
|
|
162
|
+
observer.added && observer.added(newDoc._id, fields);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// moved
|
|
166
|
+
oldDoc = old_results[old_index_of_id[newDoc._id]];
|
|
167
|
+
projectedNew = projectionFn(newDoc);
|
|
168
|
+
projectedOld = projectionFn(oldDoc);
|
|
169
|
+
fields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
170
|
+
if (!isObjEmpty(fields)) {
|
|
171
|
+
observer.changed && observer.changed(newDoc._id, fields);
|
|
172
|
+
}
|
|
173
|
+
observer.movedBefore && observer.movedBefore(newDoc._id, groupId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (groupId) {
|
|
177
|
+
newDoc = new_results[endOfGroup];
|
|
178
|
+
oldDoc = old_results[old_index_of_id[newDoc._id]];
|
|
179
|
+
projectedNew = projectionFn(newDoc);
|
|
180
|
+
projectedOld = projectionFn(oldDoc);
|
|
181
|
+
fields = exports.DiffSequence.makeChangedFields(projectedNew, projectedOld);
|
|
182
|
+
if (!isObjEmpty(fields)) {
|
|
183
|
+
observer.changed && observer.changed(newDoc._id, fields);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
startOfGroup = endOfGroup + 1;
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
// General helper for diff-ing two objects.
|
|
190
|
+
// callbacks is an object like so:
|
|
191
|
+
// { leftOnly: function (key, leftValue) {...},
|
|
192
|
+
// rightOnly: function (key, rightValue) {...},
|
|
193
|
+
// both: function (key, leftValue, rightValue) {...},
|
|
194
|
+
// }
|
|
195
|
+
exports.DiffSequence.diffObjects = function (left, right, callbacks) {
|
|
196
|
+
Object.keys(left).forEach(key => {
|
|
197
|
+
const leftValue = left[key];
|
|
198
|
+
if (hasOwn.call(right, key)) {
|
|
199
|
+
callbacks.both && callbacks.both(key, leftValue, right[key]);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
callbacks.leftOnly && callbacks.leftOnly(key, leftValue);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
if (callbacks.rightOnly) {
|
|
206
|
+
Object.keys(right).forEach(key => {
|
|
207
|
+
const rightValue = right[key];
|
|
208
|
+
if (!hasOwn.call(left, key)) {
|
|
209
|
+
callbacks.rightOnly(key, rightValue);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
exports.DiffSequence.diffMaps = function (left, right, callbacks) {
|
|
215
|
+
left.forEach(function (leftValue, key) {
|
|
216
|
+
if (right.has(key)) {
|
|
217
|
+
callbacks.both && callbacks.both(key, leftValue, right.get(key));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
callbacks.leftOnly && callbacks.leftOnly(key, leftValue);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
if (callbacks.rightOnly) {
|
|
224
|
+
right.forEach(function (rightValue, key) {
|
|
225
|
+
if (!left.has(key)) {
|
|
226
|
+
callbacks.rightOnly(key, rightValue);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
exports.DiffSequence.makeChangedFields = function (newDoc, oldDoc) {
|
|
232
|
+
var fields = {};
|
|
233
|
+
exports.DiffSequence.diffObjects(oldDoc, newDoc, {
|
|
234
|
+
leftOnly: function (key, value) {
|
|
235
|
+
fields[key] = undefined;
|
|
236
|
+
},
|
|
237
|
+
rightOnly: function (key, value) {
|
|
238
|
+
fields[key] = value;
|
|
239
|
+
},
|
|
240
|
+
both: function (key, leftValue, rightValue) {
|
|
241
|
+
if (!(0, ejson_1.equals)(leftValue, rightValue))
|
|
242
|
+
fields[key] = rightValue;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return fields;
|
|
246
|
+
};
|
|
247
|
+
exports.DiffSequence.applyChanges = function (doc, changeFields) {
|
|
248
|
+
Object.keys(changeFields).forEach(key => {
|
|
249
|
+
const value = changeFields[key];
|
|
250
|
+
if (typeof value === "undefined") {
|
|
251
|
+
delete doc[key];
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
doc[key] = value;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
};
|