Cursor.prototype.rewind = function() { var self = this; if (self.state != Cursor.INIT) { if (self.state != Cursor.CLOSED) { self.close(function() {}); } self.numberOfReturned = 0; self.totalNumberOfRecords = 0; self.items = []; self.cursorId = Long.fromInt(0); self.state = Cursor.INIT; self.queryRun = false; } return self; };
db.command(command, _options, function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Retrieve the cursor id cursorId = result.cursor.id; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Get the first batch results items = result.cursor.firstBatch; // We have items return the first one if(items.length > 0) { callback(null, items.shift()); } else { state = 'closed'; callback(null, null); } });
db.command(command, _options, function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Retrieve the cursor id cursorId = result.cursor.id; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Validate cursorId if(cursorId.equals(zeroCursor)) { return callback(null, result.cursor.firstBatch); }; // Add to the items items = result.cursor.firstBatch; // Execute the getMore exhaustGetMore(callback); });
var queryCallback = function (err, r) { if (err) return callback(err); // If we have a timed out query or a cursor that was killed if ((r.responseFlags & (1 << 0)) != 0) { return callback(new MongoError("cursor killed or timed out"), null); } // Ensure we have a Long valie cursor id var cursorId = typeof r.cursorId == 'number' ? Long.fromNumber(r.cursorId) : r.cursorId; // Set all the values cursorState.documents = r.documents; cursorState.cursorId = cursorId; // Return callback(null, null, r.connection); }
getMore(function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Set the cursor id cursorId = result.cursorId; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Add the items items = items.concat(result.documents); // If no more items if(items.length == 0) { state = 'closed'; return callback(null, null); } // Return the item return callback(null, items.shift()); })
test.server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster) { request.reply( Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 6 }) ); } else if (doc.find) { request.reply({ cursor: { id: Long.fromNumber(1), ns: 'test.test', firstBatch: [] }, ok: 1 }); } else if (doc.getMore) { request.reply(errdoc); } });
Cursor.prototype.nextObject = function(callback) { var self = this; if(self.state == Cursor.INIT) { var cmd; try { cmd = generateQueryCommand(self); } catch (err) { return callback(err, null); } var commandHandler = function(err, result) { if(err != null && result == null) return callback(err, null); if(!err && result.documents[0] && result.documents[0]['$err']) { return self.close(function() {callback(result.documents[0]['$err'], null);}); } self.queryRun = true; self.state = Cursor.OPEN; // Adjust the state of the cursor self.cursorId = result.cursorId; self.totalNumberOfRecords = result.numberReturned; // Add the new documents to the list of items self.items = self.items.concat(result.documents); self.nextObject(callback); result = null; }; self.db._executeQueryCommand(cmd, {read:self.read, raw:self.raw}, commandHandler); commandHandler = null; } else if(self.items.length) { callback(null, self.items.shift()); } else if(self.cursorId.greaterThan(Long.fromInt(0))) { getMore(self, callback); } else { // Force cursor to stay open return self.close(function() {callback(null, null);}); } }
getMore(function(err, result) { if(err) { items = []; state = 'closed'; return callback(err, null); } // Add the items items = items.concat(result.documents); // Set the cursor id cursorId = result.cursorId; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // If the cursor is done if(result.cursorId.equals(zeroCursor)) { return callback(null, items); } // Check the cursor id exhaustGetMore(callback); });
var queryCallback = function(err, r) { if(err) return callback(err); // If we have a timed out query or a cursor that was killed if((r.responseFlags & (1 << 0)) != 0) { return callback(new MongoError("cursor killed or timed out"), null); } // Raw, return all the extracted documents if(raw) { cursorState.documents = r.documents; cursorState.cursorId = r.cursorId; return callback(null, r.documents); } // // Check if we have any errors // if((Array.isArray(r.documents) || r.documents.length == 0)) { // console.log("@@@@@@@@@@@@@@@@@@@ YO") // console.dir(r.cursorId) // // return callback(new MongoError(f('invalid getMore result returned for cursor id %s', cursorState.cursorId))); // } // We have an error detected if(r.documents[0].ok == 0) { return callback(MongoError.create(r.documents[0])); } // Ensure we have a Long valid cursor id var cursorId = typeof r.documents[0].cursor.id == 'number' ? Long.fromNumber(r.documents[0].cursor.id) : r.documents[0].cursor.id; // Set all the values cursorState.documents = r.documents[0].cursor.nextBatch; cursorState.cursorId = cursorId; // Return the result callback(null, r.documents[0]); }
this.db.command(commandObject, options, function (err, result) { if (err) return callback(err, null); if (result == null) return callback(new Error("no result returned for parallelCollectionScan"), null); var cursors = []; // Create command cursors for each item for (var i = 0; i < result.cursors.length; i++) { var rawId = result.cursors[i].cursor.id // Convert cursorId to Long if needed var cursorId = typeof rawId == 'number' ? Long.fromNumber(rawId) : rawId; // Command cursor options var commandOptions = { batchSize: options.batchSize, cursorId: cursorId, items: result.cursors[i].cursor.firstBatch } // Add a command cursor cursors.push(new CommandCursor(self.db, self, {}, commandOptions)); } callback(null, cursors); });
/** * Constructor for a cursor object that handles all the operations on query result * using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly, * but use find to acquire a cursor. * * @class Represents a Cursor. * @param {Db} db the database object to work with. * @param {Collection} collection the collection to query. * @param {Object} selector the query selector. * @param {Object} fields an object containing what fields to include or exclude from objects returned. * @param {Number} skip number of documents to skip. * @param {Number} limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1. * @param {String|Array|Object} sort the required sorting for the query. * @param {Object} hint force the query to use a specific index. * @param {Boolean} explain return the explaination of the query. * @param {Boolean} snapshot Snapshot mode assures no duplicates are returned. * @param {Boolean} timeout allow the query to timeout. * @param {Boolean} tailable allow the cursor to be tailable. * @param {Number} batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database. * @param {Boolean} raw return all query documents as raw buffers (default false). * @param {Boolean} read specify override of read from source (primary/secondary). * @param {Boolean} returnKey only return the index key. * @param {Number} maxScan limit the number of items to scan. * @param {Number} min set index bounds. * @param {Number} max set index bounds. * @param {Boolean} showDiskLoc show disk location of results. * @param {String} comment you can put a $comment field on a query to make looking in the profiler logs simpler. */ function Cursor(db, collection, selector, fields, skip, limit , sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read , returnKey, maxScan, min, max, showDiskLoc, comment) { this.db = db; this.collection = collection; this.selector = selector; this.fields = fields; this.skipValue = skip == null ? 0 : skip; this.limitValue = limit == null ? 0 : limit; this.sortValue = sort; this.hint = hint; this.explainValue = explain; this.snapshot = snapshot; this.timeout = timeout == null ? true : timeout; this.tailable = tailable; this.batchSizeValue = batchSize == null ? 0 : batchSize; this.slaveOk = slaveOk == null ? collection.slaveOk : slaveOk; this.raw = raw == null ? false : raw; this.read = read == null ? true : read; this.returnKey = returnKey; this.maxScan = maxScan; this.min = min; this.max = max; this.showDiskLoc = showDiskLoc; this.comment = comment; this.totalNumberOfRecords = 0; this.items = []; this.cursorId = Long.fromInt(0); // State variables for the cursor this.state = Cursor.INIT; // Keep track of the current query run this.queryRun = false; this.getMoreTimer = false; this.collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collection.collectionName; };
var queryCallback = function(err, result) { if(err) return callback(err); // Check if we have a command cursor if(Array.isArray(result.documents) && result.documents.length == 1) { if(result.documents[0]['$err'] || result.documents[0]['errmsg']) { return callback(new MongoError(result.documents[0]), null); } if(result.documents[0].cursor != null && typeof result.documents[0].cursor != 'string') { var id = result.documents[0].cursor.id; // Promote id to long if needed cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id; // If we have a firstBatch set it if(Array.isArray(result.documents[0].cursor.firstBatch)) { cursorState.documents = result.documents[0].cursor.firstBatch; } // Return after processing command cursor return callback(null, null); } if(Array.isArray(result.documents[0].result)) { cursorState.documents = result.documents[0].result; cursorState.cursorId = Long.ZERO; return callback(null, null); } } // Otherwise fall back to regular find path cursorState.cursorId = result.cursorId; cursorState.documents = result.documents; callback(null, null); }
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) { options = options || {}; // Cursor reference var self = this; // Initial query var query = null; // Cursor pool this.pool = null; // Cursor server this.server = null; // Do we have a not connected handler this.disconnectHandler = options.disconnectHandler; // Set local values this.bson = bson; this.ns = ns; this.cmd = cmd; this.options = options; this.topology = topology; // All internal state this.cursorState = { cursorId: null , cmd: cmd , documents: options.documents || [] , cursorIndex: 0 , dead: false , killed: false , init: false , notified: false , limit: options.limit || cmd.limit || 0 , skip: options.skip || cmd.skip || 0 , batchSize: options.batchSize || cmd.batchSize || 1000 , currentLimit: 0 // Result field name if not a cursor (contains the array of results) , transforms: options.transforms } // Add promoteLong to cursor state if(typeof topologyOptions.promoteLongs == 'boolean') { this.cursorState.promoteLongs = topologyOptions.promoteLongs; } // Callback controller this.callbacks = null; // Logger this.logger = Logger('Cursor', options); // // Did we pass in a cursor id if(typeof cmd == 'number') { this.cursorState.cursorId = Long.fromNumber(cmd); this.cursorState.lastCursorId = this.cursorState.cursorId; } else if(cmd instanceof Long) { this.cursorState.cursorId = cmd; this.cursorState.lastCursorId = cmd; } }
Cursor.prototype.nextObject = function(options, callback) { var self = this; if(typeof options == 'function') { callback = options; options = {}; } if(self.state == Cursor.INIT) { var cmd; try { cmd = generateQueryCommand(self); } catch (err) { return callback(err, null); } // Execute command var commandHandler = function(err, result) { self.state = Cursor.OPEN; if(err != null && result == null) return callback(err, null); if(!err && result.documents[0] && result.documents[0]['$err']) { return self.close(function() {callback(result.documents[0]['$err'], null);}); } self.queryRun = true; self.state = Cursor.OPEN; // Adjust the state of the cursor self.cursorId = result.cursorId; self.totalNumberOfRecords = result.numberReturned; // Add the new documents to the list of items, using forloop to avoid // new array allocations and copying for(var i = 0; i < result.documents.length; i++) { self.items.push(result.documents[i]); } // If we have noReturn set just return (not modifying the internal item list) // used for toArray if(options.noReturn) { return callback(null, true); } // Ignore callbacks until the cursor is dead for exhausted if(self.exhaust && result.cursorId.toString() == "0") { self.nextObject(callback); } else if(self.exhaust == false || self.exhaust == null) { self.nextObject(callback); } }; // If we have no connection set on this cursor check one out if(self.connection == null) { try { self.connection = this.read == null ? self.db.serverConfig.checkoutWriter() : self.db.serverConfig.checkoutReader(this.read); } catch(err) { return callback(err, null); } } // Execute the command self.db._executeQueryCommand(cmd, {exhaust: self.exhaust, raw:self.raw, read:self.read, connection:self.connection}, commandHandler); // Set the command handler to null commandHandler = null; } else if(self.items.length) { callback(null, self.items.shift()); } else if(self.cursorId.greaterThan(Long.fromInt(0))) { getMore(self, callback); } else { // Force cursor to stay open return self.close(function() {callback(null, null);}); } }
it('converts `bson.Long` to `{$numberLong: <str>}`', function() { assert.deepEqual(inflate(bson.Long.fromString("10")), { $numberLong: "10" }); });
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) { options = options || {}; // Initial query var query = null; // Current limit processed, used to cut the number of returned docs correctly var currentLimit = 0; // Cursor connection var connection = null; // Cursor server var server = null; // Do we have a not connected handler var disconnectHandler = options.disconnectHandler; // Cursor reference var self = this; // All internal state var cursorState = { cursorId: null , documents: options.documents || [] , dead: false , killed: false , init: false , limit: options.limit || cmd.limit || 0 , skip: options.skip || cmd.skip || 0 , batchSize: options.batchSize || cmd.batchSize || 0 } // Callback controller var callbacks = null; // Logger var logger = Logger('Cursor', options); // // Did we pass in a cursor id if(typeof cmd == 'number') { cursorState.cursorId = Long.fromNumber(cmd); } else if(cmd instanceof Long) { cursorState.cursorId = cmd; } // Allow the manipulation of the batch size of the cursor // after creation has happened Object.defineProperty(this, 'cursorBatchSize', { enumerable:true, set: function(value) { cursorState.batchSize = value; } , get: function() { return cursorState.batchSize; } }); // Allow the manipulation of the cursor limit Object.defineProperty(this, 'cursorLimit', { enumerable:true, set: function(value) { cursorState.limit = value; } , get: function() { return cursorState.limit; } }); // Allow the manipulation of the cursor skip Object.defineProperty(this, 'cursorSkip', { enumerable:true, set: function(value) { cursorState.skip = value; } , get: function() { return cursorState.skip; } }); // // Handle callback (including any exceptions thrown) var handleCallback = function(callback, err, result) { try { callback(err, result); } catch(err) { process.nextTick(function() { throw err; }); } } /** * Clone the cursor * @method * @return {Cursor} */ this.clone = function() { return topology.cursor(ns, cmd, options); } /** * Retrieve the next document from the cursor * @method * @param {resultCallback} callback A callback function */ this.next = function(callback) { if(cursorState.killed) return handleCallback(callback, null, null); if(cursorState.dead) return handleCallback(callback, new MongoError("cursor is dead")); // We have just started the cursor if(!cursorState.init) { // Topology is not connected, save the call in the provided store to be // Executed at some point when the handler deems it's reconnected if(!topology.isConnected(options) && disconnectHandler != null) { return disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback); } try { // Get a server server = topology.getServer(options); // Get a connection connection = server.getConnection(); // Get the callbacks callbacks = server.getCallbacks(); } catch(err) { return callback(err); } // Set as init cursorState.init = true; // Get the right wire protocol command query = server.wireProtocolHandler.command(bson, ns, cmd, cursorState, topology, options); } // Process exhaust messages var processExhaustMessages = function(err, result) { if(err) { cursorState.dead = true; callbacks.unregister(query.requestId); return callback(err); } // Concatenate all the documents cursorState.documents = cursorState.documents.concat(result.documents); // If we have no documents left if(Long.ZERO.equals(result.cursorId)) { cursorState.cursorId = Long.ZERO; callbacks.unregister(query.requestId); return self.next(callback); } // Set up next listener callbacks.register(result.requestId, processExhaustMessages) // Initial result if(cursorState.cursorId == null) { cursorState.cursorId = result.cursorId; self.next(callback); } } // If we have exhaust if(options.exhaust && cursorState.cursorId == null) { // Handle all the exhaust responses callbacks.register(query.requestId, processExhaustMessages); // Write the initial command out return connection.write(query); } else if(options.exhaust && cursorState.documents.length > 0) { return handleCallback(callback, null, cursorState.documents.shift()); } else if(options.exhaust && Long.ZERO.equals(cursorState.cursorId)) { callbacks.unregister(query.requestId); return handleCallback(callback, null, null); } else if(options.exhaust) { return setTimeout(function() { if(Long.ZERO.equals(cursorState.cursorId)) return; self.next(callback); }, 1); } // If we don't have a cursorId execute the first query if(cursorState.cursorId == null) { execInitialQuery(query, function(err, r) { if(err) return handleCallback(callback, err, null); if(cursorState.documents.length == 0) return handleCallback(callback, null, null); self.next(callback); }); } else if(cursorState.documents.length == 0 && !Long.ZERO.equals(cursorState.cursorId)) { execGetMore(function(err, doc) { if(err) return handleCallback(callback, err); if(cursorState.documents.length == 0 && Long.ZERO.equals(cursorState.cursorId)) cursorState.dead = true; // Tailable cursor getMore result, notify owner about it // No attempt is made here to retry, this is left to the user of the // core module to handle to keep core simple if(cursorState.documents.length == 0 && options.tailable) { return handleCallback(callback, MongoError.create({ message: "No more documents in tailed cursor" , tailable: options.tailable , awaitData: options.awaitData })); } if(cursorState.limit > 0 && currentLimit >= cursorState.limit) { cursorState.dead = true; cursorState.documents = []; return handleCallback(callback, null, null); } self.next(callback); }); } else if(cursorState.documents.length == 0 && options.tailable) { return handleCallback(callback, MongoError.create({ message: "No more documents in tailed cursor" , tailable: options.tailable , awaitData: options.awaitData })); } else if(cursorState.documents.length == 0 && Long.ZERO.equals(cursorState.cursorId)) { cursorState.dead = true; handleCallback(callback, null, null); } else { if(cursorState.limit > 0 && currentLimit >= cursorState.limit) { cursorState.dead = true; return handleCallback(callback, null, null); } currentLimit += 1; handleCallback(callback, null, cursorState.documents.shift()); } } /** * Checks if the cursor is dead * @method * @return {boolean} A boolean signifying if the cursor is dead or not */ this.isDead = function() { return cursorState.dead == true; } /** * Returns current buffered documents length * @method * @return {number} The number of items in the buffered documents */ this.bufferedCount = function() { return cursorState.documents.length; } /** * Returns current buffered documents * @method * @return {Array} An array of buffered documents */ this.readBufferedDocuments = function(number) { var length = number < cursorState.documents.length ? number : cursorState.documents.length; var elements = cursorState.documents.splice(0, length); currentLimit = currentLimit + length; return elements; } /** * Resets the cursor * @method * @return {null} */ this.rewind = function() { if(cursorState.init) { if(!cursorState.dead) { this.kill(); } currentLimit = 0; cursorState.init = false; cursorState.dead = false; cursorState.killed = false; cursorState.documents = []; cursorState.cursorId = null; } } /** * Kill the cursor * @method * @param {resultCallback} callback A callback function */ this.kill = function(callback) { // Set cursor to dead cursorState.dead = true; cursorState.killed = true; // Remove documents cursorState.documents = []; // If no cursor id just return if(cursorState.cursorId == null || cursorState.cursorId.isZero() || cursorState.init == false) { if(callback) callback(null, null); return; } // Execute command server.wireProtocolHandler.killCursor(bson, cursorState.cursorId, connection, callback); } // // Execute getMore command var execGetMore = function(callback) { if(logger.isDebug()) logger.debug(f("schedule getMore call for query [%s]", JSON.stringify(query))) // Determine if it's a raw query var raw = options.raw || cmd.raw; // We have a wire protocol handler server.wireProtocolHandler.getMore(bson, ns, cursorState, cursorState.batchSize, raw, connection, callbacks, options, callback); } // // Execute the first query var execInitialQuery = function(query, callback) { if(logger.isDebug()) { logger.debug(f("issue initial query [%s] with flags [%s]" , JSON.stringify(cmd) , JSON.stringify(query))); } var queryCallback = function(err, result) { if(err) return callback(err); // Check if we have a command cursor if(Array.isArray(result.documents) && result.documents.length == 1) { if(result.documents[0]['$err'] || result.documents[0]['errmsg']) { return callback(new MongoError(result.documents[0]), null); } if(result.documents[0].cursor != null && typeof result.documents[0].cursor != 'string') { var id = result.documents[0].cursor.id; // Promote id to long if needed cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id; // If we have a firstBatch set it if(Array.isArray(result.documents[0].cursor.firstBatch)) { cursorState.documents = result.documents[0].cursor.firstBatch; } // Return after processing command cursor return callback(null, null); } if(Array.isArray(result.documents[0].result)) { cursorState.documents = result.documents[0].result; cursorState.cursorId = Long.ZERO; return callback(null, null); } } // Otherwise fall back to regular find path cursorState.cursorId = result.cursorId; cursorState.documents = result.documents; callback(null, null); } // If we have a raw query decorate the function if(options.raw || cmd.raw) { queryCallback.raw = options.raw || cmd.raw; } // Set up callback callbacks.register(query.requestId, queryCallback); // Write the initial command out connection.write(query); } }
it('converts `bson.Long` to `{$numberLong: <str>}` (between 2^32 and 2^53)', function() { assert.deepEqual(inflate(bson.Long.fromString("4294967297")), { $numberLong: "4294967297" }); });
Response.prototype.parse = function(options) { // Don't parse again if not needed if(this.parsed) return; options = options || {}; // Allow the return of raw documents instead of parsing var raw = options.raw || false; var documentsReturnedIn = options.documentsReturnedIn || null; // // Single document and documentsReturnedIn set // if(this.numberReturned == 1 && documentsReturnedIn != null && raw) { // Calculate the bson size var bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24; // Slice out the buffer containing the command result document var document = this.data.slice(this.index, this.index + bsonSize); // Set up field we wish to keep as raw var fieldsAsRaw = {} fieldsAsRaw[documentsReturnedIn] = true; // Set up the options var _options = {promoteLongs: this.opts.promoteLongs, fieldsAsRaw: fieldsAsRaw}; // Deserialize but keep the array of documents in non-parsed form var doc = this.bson.deserialize(document, _options); // Get the documents this.documents = doc.cursor[documentsReturnedIn]; this.numberReturned = this.documents.length; // Ensure we have a Long valie cursor id this.cursorId = typeof doc.cursor.id == 'number' ? Long.fromNumber(doc.cursor.id) : doc.cursor.id; // Adjust the index this.index = this.index + bsonSize; // Set as parsed this.parsed = true return; } // // Parse Body // for(var i = 0; i < this.numberReturned; i++) { var bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24; // Parse options var _options = {promoteLongs: this.opts.promoteLongs}; // If we have raw results specified slice the return document if(raw) { this.documents[i] = this.data.slice(this.index, this.index + bsonSize); } else { this.documents[i] = this.bson.deserialize(this.data.slice(this.index, this.index + bsonSize), _options); } // Adjust the index this.index = this.index + bsonSize; } // Set parsed this.parsed = true; }
var CommandCursor = function(db, collection, command, options) { // Ensure empty options if no options passed options = options || {}; // Default cursor id is 0 var cursorId = Long.fromInt(0); var zeroCursor = Long.fromInt(0); var state = 'init'; // Hardcode batch size command.cursor.batchSize = 1; // BatchSize var batchSize = command.cursor.batchSize || 0; var raw = options.raw || false; var readPreference = options.readPreference || 'primary'; // Checkout a connection var connection = db.serverConfig.checkoutReader(readPreference); // MaxTimeMS var maxTimeMS = options.maxTimeMS; // Contains all the items var items = null; // Execute getmore var getMore = function(callback) { // Resolve more of the cursor using the getMore command var getMoreCommand = new GetMoreCommand(db , db.databaseName + "." + collection.collectionName , batchSize , cursorId ); // Set up options var command_options = { connection:connection }; // Execute the getMore Command db._executeQueryCommand(getMoreCommand, command_options, function(err, result) { if(err) { items = []; state = 'closed'; return callback(err); } // Return all the documents callback(null, result); }); } var exhaustGetMore = function(callback) { getMore(function(err, result) { if(err) { items = []; state = 'closed'; return callback(err, null); } // Add the items items = items.concat(result.documents); // Set the cursor id cursorId = result.cursorId; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // If the cursor is done if(result.cursorId.equals(zeroCursor)) { return callback(null, items); } // Check the cursor id exhaustGetMore(callback); }); } var exhaustGetMoreEach = function(callback) { getMore(function(err, result) { if(err) { items = []; state = 'closed'; return callback(err, null); } // Add the items items = result.documents; // Emit all the items in the first batch while(items.length > 0) { callback(null, items.shift()); } // Set the cursor id cursorId = result.cursorId; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // If the cursor is done if(result.cursorId.equals(zeroCursor)) { state = "closed"; return callback(null, null); } // Check the cursor id exhaustGetMoreEach(callback); }); } // // Get all the elements // this.get = function(options, callback) { if(typeof options == 'function') { callback = options; options = {}; } // Set the connection to the passed in one if it's provided connection = options.connection ? options.connection : connection; // Command options var _options = {connection:connection}; if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS; // Execute the internal command first db.command(command, _options, function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Retrieve the cursor id cursorId = result.cursor.id; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Validate cursorId if(cursorId.equals(zeroCursor)) { return callback(null, result.cursor.firstBatch); }; // Add to the items items = result.cursor.firstBatch; // Execute the getMore exhaustGetMore(callback); }); } // // Iterate over all the items // this.each = function(options, callback) { if(typeof options == 'function') { callback = options; options = {}; } // If it's a closed cursor return error if(this.isClosed()) return callback(new Error("cursor is closed")); // Set the connection to the passed in one if it's provided connection = options.connection ? options.connection : connection; // Command options var _options = {connection:connection}; if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS; // Execute the internal command first db.command(command, _options, function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Get all the items items = result.cursor.firstBatch; // Emit all the items in the first batch while(items.length > 0) { callback(null, items.shift()); } // Retrieve the cursor id cursorId = result.cursor.id; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // If no cursor we just finish up the current batch of items if(cursorId.equals(zeroCursor)) { state = 'closed'; return callback(null, null); } // Emit each until no more getMore's exhaustGetMoreEach(callback); }); } // // Get the next object // this.next = function(options, callback) { if(typeof options == 'function') { callback = options; options = {}; } // If it's a closed cursor return error if(this.isClosed()) return callback(new Error("cursor is closed")); // Set the connection to the passed in one if it's provided connection = options.connection ? options.connection : connection; // Command options var _options = {connection:connection}; if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS; // Execute the internal command first if(!items) { db.command(command, _options, function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Retrieve the cursor id cursorId = result.cursor.id; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Get the first batch results items = result.cursor.firstBatch; // We have items return the first one if(items.length > 0) { callback(null, items.shift()); } else { state = 'closed'; callback(null, null); } }); } else if(items.length > 0) { callback(null, items.shift()); } else if(items.length == 0 && cursorId.equals(zeroCursor)) { state = 'closed'; callback(null, null); } else { // Execute a getMore getMore(function(err, result) { if(err) { state = 'closed'; return callback(err, null); } // Set the cursor id cursorId = result.cursorId; if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId); // Add the items items = items.concat(result.documents); // If no more items if(items.length == 0) { state = 'closed'; return callback(null, null); } // Return the item return callback(null, items.shift()); }) } } // Validate if the cursor is closed this.isClosed = function() { return state == 'closed'; } // Allow us to set the MaxTimeMS this.maxTimeMS = function(_maxTimeMS) { maxTimeMS = _maxTimeMS; } // Close the cursor sending a kill cursor command if needed this.close = function(options, callback) { if(typeof options == 'function') { callback = options; options = {}; } // Close the cursor if not needed if(cursorId instanceof Long && cursorId.greaterThan(Long.fromInt(0))) { try { var command = new KillCursorCommand(this.db, [cursorId]); // Added an empty callback to ensure we don't throw any null exceptions db._executeQueryCommand(command, {connection:connection}); } catch(err) {} } // Null out the connection connection = null; // Reset cursor id cursorId = Long.fromInt(0); // Set to closed status state = 'closed'; // Clear out all the items items = null; if(callback) { callback(null, null); } } }
it('converts `bson.Long` to `{$numberLong: <str>}` (greater than 2^53)', function() { assert.deepEqual(inflate(bson.Long.fromString("18014398509481984")), { $numberLong: "18014398509481984" }); });
$numberLong: function(val) { return bson.Long.fromString(val.$numberLong); },
Cursor.prototype.nextObject = function(options, callback) { var self = this; if(typeof options == 'function') { callback = options; options = {}; } if(self.state == Cursor.INIT) { var cmd; try { cmd = generateQueryCommand(self); } catch (err) { return callback(err, null); } // No need to check the keys var queryOptions = {exhaust: self.exhaust , raw:self.raw , readPreference:self.readPreference , connection:self.connection , checkKeys: false}; // Execute command var commandHandler = function(err, result) { // If on reconnect, the command got given a different connection, switch // the whole cursor to it. self.connection = queryOptions.connection; self.state = Cursor.OPEN; // Adjust the state of the cursor if(err != null && result == null) return callback(utils.toError(err), null); if(err == null && (result == null || result.documents == null || !Array.isArray(result.documents))) { return self.close(function() {callback(new Error("command failed to return results"), null);}); } if(err == null && result && result.documents[0] && result.documents[0]['$err']) { return self.close(function() {callback(utils.toError(result.documents[0]), null);}); } if(err == null && result && result.documents[0] && result.documents[0]['errmsg']) { return self.close(function() {callback(utils.toError(result.documents[0]), null);}); } self.queryRun = true; self.cursorId = result.cursorId; self.totalNumberOfRecords = result.numberReturned; // Add the new documents to the list of items, using forloop to avoid // new array allocations and copying for(var i = 0; i < result.documents.length; i++) { self.items.push(result.documents[i]); } // If we have noReturn set just return (not modifying the internal item list) // used for toArray if(options.noReturn) { return callback(null, true); } // Ignore callbacks until the cursor is dead for exhausted if(self.exhaust && result.cursorId.toString() == "0") { self.nextObject(callback); } else if(self.exhaust == false || self.exhaust == null) { self.nextObject(callback); } }; // If we have no connection set on this cursor check one out if(self.connection == null) { try { self.connection = self.db.serverConfig.checkoutReader(this.readPreference); // Check if we have an error from the checkout Reader function if(self.connection instanceof Error) { return callback(utils.toError(self.connection), null); } // Add to the query options queryOptions.connection = self.connection; } catch(err) { return callback(utils.toError(err), null); } } // Execute the command self.db._executeQueryCommand(cmd, queryOptions, commandHandler); // Set the command handler to null commandHandler = null; } else if(self.items.length) { var doc = self.items.shift(); // If we have a transform if(typeof self.transforms == 'function') { doc = self.transforms(doc); } callback(null, doc); } else if(self.cursorId.greaterThan(Long.fromInt(0))) { getMore(self, callback); } else { // Force cursor to stay open return self.close(function() {callback(null, null);}); } }
var queryCallback = function(err, result) { if(err) return callback(err); // Query failure bit set if(result.queryFailure) { return callback(MongoError.create(result.documents[0]), null); } // Store the connection for usage with getMore command self.connection = result.connection; // Check if we have a command cursor if(Array.isArray(result.documents) && result.documents.length == 1 && (!self.cmd.find || (self.cmd.find && self.cmd.virtual == false)) && (result.documents[0].cursor != 'string' || result.documents[0]['$err'] || result.documents[0]['errmsg'] || Array.isArray(result.documents[0].result)) ) { // We have a an error document return the error if(result.documents[0]['$err'] || result.documents[0]['errmsg']) { return callback(MongoError.create(result.documents[0]), null); } // We have a cursor document if(result.documents[0].cursor != null && typeof result.documents[0].cursor != 'string') { var id = result.documents[0].cursor.id; // If we have a namespace change set the new namespace for getmores if(result.documents[0].cursor.ns) { self.ns = result.documents[0].cursor.ns; } // Promote id to long if needed self.cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id; self.cursorState.lastCursorId = self.cursorState.cursorId; // If we have a firstBatch set it if(Array.isArray(result.documents[0].cursor.firstBatch)) { self.cursorState.documents = result.documents[0].cursor.firstBatch;//.reverse(); } // Return after processing command cursor return callback(null, null); } if(Array.isArray(result.documents[0].result)) { self.cursorState.documents = result.documents[0].result; self.cursorState.cursorId = Long.ZERO; return callback(null, null); } } // Otherwise fall back to regular find path self.cursorState.cursorId = result.cursorId; self.cursorState.documents = result.documents; self.cursorState.lastCursorId = result.cursorId; // Transform the results with passed in transformation method if provided if(self.cursorState.transforms && typeof self.cursorState.transforms.query == 'function') { self.cursorState.documents = self.cursorState.transforms.query(result); } // Return callback callback(null, null); }