mongodb 3.2.5 → 3.3.0-beta2
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/HISTORY.md +0 -10
- package/index.js +4 -4
- package/lib/admin.js +56 -56
- package/lib/aggregation_cursor.js +7 -3
- package/lib/bulk/common.js +18 -13
- package/lib/change_stream.js +196 -89
- package/lib/collection.js +217 -169
- package/lib/command_cursor.js +17 -7
- package/lib/core/auth/auth_provider.js +158 -0
- package/lib/core/auth/defaultAuthProviders.js +29 -0
- package/lib/core/auth/gssapi.js +241 -0
- package/lib/core/auth/mongo_credentials.js +81 -0
- package/lib/core/auth/mongocr.js +51 -0
- package/lib/core/auth/plain.js +35 -0
- package/lib/core/auth/scram.js +293 -0
- package/lib/core/auth/sspi.js +131 -0
- package/lib/core/auth/x509.js +26 -0
- package/lib/core/connection/apm.js +236 -0
- package/lib/core/connection/command_result.js +36 -0
- package/lib/core/connection/commands.js +507 -0
- package/lib/core/connection/connect.js +370 -0
- package/lib/core/connection/connection.js +624 -0
- package/lib/core/connection/logger.js +246 -0
- package/lib/core/connection/msg.js +219 -0
- package/lib/core/connection/pool.js +1285 -0
- package/lib/core/connection/utils.js +57 -0
- package/lib/core/cursor.js +752 -0
- package/lib/core/error.js +186 -0
- package/lib/core/index.js +50 -0
- package/lib/core/sdam/monitoring.js +228 -0
- package/lib/core/sdam/server.js +467 -0
- package/lib/core/sdam/server_description.js +163 -0
- package/lib/core/sdam/server_selectors.js +244 -0
- package/lib/core/sdam/srv_polling.js +135 -0
- package/lib/core/sdam/topology.js +1151 -0
- package/lib/core/sdam/topology_description.js +408 -0
- package/lib/core/sessions.js +711 -0
- package/lib/core/tools/smoke_plugin.js +61 -0
- package/lib/core/topologies/mongos.js +1337 -0
- package/lib/core/topologies/read_preference.js +202 -0
- package/lib/core/topologies/replset.js +1507 -0
- package/lib/core/topologies/replset_state.js +1121 -0
- package/lib/core/topologies/server.js +984 -0
- package/lib/core/topologies/shared.js +453 -0
- package/lib/core/transactions.js +167 -0
- package/lib/core/uri_parser.js +631 -0
- package/lib/core/utils.js +165 -0
- package/lib/core/wireprotocol/command.js +170 -0
- package/lib/core/wireprotocol/compression.js +73 -0
- package/lib/core/wireprotocol/constants.js +13 -0
- package/lib/core/wireprotocol/get_more.js +86 -0
- package/lib/core/wireprotocol/index.js +18 -0
- package/lib/core/wireprotocol/kill_cursors.js +70 -0
- package/lib/core/wireprotocol/query.js +224 -0
- package/lib/core/wireprotocol/shared.js +115 -0
- package/lib/core/wireprotocol/write_command.js +50 -0
- package/lib/cursor.js +40 -46
- package/lib/db.js +141 -95
- package/lib/dynamic_loaders.js +32 -0
- package/lib/error.js +12 -10
- package/lib/gridfs/chunk.js +2 -2
- package/lib/gridfs/grid_store.js +31 -25
- package/lib/gridfs-stream/index.js +4 -4
- package/lib/gridfs-stream/upload.js +1 -1
- package/lib/mongo_client.js +37 -15
- package/lib/operations/add_user.js +96 -0
- package/lib/operations/aggregate.js +24 -13
- package/lib/operations/aggregate_operation.js +127 -0
- package/lib/operations/bulk_write.js +104 -0
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +28 -287
- package/lib/operations/collections.js +55 -0
- package/lib/operations/command.js +120 -0
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/common_functions.js +372 -0
- package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
- package/lib/operations/count.js +72 -0
- package/lib/operations/count_documents.js +46 -0
- package/lib/operations/create_collection.js +118 -0
- package/lib/operations/create_index.js +92 -0
- package/lib/operations/create_indexes.js +61 -0
- package/lib/operations/cursor_ops.js +3 -4
- package/lib/operations/db_ops.js +15 -12
- package/lib/operations/delete_many.js +25 -0
- package/lib/operations/delete_one.js +25 -0
- package/lib/operations/distinct.js +85 -0
- package/lib/operations/drop.js +53 -0
- package/lib/operations/drop_index.js +42 -0
- package/lib/operations/drop_indexes.js +23 -0
- package/lib/operations/estimated_document_count.js +33 -0
- package/lib/operations/execute_db_admin_command.js +34 -0
- package/lib/operations/execute_operation.js +165 -0
- package/lib/operations/explain.js +23 -0
- package/lib/operations/find_and_modify.js +98 -0
- package/lib/operations/find_one.js +33 -0
- package/lib/operations/find_one_and_delete.js +16 -0
- package/lib/operations/find_one_and_replace.js +18 -0
- package/lib/operations/find_one_and_update.js +19 -0
- package/lib/operations/geo_haystack_search.js +79 -0
- package/lib/operations/has_next.js +40 -0
- package/lib/operations/index_exists.js +39 -0
- package/lib/operations/index_information.js +23 -0
- package/lib/operations/indexes.js +22 -0
- package/lib/operations/insert_many.js +63 -0
- package/lib/operations/insert_one.js +75 -0
- package/lib/operations/is_capped.js +19 -0
- package/lib/operations/list_indexes.js +66 -0
- package/lib/operations/map_reduce.js +189 -0
- package/lib/operations/next.js +32 -0
- package/lib/operations/operation.js +63 -0
- package/lib/operations/options_operation.js +32 -0
- package/lib/operations/profiling_level.js +31 -0
- package/lib/operations/re_index.js +28 -0
- package/lib/operations/remove_user.js +52 -0
- package/lib/operations/rename.js +61 -0
- package/lib/operations/replace_one.js +47 -0
- package/lib/operations/set_profiling_level.js +48 -0
- package/lib/operations/stats.js +45 -0
- package/lib/operations/to_array.js +68 -0
- package/lib/operations/update_many.js +29 -0
- package/lib/operations/update_one.js +44 -0
- package/lib/operations/validate_collection.js +40 -0
- package/lib/read_concern.js +55 -0
- package/lib/topologies/mongos.js +3 -3
- package/lib/topologies/native_topology.js +22 -2
- package/lib/topologies/replset.js +3 -3
- package/lib/topologies/server.js +4 -4
- package/lib/topologies/topology_base.js +6 -6
- package/lib/url_parser.js +4 -3
- package/lib/utils.js +46 -59
- package/lib/write_concern.js +66 -0
- package/package.json +15 -6
- package/lib/.DS_Store +0 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Logger = require('./connection/logger');
|
|
4
|
+
const retrieveBSON = require('./connection/utils').retrieveBSON;
|
|
5
|
+
const MongoError = require('./error').MongoError;
|
|
6
|
+
const MongoNetworkError = require('./error').MongoNetworkError;
|
|
7
|
+
const mongoErrorContextSymbol = require('./error').mongoErrorContextSymbol;
|
|
8
|
+
const f = require('util').format;
|
|
9
|
+
const collationNotSupported = require('./utils').collationNotSupported;
|
|
10
|
+
const ReadPreference = require('./topologies/read_preference');
|
|
11
|
+
const isUnifiedTopology = require('./utils').isUnifiedTopology;
|
|
12
|
+
|
|
13
|
+
const BSON = retrieveBSON();
|
|
14
|
+
const Long = BSON.Long;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This is a cursor results callback
|
|
18
|
+
*
|
|
19
|
+
* @callback resultCallback
|
|
20
|
+
* @param {error} error An error object. Set to null if no error present
|
|
21
|
+
* @param {object} document
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @fileOverview The **Cursor** class is an internal class that embodies a cursor on MongoDB
|
|
26
|
+
* allowing for iteration over the results returned from the underlying query.
|
|
27
|
+
*
|
|
28
|
+
* **CURSORS Cannot directly be instantiated**
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new Cursor, not to be used directly
|
|
33
|
+
* @class
|
|
34
|
+
* @param {object} topology The server topology instance.
|
|
35
|
+
* @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
|
|
36
|
+
* @param {{object}|Long} cmd The selector (can be a command or a cursorId)
|
|
37
|
+
* @param {object} [options=null] Optional settings.
|
|
38
|
+
* @param {object} [options.batchSize=1000] Batchsize for the operation
|
|
39
|
+
* @param {array} [options.documents=[]] Initial documents list for cursor
|
|
40
|
+
* @param {object} [options.transforms=null] Transform methods for the cursor results
|
|
41
|
+
* @param {function} [options.transforms.query] Transform the value returned from the initial query
|
|
42
|
+
* @param {function} [options.transforms.doc] Transform each document returned from Cursor.prototype.next
|
|
43
|
+
* @return {Cursor} A cursor instance
|
|
44
|
+
* @property {number} cursorBatchSize The current cursorBatchSize for the cursor
|
|
45
|
+
* @property {number} cursorLimit The current cursorLimit for the cursor
|
|
46
|
+
* @property {number} cursorSkip The current cursorSkip for the cursor
|
|
47
|
+
*/
|
|
48
|
+
var Cursor = function(topology, ns, cmd, options) {
|
|
49
|
+
options = options || {};
|
|
50
|
+
|
|
51
|
+
// Cursor pool
|
|
52
|
+
this.pool = null;
|
|
53
|
+
// Cursor server
|
|
54
|
+
this.server = null;
|
|
55
|
+
|
|
56
|
+
// Do we have a not connected handler
|
|
57
|
+
this.disconnectHandler = options.disconnectHandler;
|
|
58
|
+
|
|
59
|
+
// Set local values
|
|
60
|
+
this.bson = topology.s.bson;
|
|
61
|
+
this.ns = ns;
|
|
62
|
+
this.cmd = cmd;
|
|
63
|
+
this.options = options;
|
|
64
|
+
this.topology = topology;
|
|
65
|
+
|
|
66
|
+
// All internal state
|
|
67
|
+
this.cursorState = {
|
|
68
|
+
cursorId: null,
|
|
69
|
+
cmd: cmd,
|
|
70
|
+
documents: options.documents || [],
|
|
71
|
+
cursorIndex: 0,
|
|
72
|
+
dead: false,
|
|
73
|
+
killed: false,
|
|
74
|
+
init: false,
|
|
75
|
+
notified: false,
|
|
76
|
+
limit: options.limit || cmd.limit || 0,
|
|
77
|
+
skip: options.skip || cmd.skip || 0,
|
|
78
|
+
batchSize: options.batchSize || cmd.batchSize || 1000,
|
|
79
|
+
currentLimit: 0,
|
|
80
|
+
// Result field name if not a cursor (contains the array of results)
|
|
81
|
+
transforms: options.transforms,
|
|
82
|
+
raw: options.raw || (cmd && cmd.raw)
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (typeof options.session === 'object') {
|
|
86
|
+
this.cursorState.session = options.session;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Add promoteLong to cursor state
|
|
90
|
+
const topologyOptions = topology.s.options;
|
|
91
|
+
if (typeof topologyOptions.promoteLongs === 'boolean') {
|
|
92
|
+
this.cursorState.promoteLongs = topologyOptions.promoteLongs;
|
|
93
|
+
} else if (typeof options.promoteLongs === 'boolean') {
|
|
94
|
+
this.cursorState.promoteLongs = options.promoteLongs;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add promoteValues to cursor state
|
|
98
|
+
if (typeof topologyOptions.promoteValues === 'boolean') {
|
|
99
|
+
this.cursorState.promoteValues = topologyOptions.promoteValues;
|
|
100
|
+
} else if (typeof options.promoteValues === 'boolean') {
|
|
101
|
+
this.cursorState.promoteValues = options.promoteValues;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add promoteBuffers to cursor state
|
|
105
|
+
if (typeof topologyOptions.promoteBuffers === 'boolean') {
|
|
106
|
+
this.cursorState.promoteBuffers = topologyOptions.promoteBuffers;
|
|
107
|
+
} else if (typeof options.promoteBuffers === 'boolean') {
|
|
108
|
+
this.cursorState.promoteBuffers = options.promoteBuffers;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (topologyOptions.reconnect) {
|
|
112
|
+
this.cursorState.reconnect = topologyOptions.reconnect;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Logger
|
|
116
|
+
this.logger = Logger('Cursor', topologyOptions);
|
|
117
|
+
|
|
118
|
+
//
|
|
119
|
+
// Did we pass in a cursor id
|
|
120
|
+
if (typeof cmd === 'number') {
|
|
121
|
+
this.cursorState.cursorId = Long.fromNumber(cmd);
|
|
122
|
+
this.cursorState.lastCursorId = this.cursorState.cursorId;
|
|
123
|
+
} else if (cmd instanceof Long) {
|
|
124
|
+
this.cursorState.cursorId = cmd;
|
|
125
|
+
this.cursorState.lastCursorId = cmd;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
Cursor.prototype.setCursorBatchSize = function(value) {
|
|
130
|
+
this.cursorState.batchSize = value;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
Cursor.prototype.cursorBatchSize = function() {
|
|
134
|
+
return this.cursorState.batchSize;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
Cursor.prototype.setCursorLimit = function(value) {
|
|
138
|
+
this.cursorState.limit = value;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
Cursor.prototype.cursorLimit = function() {
|
|
142
|
+
return this.cursorState.limit;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
Cursor.prototype.setCursorSkip = function(value) {
|
|
146
|
+
this.cursorState.skip = value;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
Cursor.prototype.cursorSkip = function() {
|
|
150
|
+
return this.cursorState.skip;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
Cursor.prototype._endSession = function(options, callback) {
|
|
154
|
+
if (typeof options === 'function') {
|
|
155
|
+
callback = options;
|
|
156
|
+
options = {};
|
|
157
|
+
}
|
|
158
|
+
options = options || {};
|
|
159
|
+
|
|
160
|
+
const session = this.cursorState.session;
|
|
161
|
+
|
|
162
|
+
if (session && (options.force || session.owner === this)) {
|
|
163
|
+
this.cursorState.session = undefined;
|
|
164
|
+
session.endSession(callback);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (callback) {
|
|
169
|
+
callback();
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
//
|
|
175
|
+
// Handle callback (including any exceptions thrown)
|
|
176
|
+
var handleCallback = function(callback, err, result) {
|
|
177
|
+
try {
|
|
178
|
+
callback(err, result);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
process.nextTick(function() {
|
|
181
|
+
throw err;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Internal methods
|
|
187
|
+
Cursor.prototype._getMore = function(callback) {
|
|
188
|
+
if (this.logger.isDebug())
|
|
189
|
+
this.logger.debug(f('schedule getMore call for query [%s]', JSON.stringify(this.query)));
|
|
190
|
+
|
|
191
|
+
// Set the current batchSize
|
|
192
|
+
var batchSize = this.cursorState.batchSize;
|
|
193
|
+
if (
|
|
194
|
+
this.cursorState.limit > 0 &&
|
|
195
|
+
this.cursorState.currentLimit + batchSize > this.cursorState.limit
|
|
196
|
+
) {
|
|
197
|
+
batchSize = this.cursorState.limit - this.cursorState.currentLimit;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.server.getMore(this.ns, this.cursorState, batchSize, this.options, callback);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Clone the cursor
|
|
205
|
+
* @method
|
|
206
|
+
* @return {Cursor}
|
|
207
|
+
*/
|
|
208
|
+
Cursor.prototype.clone = function() {
|
|
209
|
+
return this.topology.cursor(this.ns, this.cmd, this.options);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Checks if the cursor is dead
|
|
214
|
+
* @method
|
|
215
|
+
* @return {boolean} A boolean signifying if the cursor is dead or not
|
|
216
|
+
*/
|
|
217
|
+
Cursor.prototype.isDead = function() {
|
|
218
|
+
return this.cursorState.dead === true;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Checks if the cursor was killed by the application
|
|
223
|
+
* @method
|
|
224
|
+
* @return {boolean} A boolean signifying if the cursor was killed by the application
|
|
225
|
+
*/
|
|
226
|
+
Cursor.prototype.isKilled = function() {
|
|
227
|
+
return this.cursorState.killed === true;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Checks if the cursor notified it's caller about it's death
|
|
232
|
+
* @method
|
|
233
|
+
* @return {boolean} A boolean signifying if the cursor notified the callback
|
|
234
|
+
*/
|
|
235
|
+
Cursor.prototype.isNotified = function() {
|
|
236
|
+
return this.cursorState.notified === true;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Returns current buffered documents length
|
|
241
|
+
* @method
|
|
242
|
+
* @return {number} The number of items in the buffered documents
|
|
243
|
+
*/
|
|
244
|
+
Cursor.prototype.bufferedCount = function() {
|
|
245
|
+
return this.cursorState.documents.length - this.cursorState.cursorIndex;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Returns current buffered documents
|
|
250
|
+
* @method
|
|
251
|
+
* @return {Array} An array of buffered documents
|
|
252
|
+
*/
|
|
253
|
+
Cursor.prototype.readBufferedDocuments = function(number) {
|
|
254
|
+
var unreadDocumentsLength = this.cursorState.documents.length - this.cursorState.cursorIndex;
|
|
255
|
+
var length = number < unreadDocumentsLength ? number : unreadDocumentsLength;
|
|
256
|
+
var elements = this.cursorState.documents.slice(
|
|
257
|
+
this.cursorState.cursorIndex,
|
|
258
|
+
this.cursorState.cursorIndex + length
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Transform the doc with passed in transformation method if provided
|
|
262
|
+
if (this.cursorState.transforms && typeof this.cursorState.transforms.doc === 'function') {
|
|
263
|
+
// Transform all the elements
|
|
264
|
+
for (var i = 0; i < elements.length; i++) {
|
|
265
|
+
elements[i] = this.cursorState.transforms.doc(elements[i]);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Ensure we do not return any more documents than the limit imposed
|
|
270
|
+
// Just return the number of elements up to the limit
|
|
271
|
+
if (
|
|
272
|
+
this.cursorState.limit > 0 &&
|
|
273
|
+
this.cursorState.currentLimit + elements.length > this.cursorState.limit
|
|
274
|
+
) {
|
|
275
|
+
elements = elements.slice(0, this.cursorState.limit - this.cursorState.currentLimit);
|
|
276
|
+
this.kill();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Adjust current limit
|
|
280
|
+
this.cursorState.currentLimit = this.cursorState.currentLimit + elements.length;
|
|
281
|
+
this.cursorState.cursorIndex = this.cursorState.cursorIndex + elements.length;
|
|
282
|
+
|
|
283
|
+
// Return elements
|
|
284
|
+
return elements;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Kill the cursor
|
|
289
|
+
* @method
|
|
290
|
+
* @param {resultCallback} callback A callback function
|
|
291
|
+
*/
|
|
292
|
+
Cursor.prototype.kill = function(callback) {
|
|
293
|
+
// Set cursor to dead
|
|
294
|
+
this.cursorState.dead = true;
|
|
295
|
+
this.cursorState.killed = true;
|
|
296
|
+
// Remove documents
|
|
297
|
+
this.cursorState.documents = [];
|
|
298
|
+
|
|
299
|
+
// If no cursor id just return
|
|
300
|
+
if (
|
|
301
|
+
this.cursorState.cursorId == null ||
|
|
302
|
+
this.cursorState.cursorId.isZero() ||
|
|
303
|
+
this.cursorState.init === false
|
|
304
|
+
) {
|
|
305
|
+
if (callback) callback(null, null);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.server.killCursors(this.ns, this.cursorState, callback);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Resets the cursor
|
|
314
|
+
* @method
|
|
315
|
+
* @return {null}
|
|
316
|
+
*/
|
|
317
|
+
Cursor.prototype.rewind = function() {
|
|
318
|
+
if (this.cursorState.init) {
|
|
319
|
+
if (!this.cursorState.dead) {
|
|
320
|
+
this.kill();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.cursorState.currentLimit = 0;
|
|
324
|
+
this.cursorState.init = false;
|
|
325
|
+
this.cursorState.dead = false;
|
|
326
|
+
this.cursorState.killed = false;
|
|
327
|
+
this.cursorState.notified = false;
|
|
328
|
+
this.cursorState.documents = [];
|
|
329
|
+
this.cursorState.cursorId = null;
|
|
330
|
+
this.cursorState.cursorIndex = 0;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Validate if the pool is dead and return error
|
|
336
|
+
*/
|
|
337
|
+
var isConnectionDead = function(self, callback) {
|
|
338
|
+
if (self.pool && self.pool.isDestroyed()) {
|
|
339
|
+
self.cursorState.killed = true;
|
|
340
|
+
const err = new MongoNetworkError(
|
|
341
|
+
f('connection to host %s:%s was destroyed', self.pool.host, self.pool.port)
|
|
342
|
+
);
|
|
343
|
+
_setCursorNotifiedImpl(self, () => callback(err));
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return false;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Validate if the cursor is dead but was not explicitly killed by user
|
|
352
|
+
*/
|
|
353
|
+
var isCursorDeadButNotkilled = function(self, callback) {
|
|
354
|
+
// Cursor is dead but not marked killed, return null
|
|
355
|
+
if (self.cursorState.dead && !self.cursorState.killed) {
|
|
356
|
+
self.cursorState.killed = true;
|
|
357
|
+
setCursorNotified(self, callback);
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return false;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Validate if the cursor is dead and was killed by user
|
|
366
|
+
*/
|
|
367
|
+
var isCursorDeadAndKilled = function(self, callback) {
|
|
368
|
+
if (self.cursorState.dead && self.cursorState.killed) {
|
|
369
|
+
handleCallback(callback, new MongoError('cursor is dead'));
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return false;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Validate if the cursor was killed by the user
|
|
378
|
+
*/
|
|
379
|
+
var isCursorKilled = function(self, callback) {
|
|
380
|
+
if (self.cursorState.killed) {
|
|
381
|
+
setCursorNotified(self, callback);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return false;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Mark cursor as being dead and notified
|
|
390
|
+
*/
|
|
391
|
+
var setCursorDeadAndNotified = function(self, callback) {
|
|
392
|
+
self.cursorState.dead = true;
|
|
393
|
+
setCursorNotified(self, callback);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Mark cursor as being notified
|
|
398
|
+
*/
|
|
399
|
+
var setCursorNotified = function(self, callback) {
|
|
400
|
+
_setCursorNotifiedImpl(self, () => handleCallback(callback, null, null));
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
var _setCursorNotifiedImpl = function(self, callback) {
|
|
404
|
+
self.cursorState.notified = true;
|
|
405
|
+
self.cursorState.documents = [];
|
|
406
|
+
self.cursorState.cursorIndex = 0;
|
|
407
|
+
if (self._endSession) {
|
|
408
|
+
return self._endSession(undefined, () => callback());
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return callback();
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
var nextFunction = function(self, callback) {
|
|
415
|
+
// We have notified about it
|
|
416
|
+
if (self.cursorState.notified) {
|
|
417
|
+
return callback(new Error('cursor is exhausted'));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Cursor is killed return null
|
|
421
|
+
if (isCursorKilled(self, callback)) return;
|
|
422
|
+
|
|
423
|
+
// Cursor is dead but not marked killed, return null
|
|
424
|
+
if (isCursorDeadButNotkilled(self, callback)) return;
|
|
425
|
+
|
|
426
|
+
// We have a dead and killed cursor, attempting to call next should error
|
|
427
|
+
if (isCursorDeadAndKilled(self, callback)) return;
|
|
428
|
+
|
|
429
|
+
// We have just started the cursor
|
|
430
|
+
if (!self.cursorState.init) {
|
|
431
|
+
// Topology is not connected, save the call in the provided store to be
|
|
432
|
+
// Executed at some point when the handler deems it's reconnected
|
|
433
|
+
if (!self.topology.isConnected(self.options)) {
|
|
434
|
+
// Only need this for single server, because repl sets and mongos
|
|
435
|
+
// will always continue trying to reconnect
|
|
436
|
+
if (self.topology._type === 'server' && !self.topology.s.options.reconnect) {
|
|
437
|
+
// Reconnect is disabled, so we'll never reconnect
|
|
438
|
+
return callback(new MongoError('no connection available'));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (self.disconnectHandler != null) {
|
|
442
|
+
if (self.topology.isDestroyed()) {
|
|
443
|
+
// Topology was destroyed, so don't try to wait for it to reconnect
|
|
444
|
+
return callback(new MongoError('Topology was destroyed'));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
self.disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
self._initializeCursor((err, result) => {
|
|
453
|
+
if (err || result === null) {
|
|
454
|
+
callback(err, result);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
nextFunction(self, callback);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
|
|
465
|
+
// Ensure we kill the cursor on the server
|
|
466
|
+
self.kill();
|
|
467
|
+
// Set cursor in dead and notified state
|
|
468
|
+
return setCursorDeadAndNotified(self, callback);
|
|
469
|
+
} else if (
|
|
470
|
+
self.cursorState.cursorIndex === self.cursorState.documents.length &&
|
|
471
|
+
!Long.ZERO.equals(self.cursorState.cursorId)
|
|
472
|
+
) {
|
|
473
|
+
// Ensure an empty cursor state
|
|
474
|
+
self.cursorState.documents = [];
|
|
475
|
+
self.cursorState.cursorIndex = 0;
|
|
476
|
+
|
|
477
|
+
// Check if topology is destroyed
|
|
478
|
+
if (self.topology.isDestroyed())
|
|
479
|
+
return callback(
|
|
480
|
+
new MongoNetworkError('connection destroyed, not possible to instantiate cursor')
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
// Check if connection is dead and return if not possible to
|
|
484
|
+
// execute a getMore on this connection
|
|
485
|
+
if (isConnectionDead(self, callback)) return;
|
|
486
|
+
|
|
487
|
+
// Execute the next get more
|
|
488
|
+
self._getMore(function(err, doc, connection) {
|
|
489
|
+
if (err) {
|
|
490
|
+
if (err instanceof MongoError) {
|
|
491
|
+
err[mongoErrorContextSymbol].isGetMore = true;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return handleCallback(callback, err);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (self.cursorState.cursorId && self.cursorState.cursorId.isZero() && self._endSession) {
|
|
498
|
+
self._endSession();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Save the returned connection to ensure all getMore's fire over the same connection
|
|
502
|
+
self.connection = connection;
|
|
503
|
+
|
|
504
|
+
// Tailable cursor getMore result, notify owner about it
|
|
505
|
+
// No attempt is made here to retry, this is left to the user of the
|
|
506
|
+
// core module to handle to keep core simple
|
|
507
|
+
if (
|
|
508
|
+
self.cursorState.documents.length === 0 &&
|
|
509
|
+
self.cmd.tailable &&
|
|
510
|
+
Long.ZERO.equals(self.cursorState.cursorId)
|
|
511
|
+
) {
|
|
512
|
+
// No more documents in the tailed cursor
|
|
513
|
+
return handleCallback(
|
|
514
|
+
callback,
|
|
515
|
+
new MongoError({
|
|
516
|
+
message: 'No more documents in tailed cursor',
|
|
517
|
+
tailable: self.cmd.tailable,
|
|
518
|
+
awaitData: self.cmd.awaitData
|
|
519
|
+
})
|
|
520
|
+
);
|
|
521
|
+
} else if (
|
|
522
|
+
self.cursorState.documents.length === 0 &&
|
|
523
|
+
self.cmd.tailable &&
|
|
524
|
+
!Long.ZERO.equals(self.cursorState.cursorId)
|
|
525
|
+
) {
|
|
526
|
+
return nextFunction(self, callback);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
|
|
530
|
+
return setCursorDeadAndNotified(self, callback);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
nextFunction(self, callback);
|
|
534
|
+
});
|
|
535
|
+
} else if (
|
|
536
|
+
self.cursorState.documents.length === self.cursorState.cursorIndex &&
|
|
537
|
+
self.cmd.tailable &&
|
|
538
|
+
Long.ZERO.equals(self.cursorState.cursorId)
|
|
539
|
+
) {
|
|
540
|
+
return handleCallback(
|
|
541
|
+
callback,
|
|
542
|
+
new MongoError({
|
|
543
|
+
message: 'No more documents in tailed cursor',
|
|
544
|
+
tailable: self.cmd.tailable,
|
|
545
|
+
awaitData: self.cmd.awaitData
|
|
546
|
+
})
|
|
547
|
+
);
|
|
548
|
+
} else if (
|
|
549
|
+
self.cursorState.documents.length === self.cursorState.cursorIndex &&
|
|
550
|
+
Long.ZERO.equals(self.cursorState.cursorId)
|
|
551
|
+
) {
|
|
552
|
+
setCursorDeadAndNotified(self, callback);
|
|
553
|
+
} else {
|
|
554
|
+
if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
|
|
555
|
+
// Ensure we kill the cursor on the server
|
|
556
|
+
self.kill();
|
|
557
|
+
// Set cursor in dead and notified state
|
|
558
|
+
return setCursorDeadAndNotified(self, callback);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Increment the current cursor limit
|
|
562
|
+
self.cursorState.currentLimit += 1;
|
|
563
|
+
|
|
564
|
+
// Get the document
|
|
565
|
+
var doc = self.cursorState.documents[self.cursorState.cursorIndex++];
|
|
566
|
+
|
|
567
|
+
// Doc overflow
|
|
568
|
+
if (!doc || doc.$err) {
|
|
569
|
+
// Ensure we kill the cursor on the server
|
|
570
|
+
self.kill();
|
|
571
|
+
// Set cursor in dead and notified state
|
|
572
|
+
return setCursorDeadAndNotified(self, function() {
|
|
573
|
+
handleCallback(callback, new MongoError(doc ? doc.$err : undefined));
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Transform the doc with passed in transformation method if provided
|
|
578
|
+
if (self.cursorState.transforms && typeof self.cursorState.transforms.doc === 'function') {
|
|
579
|
+
doc = self.cursorState.transforms.doc(doc);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Return the document
|
|
583
|
+
handleCallback(callback, null, doc);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
Cursor.prototype._initializeCursor = function(callback) {
|
|
588
|
+
const cursor = this;
|
|
589
|
+
|
|
590
|
+
// NOTE: this goes away once cursors use `executeOperation`
|
|
591
|
+
if (isUnifiedTopology(cursor.topology) && cursor.topology.shouldCheckForSessionSupport()) {
|
|
592
|
+
cursor.topology.selectServer(ReadPreference.primaryPreferred, err => {
|
|
593
|
+
if (err) {
|
|
594
|
+
callback(err);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
cursor.next(callback);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Very explicitly choose what is passed to selectServer
|
|
605
|
+
const serverSelectOptions = {};
|
|
606
|
+
if (cursor.cursorState.session) {
|
|
607
|
+
serverSelectOptions.session = cursor.cursorState.session;
|
|
608
|
+
}
|
|
609
|
+
if (cursor.options.readPreference) {
|
|
610
|
+
serverSelectOptions.readPreference = cursor.options.readPreference;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return cursor.topology.selectServer(serverSelectOptions, (err, server) => {
|
|
614
|
+
if (err) {
|
|
615
|
+
const disconnectHandler = cursor.disconnectHandler;
|
|
616
|
+
if (disconnectHandler != null) {
|
|
617
|
+
return disconnectHandler.addObjectAndMethod('cursor', cursor, 'next', [callback], callback);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return callback(err);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
cursor.server = server;
|
|
624
|
+
cursor.cursorState.init = true;
|
|
625
|
+
if (collationNotSupported(cursor.server, cursor.cmd)) {
|
|
626
|
+
return callback(new MongoError(`server ${cursor.server.name} does not support collation`));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function done(err, result) {
|
|
630
|
+
if (
|
|
631
|
+
cursor.cursorState.cursorId &&
|
|
632
|
+
cursor.cursorState.cursorId.isZero() &&
|
|
633
|
+
cursor._endSession
|
|
634
|
+
) {
|
|
635
|
+
cursor._endSession();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (
|
|
639
|
+
cursor.cursorState.documents.length === 0 &&
|
|
640
|
+
cursor.cursorState.cursorId &&
|
|
641
|
+
cursor.cursorState.cursorId.isZero() &&
|
|
642
|
+
!cursor.cmd.tailable &&
|
|
643
|
+
!cursor.cmd.awaitData
|
|
644
|
+
) {
|
|
645
|
+
return setCursorNotified(cursor, callback);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
callback(err, result);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// NOTE: this is a special internal method for cloning a cursor, consider removing
|
|
652
|
+
if (cursor.cursorState.cursorId != null) {
|
|
653
|
+
return done();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const queryCallback = (err, r) => {
|
|
657
|
+
if (err) {
|
|
658
|
+
return done(err);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const result = r.message;
|
|
662
|
+
if (result.queryFailure) {
|
|
663
|
+
return done(new MongoError(result.documents[0]), null);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Check if we have a command cursor
|
|
667
|
+
if (
|
|
668
|
+
Array.isArray(result.documents) &&
|
|
669
|
+
result.documents.length === 1 &&
|
|
670
|
+
(!cursor.cmd.find || (cursor.cmd.find && cursor.cmd.virtual === false)) &&
|
|
671
|
+
(typeof result.documents[0].cursor !== 'string' ||
|
|
672
|
+
result.documents[0]['$err'] ||
|
|
673
|
+
result.documents[0]['errmsg'] ||
|
|
674
|
+
Array.isArray(result.documents[0].result))
|
|
675
|
+
) {
|
|
676
|
+
// We have an error document, return the error
|
|
677
|
+
if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
|
|
678
|
+
return done(new MongoError(result.documents[0]), null);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// We have a cursor document
|
|
682
|
+
if (result.documents[0].cursor != null && typeof result.documents[0].cursor !== 'string') {
|
|
683
|
+
var id = result.documents[0].cursor.id;
|
|
684
|
+
// If we have a namespace change set the new namespace for getmores
|
|
685
|
+
if (result.documents[0].cursor.ns) {
|
|
686
|
+
cursor.ns = result.documents[0].cursor.ns;
|
|
687
|
+
}
|
|
688
|
+
// Promote id to long if needed
|
|
689
|
+
cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
|
|
690
|
+
cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
|
|
691
|
+
cursor.cursorState.operationTime = result.documents[0].operationTime;
|
|
692
|
+
|
|
693
|
+
// If we have a firstBatch set it
|
|
694
|
+
if (Array.isArray(result.documents[0].cursor.firstBatch)) {
|
|
695
|
+
cursor.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Return after processing command cursor
|
|
699
|
+
return done(null, result);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (Array.isArray(result.documents[0].result)) {
|
|
703
|
+
cursor.cursorState.documents = result.documents[0].result;
|
|
704
|
+
cursor.cursorState.cursorId = Long.ZERO;
|
|
705
|
+
return done(null, result);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Otherwise fall back to regular find path
|
|
710
|
+
const cursorId = result.cursorId || 0;
|
|
711
|
+
cursor.cursorState.cursorId = cursorId instanceof Long ? cursorId : Long.fromNumber(cursorId);
|
|
712
|
+
cursor.cursorState.documents = result.documents;
|
|
713
|
+
cursor.cursorState.lastCursorId = result.cursorId;
|
|
714
|
+
|
|
715
|
+
// Transform the results with passed in transformation method if provided
|
|
716
|
+
if (
|
|
717
|
+
cursor.cursorState.transforms &&
|
|
718
|
+
typeof cursor.cursorState.transforms.query === 'function'
|
|
719
|
+
) {
|
|
720
|
+
cursor.cursorState.documents = cursor.cursorState.transforms.query(result);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
done(null, result);
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
if (cursor.logger.isDebug()) {
|
|
727
|
+
cursor.logger.debug(
|
|
728
|
+
`issue initial query [${JSON.stringify(cursor.cmd)}] with flags [${JSON.stringify(
|
|
729
|
+
cursor.query
|
|
730
|
+
)}]`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (cursor.cmd.find != null) {
|
|
735
|
+
server.query(cursor.ns, cursor.cmd, cursor.cursorState, cursor.options, queryCallback);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
server.command(cursor.ns, cursor.cmd, cursor.options, queryCallback);
|
|
740
|
+
});
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Retrieve the next document from the cursor
|
|
745
|
+
* @method
|
|
746
|
+
* @param {resultCallback} callback A callback function
|
|
747
|
+
*/
|
|
748
|
+
Cursor.prototype.next = function(callback) {
|
|
749
|
+
nextFunction(this, callback);
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
module.exports = Cursor;
|