mongodb-livedata-server 0.0.1
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 +63 -0
- package/dist/livedata_server.js +9 -0
- package/dist/meteor/binary-heap/max_heap.js +186 -0
- package/dist/meteor/binary-heap/min_heap.js +17 -0
- package/dist/meteor/binary-heap/min_max_heap.js +48 -0
- package/dist/meteor/callback-hook/hook.js +78 -0
- package/dist/meteor/ddp/crossbar.js +136 -0
- package/dist/meteor/ddp/heartbeat.js +77 -0
- package/dist/meteor/ddp/livedata_server.js +403 -0
- package/dist/meteor/ddp/method-invocation.js +72 -0
- package/dist/meteor/ddp/random-stream.js +100 -0
- package/dist/meteor/ddp/session-collection-view.js +106 -0
- package/dist/meteor/ddp/session-document-view.js +82 -0
- package/dist/meteor/ddp/session.js +570 -0
- package/dist/meteor/ddp/stream_server.js +181 -0
- package/dist/meteor/ddp/subscription.js +347 -0
- package/dist/meteor/ddp/utils.js +104 -0
- package/dist/meteor/ddp/writefence.js +111 -0
- package/dist/meteor/diff-sequence/diff.js +257 -0
- package/dist/meteor/ejson/ejson.js +569 -0
- package/dist/meteor/ejson/stringify.js +119 -0
- package/dist/meteor/ejson/utils.js +42 -0
- package/dist/meteor/id-map/id_map.js +92 -0
- package/dist/meteor/mongo/caching_change_observer.js +94 -0
- package/dist/meteor/mongo/doc_fetcher.js +53 -0
- package/dist/meteor/mongo/geojson_utils.js +41 -0
- package/dist/meteor/mongo/live_connection.js +264 -0
- package/dist/meteor/mongo/live_cursor.js +57 -0
- package/dist/meteor/mongo/minimongo_common.js +2002 -0
- package/dist/meteor/mongo/minimongo_matcher.js +217 -0
- package/dist/meteor/mongo/minimongo_sorter.js +268 -0
- package/dist/meteor/mongo/observe_driver_utils.js +73 -0
- package/dist/meteor/mongo/observe_multiplexer.js +228 -0
- package/dist/meteor/mongo/oplog-observe-driver.js +919 -0
- package/dist/meteor/mongo/oplog_tailing.js +352 -0
- package/dist/meteor/mongo/oplog_v2_converter.js +126 -0
- package/dist/meteor/mongo/polling_observe_driver.js +195 -0
- package/dist/meteor/mongo/synchronous-cursor.js +261 -0
- package/dist/meteor/mongo/synchronous-queue.js +110 -0
- package/dist/meteor/ordered-dict/ordered_dict.js +198 -0
- package/dist/meteor/random/AbstractRandomGenerator.js +92 -0
- package/dist/meteor/random/AleaRandomGenerator.js +90 -0
- package/dist/meteor/random/NodeRandomGenerator.js +42 -0
- package/dist/meteor/random/createAleaGenerator.js +32 -0
- package/dist/meteor/random/createRandom.js +22 -0
- package/dist/meteor/random/main.js +12 -0
- package/livedata_server.ts +3 -0
- package/meteor/LICENSE +28 -0
- package/meteor/binary-heap/max_heap.ts +225 -0
- package/meteor/binary-heap/min_heap.ts +15 -0
- package/meteor/binary-heap/min_max_heap.ts +53 -0
- package/meteor/callback-hook/hook.ts +85 -0
- package/meteor/ddp/crossbar.ts +148 -0
- package/meteor/ddp/heartbeat.ts +97 -0
- package/meteor/ddp/livedata_server.ts +473 -0
- package/meteor/ddp/method-invocation.ts +86 -0
- package/meteor/ddp/random-stream.ts +102 -0
- package/meteor/ddp/session-collection-view.ts +119 -0
- package/meteor/ddp/session-document-view.ts +92 -0
- package/meteor/ddp/session.ts +708 -0
- package/meteor/ddp/stream_server.ts +204 -0
- package/meteor/ddp/subscription.ts +392 -0
- package/meteor/ddp/utils.ts +119 -0
- package/meteor/ddp/writefence.ts +130 -0
- package/meteor/diff-sequence/diff.ts +295 -0
- package/meteor/ejson/ejson.ts +601 -0
- package/meteor/ejson/stringify.ts +122 -0
- package/meteor/ejson/utils.ts +38 -0
- package/meteor/id-map/id_map.ts +84 -0
- package/meteor/mongo/caching_change_observer.ts +120 -0
- package/meteor/mongo/doc_fetcher.ts +52 -0
- package/meteor/mongo/geojson_utils.ts +42 -0
- package/meteor/mongo/live_connection.ts +302 -0
- package/meteor/mongo/live_cursor.ts +79 -0
- package/meteor/mongo/minimongo_common.ts +2440 -0
- package/meteor/mongo/minimongo_matcher.ts +275 -0
- package/meteor/mongo/minimongo_sorter.ts +331 -0
- package/meteor/mongo/observe_driver_utils.ts +79 -0
- package/meteor/mongo/observe_multiplexer.ts +256 -0
- package/meteor/mongo/oplog-observe-driver.ts +1049 -0
- package/meteor/mongo/oplog_tailing.ts +414 -0
- package/meteor/mongo/oplog_v2_converter.ts +124 -0
- package/meteor/mongo/polling_observe_driver.ts +247 -0
- package/meteor/mongo/synchronous-cursor.ts +293 -0
- package/meteor/mongo/synchronous-queue.ts +119 -0
- package/meteor/ordered-dict/ordered_dict.ts +229 -0
- package/meteor/random/AbstractRandomGenerator.ts +99 -0
- package/meteor/random/AleaRandomGenerator.ts +96 -0
- package/meteor/random/NodeRandomGenerator.ts +37 -0
- package/meteor/random/createAleaGenerator.ts +31 -0
- package/meteor/random/createRandom.ts +19 -0
- package/meteor/random/main.ts +8 -0
- package/package.json +30 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleError = exports.checkError = exports.isInfOrNaN = exports.isArguments = exports.convertMapToObject = exports.hasOwn = exports.lengthOf = exports.keysOf = exports.isObject = exports.isFunction = void 0;
|
|
4
|
+
const isFunction = (fn) => typeof fn === 'function';
|
|
5
|
+
exports.isFunction = isFunction;
|
|
6
|
+
const isObject = (fn) => typeof fn === 'object';
|
|
7
|
+
exports.isObject = isObject;
|
|
8
|
+
const keysOf = (obj) => Object.keys(obj);
|
|
9
|
+
exports.keysOf = keysOf;
|
|
10
|
+
const lengthOf = (obj) => Object.keys(obj).length;
|
|
11
|
+
exports.lengthOf = lengthOf;
|
|
12
|
+
const hasOwn = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
exports.hasOwn = hasOwn;
|
|
14
|
+
const convertMapToObject = (map) => Array.from(map).reduce((acc, [key, value]) => {
|
|
15
|
+
// reassign to not create new object
|
|
16
|
+
acc[key] = value;
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
19
|
+
exports.convertMapToObject = convertMapToObject;
|
|
20
|
+
function isArguments(obj) {
|
|
21
|
+
return obj != null && (0, exports.hasOwn)(obj, 'callee');
|
|
22
|
+
}
|
|
23
|
+
exports.isArguments = isArguments;
|
|
24
|
+
const isInfOrNaN = obj => Number.isNaN(obj) || obj === Infinity || obj === -Infinity;
|
|
25
|
+
exports.isInfOrNaN = isInfOrNaN;
|
|
26
|
+
exports.checkError = {
|
|
27
|
+
maxStack: (msgError) => new RegExp('Maximum call stack size exceeded', 'g').test(msgError),
|
|
28
|
+
};
|
|
29
|
+
function handleError(fn, ...args) {
|
|
30
|
+
try {
|
|
31
|
+
return fn.apply(this, args);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const isMaxStack = exports.checkError.maxStack(error.message);
|
|
35
|
+
if (isMaxStack) {
|
|
36
|
+
throw new Error('Converting circular structure to JSON');
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.handleError = handleError;
|
|
42
|
+
;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.IdMap = void 0;
|
|
27
|
+
const EJSON = __importStar(require("../ejson/ejson"));
|
|
28
|
+
class IdMap {
|
|
29
|
+
constructor(idStringify, idParse) {
|
|
30
|
+
this._map = new Map();
|
|
31
|
+
this._idStringify = idStringify || JSON.stringify;
|
|
32
|
+
this._idParse = idParse || JSON.parse;
|
|
33
|
+
}
|
|
34
|
+
// Some of these methods are designed to match methods on OrderedDict, since
|
|
35
|
+
// (eg) ObserveMultiplex and _CachingChangeObserver use them interchangeably.
|
|
36
|
+
// (Conceivably, this should be replaced with "UnorderedDict" with a specific
|
|
37
|
+
// set of methods that overlap between the two.)
|
|
38
|
+
get(id) {
|
|
39
|
+
const key = this._idStringify(id);
|
|
40
|
+
return this._map.get(key);
|
|
41
|
+
}
|
|
42
|
+
set(id, value) {
|
|
43
|
+
const key = this._idStringify(id);
|
|
44
|
+
this._map.set(key, value);
|
|
45
|
+
}
|
|
46
|
+
remove(id) {
|
|
47
|
+
const key = this._idStringify(id);
|
|
48
|
+
this._map.delete(key);
|
|
49
|
+
}
|
|
50
|
+
has(id) {
|
|
51
|
+
const key = this._idStringify(id);
|
|
52
|
+
return this._map.has(key);
|
|
53
|
+
}
|
|
54
|
+
empty() {
|
|
55
|
+
return this._map.size === 0;
|
|
56
|
+
}
|
|
57
|
+
clear() {
|
|
58
|
+
this._map.clear();
|
|
59
|
+
}
|
|
60
|
+
// Iterates over the items in the map. Return `false` to break the loop.
|
|
61
|
+
forEach(iterator) {
|
|
62
|
+
// don't use _.each, because we can't break out of it.
|
|
63
|
+
for (let [key, value] of this._map) {
|
|
64
|
+
const breakIfFalse = iterator.call(null, value, this._idParse(key));
|
|
65
|
+
if (breakIfFalse === false) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
size() {
|
|
71
|
+
return this._map.size;
|
|
72
|
+
}
|
|
73
|
+
setDefault(id, def) {
|
|
74
|
+
const key = this._idStringify(id);
|
|
75
|
+
if (this._map.has(key)) {
|
|
76
|
+
return this._map.get(key);
|
|
77
|
+
}
|
|
78
|
+
this._map.set(key, def);
|
|
79
|
+
return def;
|
|
80
|
+
}
|
|
81
|
+
// Assumes that values are EJSON-cloneable, and that we don't need to clone
|
|
82
|
+
// IDs (ie, that nobody is going to mutate an ObjectId).
|
|
83
|
+
clone() {
|
|
84
|
+
const clone = new IdMap(this._idStringify, this._idParse);
|
|
85
|
+
// copy directly to avoid stringify/parse overhead
|
|
86
|
+
this._map.forEach(function (value, key) {
|
|
87
|
+
clone._map.set(key, EJSON.clone(value));
|
|
88
|
+
});
|
|
89
|
+
return clone;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.IdMap = IdMap;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// _CachingChangeObserver is an object which receives observeChanges callbacks
|
|
3
|
+
// and keeps a cache of the current cursor state up to date in this.docs. Users
|
|
4
|
+
// of this class should read the docs field but not modify it. You should pass
|
|
5
|
+
// the "applyChange" field as the callbacks to the underlying observeChanges
|
|
6
|
+
// call. Optionally, you can specify your own observeChanges callbacks which are
|
|
7
|
+
// invoked immediately before the docs field is updated; this object is made
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports._CachingChangeObserver = void 0;
|
|
10
|
+
const diff_1 = require("../diff-sequence/diff");
|
|
11
|
+
const ejson_1 = require("../ejson/ejson");
|
|
12
|
+
const ordered_dict_1 = require("../ordered-dict/ordered_dict");
|
|
13
|
+
// available as `this` to those callbacks.
|
|
14
|
+
class _CachingChangeObserver {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
const orderedFromCallbacks = (options.callbacks &&
|
|
17
|
+
!!(options.callbacks.addedBefore || options.callbacks.movedBefore));
|
|
18
|
+
if (options.hasOwnProperty('ordered')) {
|
|
19
|
+
this.ordered = options.ordered;
|
|
20
|
+
if (options.callbacks && options.ordered !== orderedFromCallbacks) {
|
|
21
|
+
throw Error('ordered option doesn\'t match callbacks');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (options.callbacks) {
|
|
25
|
+
this.ordered = orderedFromCallbacks;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw Error('must provide ordered or callbacks');
|
|
29
|
+
}
|
|
30
|
+
const callbacks = options.callbacks || {};
|
|
31
|
+
if (this.ordered) {
|
|
32
|
+
this.docs = new ordered_dict_1.OrderedDict();
|
|
33
|
+
this.applyChange = {
|
|
34
|
+
addedBefore: (id, fields, before) => {
|
|
35
|
+
// Take a shallow copy since the top-level properties can be changed
|
|
36
|
+
const doc = Object.assign({}, fields);
|
|
37
|
+
doc._id = id;
|
|
38
|
+
if (callbacks.addedBefore) {
|
|
39
|
+
callbacks.addedBefore.call(this, id, (0, ejson_1.clone)(fields), before);
|
|
40
|
+
}
|
|
41
|
+
// This line triggers if we provide added with movedBefore.
|
|
42
|
+
if (callbacks.added) {
|
|
43
|
+
callbacks.added.call(this, id, (0, ejson_1.clone)(fields));
|
|
44
|
+
}
|
|
45
|
+
// XXX could `before` be a falsy ID? Technically
|
|
46
|
+
// idStringify seems to allow for them -- though
|
|
47
|
+
// OrderedDict won't call stringify on a falsy arg.
|
|
48
|
+
this.docs.putBefore(id, doc, before || null);
|
|
49
|
+
},
|
|
50
|
+
movedBefore: (id, before) => {
|
|
51
|
+
const doc = this.docs.get(id);
|
|
52
|
+
if (callbacks.movedBefore) {
|
|
53
|
+
callbacks.movedBefore.call(this, id, before);
|
|
54
|
+
}
|
|
55
|
+
this.docs.moveBefore(id, before || null);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.docs = new Map();
|
|
61
|
+
this.applyChange = {
|
|
62
|
+
added: (id, fields) => {
|
|
63
|
+
// Take a shallow copy since the top-level properties can be changed
|
|
64
|
+
const doc = Object.assign({}, fields);
|
|
65
|
+
if (callbacks.added) {
|
|
66
|
+
callbacks.added.call(this, id, (0, ejson_1.clone)(fields));
|
|
67
|
+
}
|
|
68
|
+
doc._id = id;
|
|
69
|
+
this.docs.set(id, doc);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// The methods in _IdMap and OrderedDict used by these callbacks are
|
|
74
|
+
// identical.
|
|
75
|
+
this.applyChange.changed = (id, fields) => {
|
|
76
|
+
const doc = this.docs.get(id);
|
|
77
|
+
if (!doc) {
|
|
78
|
+
throw new Error(`Unknown id for changed: ${id}`);
|
|
79
|
+
}
|
|
80
|
+
if (callbacks.changed) {
|
|
81
|
+
callbacks.changed.call(this, id, (0, ejson_1.clone)(fields));
|
|
82
|
+
}
|
|
83
|
+
diff_1.DiffSequence.applyChanges(doc, fields);
|
|
84
|
+
};
|
|
85
|
+
this.applyChange.removed = id => {
|
|
86
|
+
if (callbacks.removed) {
|
|
87
|
+
callbacks.removed.call(this, id);
|
|
88
|
+
}
|
|
89
|
+
this.docs.delete(id);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports._CachingChangeObserver = _CachingChangeObserver;
|
|
94
|
+
;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DocFetcher = void 0;
|
|
4
|
+
const ejson_1 = require("../ejson/ejson");
|
|
5
|
+
class DocFetcher {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this._callbacksForOp = new Map();
|
|
9
|
+
}
|
|
10
|
+
// Fetches document "id" from collectionName, returning it or null if not
|
|
11
|
+
// found.
|
|
12
|
+
//
|
|
13
|
+
// If you make multiple calls to fetch() with the same op reference,
|
|
14
|
+
// DocFetcher may assume that they all return the same document. (It does
|
|
15
|
+
// not check to see if collectionName/id match.)
|
|
16
|
+
//
|
|
17
|
+
// You may assume that callback is never called synchronously (and in fact
|
|
18
|
+
// OplogObserveDriver does so).
|
|
19
|
+
async fetch(collectionName, id, op, callback) {
|
|
20
|
+
const self = this;
|
|
21
|
+
// If there's already an in-progress fetch for this cache key, yield until
|
|
22
|
+
// it's done and return whatever it returns.
|
|
23
|
+
if (self._callbacksForOp.has(op)) {
|
|
24
|
+
self._callbacksForOp.get(op).push(callback);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const callbacks = [callback];
|
|
28
|
+
self._callbacksForOp.set(op, callbacks);
|
|
29
|
+
try {
|
|
30
|
+
var doc = await self.db.collection(collectionName).findOne({ _id: id }) || null;
|
|
31
|
+
// Return doc to all relevant callbacks. Note that this array can
|
|
32
|
+
// continue to grow during callback execution.
|
|
33
|
+
while (callbacks.length > 0) {
|
|
34
|
+
// Clone the document so that the various calls to fetch don't return
|
|
35
|
+
// objects that are intertwingled with each other. Clone before
|
|
36
|
+
// popping the future, so that if clone throws, the error gets passed
|
|
37
|
+
// to the next callback.
|
|
38
|
+
callbacks.pop()(null, (0, ejson_1.clone)(doc));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
while (callbacks.length > 0) {
|
|
43
|
+
callbacks.pop()(e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
// XXX consider keeping the doc around for a period of time before
|
|
48
|
+
// removing from the cache
|
|
49
|
+
self._callbacksForOp.delete(op);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.DocFetcher = DocFetcher;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.geometryWithinRadius = exports.pointDistance = exports.numberToRadius = void 0;
|
|
4
|
+
function numberToRadius(number) {
|
|
5
|
+
return number * Math.PI / 180;
|
|
6
|
+
}
|
|
7
|
+
exports.numberToRadius = numberToRadius;
|
|
8
|
+
// from http://www.movable-type.co.uk/scripts/latlong.html
|
|
9
|
+
function pointDistance(pt1, pt2) {
|
|
10
|
+
var lon1 = pt1.coordinates[0], lat1 = pt1.coordinates[1], lon2 = pt2.coordinates[0], lat2 = pt2.coordinates[1], dLat = numberToRadius(lat2 - lat1), dLon = numberToRadius(lon2 - lon1), a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(numberToRadius(lat1))
|
|
11
|
+
* Math.cos(numberToRadius(lat2)) * Math.pow(Math.sin(dLon / 2), 2), c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
12
|
+
// Earth radius is 6371 km
|
|
13
|
+
return (6371 * c) * 1000; // returns meters
|
|
14
|
+
}
|
|
15
|
+
exports.pointDistance = pointDistance;
|
|
16
|
+
// checks if geometry lies entirely within a circle
|
|
17
|
+
// works with Point, LineString, Polygon
|
|
18
|
+
function geometryWithinRadius(geometry, center, radius) {
|
|
19
|
+
if (geometry.type == 'Point') {
|
|
20
|
+
return pointDistance(geometry, center) <= radius;
|
|
21
|
+
}
|
|
22
|
+
else if (geometry.type == 'LineString' || geometry.type == 'Polygon') {
|
|
23
|
+
var point = {};
|
|
24
|
+
var coordinates;
|
|
25
|
+
if (geometry.type == 'Polygon') {
|
|
26
|
+
// it's enough to check the exterior ring of the Polygon
|
|
27
|
+
coordinates = geometry.coordinates[0];
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
coordinates = geometry.coordinates;
|
|
31
|
+
}
|
|
32
|
+
for (var i in coordinates) {
|
|
33
|
+
point.coordinates = coordinates[i];
|
|
34
|
+
if (pointDistance(point, center) > radius) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
exports.geometryWithinRadius = geometryWithinRadius;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LiveMongoConnection = void 0;
|
|
7
|
+
const hook_1 = require("../callback-hook/hook");
|
|
8
|
+
const mongodb_1 = require("mongodb");
|
|
9
|
+
const live_cursor_1 = require("./live_cursor");
|
|
10
|
+
const ejson_1 = require("../ejson/ejson");
|
|
11
|
+
const synchronous_cursor_1 = require("./synchronous-cursor");
|
|
12
|
+
const oplog_tailing_1 = require("./oplog_tailing");
|
|
13
|
+
const doc_fetcher_1 = require("./doc_fetcher");
|
|
14
|
+
const observe_multiplexer_1 = require("./observe_multiplexer");
|
|
15
|
+
const polling_observe_driver_1 = require("./polling_observe_driver");
|
|
16
|
+
const oplog_observe_driver_1 = require("./oplog-observe-driver");
|
|
17
|
+
const minimongo_matcher_1 = require("./minimongo_matcher");
|
|
18
|
+
const minimongo_sorter_1 = __importDefault(require("./minimongo_sorter"));
|
|
19
|
+
class LiveMongoConnection {
|
|
20
|
+
constructor(url, options) {
|
|
21
|
+
this._oplogHandle = null;
|
|
22
|
+
this._docFetcher = null;
|
|
23
|
+
this._observeMultiplexers = {};
|
|
24
|
+
this._onFailoverHook = new hook_1.Hook();
|
|
25
|
+
options = options || {};
|
|
26
|
+
var mongoOptions = {
|
|
27
|
+
ignoreUndefined: true,
|
|
28
|
+
maxPoolSize: undefined
|
|
29
|
+
};
|
|
30
|
+
// Internally the oplog connections specify their own maxPoolSize
|
|
31
|
+
// which we don't want to overwrite with any user defined value
|
|
32
|
+
if (options.hasOwnProperty("maxPoolSize")) {
|
|
33
|
+
// If we just set this for "server", replSet will override it. If we just
|
|
34
|
+
// set it for replSet, it will be ignored if we're not using a replSet.
|
|
35
|
+
mongoOptions.maxPoolSize = options.maxPoolSize;
|
|
36
|
+
}
|
|
37
|
+
this.client = new mongodb_1.MongoClient(url, mongoOptions);
|
|
38
|
+
this.db = this.client.db();
|
|
39
|
+
this.client.on('serverDescriptionChanged', event => {
|
|
40
|
+
// When the connection is no longer against the primary node, execute all
|
|
41
|
+
// failover hooks. This is important for the driver as it has to re-pool the
|
|
42
|
+
// query when it happens.
|
|
43
|
+
if (event.previousDescription.type !== 'RSPrimary' &&
|
|
44
|
+
event.newDescription.type === 'RSPrimary') {
|
|
45
|
+
this._onFailoverHook.each(callback => {
|
|
46
|
+
callback();
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
if (options.oplogUrl) {
|
|
52
|
+
this._oplogHandle = new oplog_tailing_1.OplogHandle(options.oplogUrl, this.db.databaseName);
|
|
53
|
+
this._docFetcher = new doc_fetcher_1.DocFetcher(this.db);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
close() {
|
|
57
|
+
var self = this;
|
|
58
|
+
if (!self.db)
|
|
59
|
+
throw Error("close called before Connection created?");
|
|
60
|
+
// XXX probably untested
|
|
61
|
+
var oplogHandle = self._oplogHandle;
|
|
62
|
+
self._oplogHandle = null;
|
|
63
|
+
if (oplogHandle)
|
|
64
|
+
oplogHandle.stop();
|
|
65
|
+
// Use Future.wrap so that errors get thrown. This happens to
|
|
66
|
+
// work even outside a fiber since the 'close' method is not
|
|
67
|
+
// actually asynchronous.
|
|
68
|
+
self.client.close(true);
|
|
69
|
+
}
|
|
70
|
+
// Tails the cursor described by cursorDescription, most likely on the
|
|
71
|
+
// oplog. Calls docCallback with each document found. Ignores errors and just
|
|
72
|
+
// restarts the tail on error.
|
|
73
|
+
//
|
|
74
|
+
// If timeoutMS is set, then if we don't get a new document every timeoutMS,
|
|
75
|
+
// kill and restart the cursor. This is primarily a workaround for #8598.
|
|
76
|
+
tail(cursorDescription, docCallback, timeoutMS) {
|
|
77
|
+
if (!cursorDescription.options.tailable)
|
|
78
|
+
throw new Error("Can only tail a tailable cursor");
|
|
79
|
+
var cursor = (0, synchronous_cursor_1._createSynchronousCursor)(this.db, cursorDescription);
|
|
80
|
+
var stopped = false;
|
|
81
|
+
var lastTS;
|
|
82
|
+
const loop = async () => {
|
|
83
|
+
var doc = null;
|
|
84
|
+
while (true) {
|
|
85
|
+
if (stopped)
|
|
86
|
+
return;
|
|
87
|
+
try {
|
|
88
|
+
doc = await cursor._nextObjectPromiseWithTimeout(timeoutMS);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// There's no good way to figure out if this was actually an error from
|
|
92
|
+
// Mongo, or just client-side (including our own timeout error). Ah
|
|
93
|
+
// well. But either way, we need to retry the cursor (unless the failure
|
|
94
|
+
// was because the observe got stopped).
|
|
95
|
+
doc = null;
|
|
96
|
+
}
|
|
97
|
+
// Since we awaited a promise above, we need to check again to see if
|
|
98
|
+
// we've been stopped before calling the callback.
|
|
99
|
+
if (stopped)
|
|
100
|
+
return;
|
|
101
|
+
if (doc) {
|
|
102
|
+
// If a tailable cursor contains a "ts" field, use it to recreate the
|
|
103
|
+
// cursor on error. ("ts" is a standard that Mongo uses internally for
|
|
104
|
+
// the oplog, and there's a special flag that lets you do binary search
|
|
105
|
+
// on it instead of needing to use an index.)
|
|
106
|
+
lastTS = doc.ts;
|
|
107
|
+
docCallback(doc);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const newSelector = Object.assign({}, cursorDescription.selector);
|
|
111
|
+
if (lastTS) {
|
|
112
|
+
newSelector.ts = { $gt: lastTS };
|
|
113
|
+
}
|
|
114
|
+
const newDescription = new live_cursor_1.CursorDescription(cursorDescription.collectionName, newSelector, cursorDescription.options);
|
|
115
|
+
cursor = (0, synchronous_cursor_1._createSynchronousCursor)(this.db, newDescription);
|
|
116
|
+
// Mongo failover takes many seconds. Retry in a bit. (Without this
|
|
117
|
+
// setTimeout, we peg the CPU at 100% and never notice the actual
|
|
118
|
+
// failover.
|
|
119
|
+
setTimeout(loop, 100);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
setImmediate(loop);
|
|
125
|
+
return {
|
|
126
|
+
stop: function () {
|
|
127
|
+
stopped = true;
|
|
128
|
+
cursor.close();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
_observeChanges(cursorDescription, ordered, callbacks, nonMutatingCallbacks) {
|
|
133
|
+
var self = this;
|
|
134
|
+
if (cursorDescription.options.tailable) {
|
|
135
|
+
return self._observeChangesTailable(cursorDescription, ordered, callbacks);
|
|
136
|
+
}
|
|
137
|
+
// You may not filter out _id when observing changes, because the id is a core
|
|
138
|
+
// part of the observeChanges API.
|
|
139
|
+
const fieldsOptions = cursorDescription.options.projection;
|
|
140
|
+
if (fieldsOptions && (fieldsOptions._id === 0 || fieldsOptions._id === false))
|
|
141
|
+
throw Error("You may not observe a cursor with {fields: {_id: 0}}");
|
|
142
|
+
var observeKey = (0, ejson_1.stringify)(Object.assign({ ordered: ordered }, cursorDescription));
|
|
143
|
+
var multiplexer, observeDriver;
|
|
144
|
+
var firstHandle = false;
|
|
145
|
+
// Find a matching ObserveMultiplexer, or create a new one. This next block is
|
|
146
|
+
// guaranteed to not yield (and it doesn't call anything that can observe a
|
|
147
|
+
// new query), so no other calls to this function can interleave with it.
|
|
148
|
+
//Meteor._noYieldsAllowed(function () {
|
|
149
|
+
if (self._observeMultiplexers.hasOwnProperty(observeKey)) {
|
|
150
|
+
multiplexer = self._observeMultiplexers[observeKey];
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
firstHandle = true;
|
|
154
|
+
// Create a new ObserveMultiplexer.
|
|
155
|
+
multiplexer = new observe_multiplexer_1.ObserveMultiplexer({
|
|
156
|
+
ordered: ordered,
|
|
157
|
+
onStop: function () {
|
|
158
|
+
delete self._observeMultiplexers[observeKey];
|
|
159
|
+
observeDriver.stop();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
self._observeMultiplexers[observeKey] = multiplexer;
|
|
163
|
+
}
|
|
164
|
+
//});
|
|
165
|
+
var observeHandle = new observe_multiplexer_1.ObserveHandle(multiplexer, callbacks, nonMutatingCallbacks);
|
|
166
|
+
if (firstHandle) {
|
|
167
|
+
let matcher;
|
|
168
|
+
let sorter;
|
|
169
|
+
// At a bare minimum, using the oplog requires us to have an oplog, to
|
|
170
|
+
// want unordered callbacks, and to not want a callback on the polls
|
|
171
|
+
// that won't happen.
|
|
172
|
+
const basicPrerequisites = self._oplogHandle && !ordered && !callbacks._testOnlyPollCallback;
|
|
173
|
+
let selectorIsCompilable = false;
|
|
174
|
+
// We need to be able to compile the selector. Fall back to polling for
|
|
175
|
+
// some newfangled $selector that minimongo doesn't support yet.
|
|
176
|
+
try {
|
|
177
|
+
matcher = new minimongo_matcher_1.MinimongoMatcher(cursorDescription.selector);
|
|
178
|
+
selectorIsCompilable = true;
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
}
|
|
182
|
+
const supportedByOplog = oplog_observe_driver_1.OplogObserveDriver.cursorSupported(cursorDescription, matcher);
|
|
183
|
+
let cursorIsSortable = false;
|
|
184
|
+
// And we need to be able to compile the sort, if any. eg, can't be
|
|
185
|
+
// {$natural: 1}.
|
|
186
|
+
if (!cursorDescription.options.sort)
|
|
187
|
+
cursorIsSortable = true;
|
|
188
|
+
try {
|
|
189
|
+
sorter = new minimongo_sorter_1.default(cursorDescription.options.sort);
|
|
190
|
+
cursorIsSortable = true;
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
}
|
|
194
|
+
const canUseOplog = basicPrerequisites && selectorIsCompilable && cursorIsSortable && supportedByOplog;
|
|
195
|
+
var driverClass = canUseOplog ? oplog_observe_driver_1.OplogObserveDriver : polling_observe_driver_1.PollingObserveDriver;
|
|
196
|
+
observeDriver = new driverClass({
|
|
197
|
+
cursorDescription: cursorDescription,
|
|
198
|
+
mongoHandle: self,
|
|
199
|
+
multiplexer: multiplexer,
|
|
200
|
+
ordered: ordered,
|
|
201
|
+
matcher,
|
|
202
|
+
sorter // ignored by polling
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
// Blocks until the initial adds have been sent.
|
|
206
|
+
multiplexer.addHandleAndSendInitialAdds(observeHandle);
|
|
207
|
+
return observeHandle;
|
|
208
|
+
}
|
|
209
|
+
// observeChanges for tailable cursors on capped collections.
|
|
210
|
+
//
|
|
211
|
+
// Some differences from normal cursors:
|
|
212
|
+
// - Will never produce anything other than 'added' or 'addedBefore'. If you
|
|
213
|
+
// do update a document that has already been produced, this will not notice
|
|
214
|
+
// it.
|
|
215
|
+
// - If you disconnect and reconnect from Mongo, it will essentially restart
|
|
216
|
+
// the query, which will lead to duplicate results. This is pretty bad,
|
|
217
|
+
// but if you include a field called 'ts' which is inserted as
|
|
218
|
+
// new MongoInternals.MongoTimestamp(0, 0) (which is initialized to the
|
|
219
|
+
// current Mongo-style timestamp), we'll be able to find the place to
|
|
220
|
+
// restart properly. (This field is specifically understood by Mongo with an
|
|
221
|
+
// optimization which allows it to find the right place to start without
|
|
222
|
+
// an index on ts. It's how the oplog works.)
|
|
223
|
+
// - No callbacks are triggered synchronously with the call (there's no
|
|
224
|
+
// differentiation between "initial data" and "later changes"; everything
|
|
225
|
+
// that matches the query gets sent asynchronously).
|
|
226
|
+
// - De-duplication is not implemented.
|
|
227
|
+
// - Does not yet interact with the write fence. Probably, this should work by
|
|
228
|
+
// ignoring removes (which don't work on capped collections) and updates
|
|
229
|
+
// (which don't affect tailable cursors), and just keeping track of the ID
|
|
230
|
+
// of the inserted object, and closing the write fence once you get to that
|
|
231
|
+
// ID (or timestamp?). This doesn't work well if the document doesn't match
|
|
232
|
+
// the query, though. On the other hand, the write fence can close
|
|
233
|
+
// immediately if it does not match the query. So if we trust minimongo
|
|
234
|
+
// enough to accurately evaluate the query against the write fence, we
|
|
235
|
+
// should be able to do this... Of course, minimongo doesn't even support
|
|
236
|
+
// Mongo Timestamps yet.
|
|
237
|
+
_observeChangesTailable(cursorDescription, ordered, callbacks) {
|
|
238
|
+
var self = this;
|
|
239
|
+
// Tailable cursors only ever call added/addedBefore callbacks, so it's an
|
|
240
|
+
// error if you didn't provide them.
|
|
241
|
+
if ((ordered && !callbacks.addedBefore) ||
|
|
242
|
+
(!ordered && !callbacks.added)) {
|
|
243
|
+
throw new Error("Can't observe an " + (ordered ? "ordered" : "unordered")
|
|
244
|
+
+ " tailable cursor without a "
|
|
245
|
+
+ (ordered ? "addedBefore" : "added") + " callback");
|
|
246
|
+
}
|
|
247
|
+
return self.tail(cursorDescription, function (doc) {
|
|
248
|
+
var id = doc._id;
|
|
249
|
+
delete doc._id;
|
|
250
|
+
// The ts is an implementation detail. Hide it.
|
|
251
|
+
delete doc.ts;
|
|
252
|
+
if (ordered) {
|
|
253
|
+
callbacks.addedBefore(id, doc, null);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
callbacks.added(id, doc);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
_onFailover(callback) {
|
|
261
|
+
return this._onFailoverHook.register(callback);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.LiveMongoConnection = LiveMongoConnection;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LiveCursor = exports.CursorDescription = void 0;
|
|
4
|
+
const main_1 = require("../random/main");
|
|
5
|
+
class CursorDescription {
|
|
6
|
+
constructor(collectionName, selector, options) {
|
|
7
|
+
this.collectionName = collectionName;
|
|
8
|
+
var self = this;
|
|
9
|
+
self.collectionName = collectionName;
|
|
10
|
+
self.selector = _rewriteSelector(selector);
|
|
11
|
+
self.options = options || {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.CursorDescription = CursorDescription;
|
|
15
|
+
class LiveCursor {
|
|
16
|
+
constructor(mongo, collectionName, selector, options) {
|
|
17
|
+
this.mongo = mongo;
|
|
18
|
+
this.cursorDescription = new CursorDescription(collectionName, selector, options);
|
|
19
|
+
}
|
|
20
|
+
_publishCursor(sub) {
|
|
21
|
+
const observeHandle = this.mongo._observeChanges(this.cursorDescription, false, {
|
|
22
|
+
added: (id, fields) => {
|
|
23
|
+
sub.added(this.cursorDescription.collectionName, id, fields);
|
|
24
|
+
},
|
|
25
|
+
changed: (id, fields) => {
|
|
26
|
+
sub.changed(this.cursorDescription.collectionName, id, fields);
|
|
27
|
+
},
|
|
28
|
+
removed: (id) => {
|
|
29
|
+
sub.removed(this.cursorDescription.collectionName, id);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
// Publications don't mutate the documents
|
|
33
|
+
// This is tested by the `livedata - publish callbacks clone` test
|
|
34
|
+
true);
|
|
35
|
+
// We don't call sub.ready() here: it gets called in livedata_server, after
|
|
36
|
+
// possibly calling _publishCursor on multiple returned cursors.
|
|
37
|
+
// register stop callback (expects lambda w/ no args).
|
|
38
|
+
sub.onStop(function () {
|
|
39
|
+
observeHandle.stop();
|
|
40
|
+
});
|
|
41
|
+
// return the observeHandle in case it needs to be stopped early
|
|
42
|
+
return observeHandle;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.LiveCursor = LiveCursor;
|
|
46
|
+
function _rewriteSelector(selector) {
|
|
47
|
+
if (Array.isArray(selector)) {
|
|
48
|
+
// This is consistent with the Mongo console itself; if we don't do this
|
|
49
|
+
// check passing an empty array ends up selecting all items
|
|
50
|
+
throw new Error("Mongo selector can't be an array.");
|
|
51
|
+
}
|
|
52
|
+
if (!selector || ('_id' in selector && !selector._id)) {
|
|
53
|
+
// can't match anything
|
|
54
|
+
return { _id: main_1.Random.id() };
|
|
55
|
+
}
|
|
56
|
+
return selector;
|
|
57
|
+
}
|