123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- 'use strict';
-
- const buildCountCommand = require('./collection_ops').buildCountCommand;
- const formattedOrderClause = require('../utils').formattedOrderClause;
- const handleCallback = require('../utils').handleCallback;
- const MongoError = require('mongodb-core').MongoError;
- const push = Array.prototype.push;
-
- let cursor;
- function loadCursor() {
- if (!cursor) {
- cursor = require('../cursor');
- }
- return cursor;
- }
-
- /**
- * Get the count of documents for this cursor.
- *
- * @method
- * @param {Cursor} cursor The Cursor instance on which to count.
- * @param {boolean} [applySkipLimit=true] Specifies whether the count command apply limit and skip settings should be applied on the cursor or in the provided options.
- * @param {object} [options] Optional settings. See Cursor.prototype.count for a list of options.
- * @param {Cursor~countResultCallback} [callback] The result callback.
- */
- function count(cursor, applySkipLimit, opts, callback) {
- if (applySkipLimit) {
- if (typeof cursor.cursorSkip() === 'number') opts.skip = cursor.cursorSkip();
- if (typeof cursor.cursorLimit() === 'number') opts.limit = cursor.cursorLimit();
- }
-
- // Ensure we have the right read preference inheritance
- if (opts.readPreference) {
- cursor.setReadPreference(opts.readPreference);
- }
-
- if (
- typeof opts.maxTimeMS !== 'number' &&
- cursor.s.cmd &&
- typeof cursor.s.cmd.maxTimeMS === 'number'
- ) {
- opts.maxTimeMS = cursor.s.cmd.maxTimeMS;
- }
-
- let options = {};
- options.skip = opts.skip;
- options.limit = opts.limit;
- options.hint = opts.hint;
- options.maxTimeMS = opts.maxTimeMS;
-
- // Command
- const delimiter = cursor.s.ns.indexOf('.');
- options.collectionName = cursor.s.ns.substr(delimiter + 1);
-
- let command;
- try {
- command = buildCountCommand(cursor, cursor.s.cmd.query, options);
- } catch (err) {
- return callback(err);
- }
-
- // Set cursor server to the same as the topology
- cursor.server = cursor.topology.s.coreTopology;
-
- // Execute the command
- cursor.s.topology.command(
- `${cursor.s.ns.substr(0, delimiter)}.$cmd`,
- command,
- cursor.s.options,
- (err, result) => {
- callback(err, result ? result.result.n : null);
- }
- );
- }
-
- /**
- * Iterates over all the documents for this cursor. See Cursor.prototype.each for more information.
- *
- * @method
- * @deprecated
- * @param {Cursor} cursor The Cursor instance on which to run.
- * @param {Cursor~resultCallback} callback The result callback.
- */
- function each(cursor, callback) {
- let Cursor = loadCursor();
-
- if (!callback) throw MongoError.create({ message: 'callback is mandatory', driver: true });
- if (cursor.isNotified()) return;
- if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) {
- return handleCallback(
- callback,
- MongoError.create({ message: 'Cursor is closed', driver: true })
- );
- }
-
- if (cursor.s.state === Cursor.INIT) cursor.s.state = Cursor.OPEN;
-
- // Define function to avoid global scope escape
- let fn = null;
- // Trampoline all the entries
- if (cursor.bufferedCount() > 0) {
- while ((fn = loop(cursor, callback))) fn(cursor, callback);
- each(cursor, callback);
- } else {
- cursor.next((err, item) => {
- if (err) return handleCallback(callback, err);
- if (item == null) {
- return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, null));
- }
-
- if (handleCallback(callback, null, item) === false) return;
- each(cursor, callback);
- });
- }
- }
-
- /**
- * Check if there is any document still available in the cursor.
- *
- * @method
- * @param {Cursor} cursor The Cursor instance on which to run.
- * @param {Cursor~resultCallback} [callback] The result callback.
- */
- function hasNext(cursor, callback) {
- let Cursor = loadCursor();
-
- if (cursor.s.currentDoc) {
- return callback(null, true);
- }
-
- if (cursor.isNotified()) {
- return callback(null, false);
- }
-
- nextObject(cursor, (err, doc) => {
- if (err) return callback(err, null);
- if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) return callback(null, false);
- if (!doc) return callback(null, false);
- cursor.s.currentDoc = doc;
- callback(null, true);
- });
- }
-
- // Trampoline emptying the number of retrieved items
- // without incurring a nextTick operation
- function loop(cursor, callback) {
- // No more items we are done
- if (cursor.bufferedCount() === 0) return;
- // Get the next document
- cursor._next(callback);
- // Loop
- return loop;
- }
-
- /**
- * Get the next available document from the cursor. Returns null if no more documents are available.
- *
- * @method
- * @param {Cursor} cursor The Cursor instance from which to get the next document.
- * @param {Cursor~resultCallback} [callback] The result callback.
- */
- function next(cursor, callback) {
- // Return the currentDoc if someone called hasNext first
- if (cursor.s.currentDoc) {
- const doc = cursor.s.currentDoc;
- cursor.s.currentDoc = null;
- return callback(null, doc);
- }
-
- // Return the next object
- nextObject(cursor, callback);
- }
-
- // Get the next available document from the cursor, returns null if no more documents are available.
- function nextObject(cursor, callback) {
- let Cursor = loadCursor();
-
- if (cursor.s.state === Cursor.CLOSED || (cursor.isDead && cursor.isDead()))
- return handleCallback(
- callback,
- MongoError.create({ message: 'Cursor is closed', driver: true })
- );
- if (cursor.s.state === Cursor.INIT && cursor.s.cmd.sort) {
- try {
- cursor.s.cmd.sort = formattedOrderClause(cursor.s.cmd.sort);
- } catch (err) {
- return handleCallback(callback, err);
- }
- }
-
- // Get the next object
- cursor._next((err, doc) => {
- cursor.s.state = Cursor.OPEN;
- if (err) return handleCallback(callback, err);
- handleCallback(callback, null, doc);
- });
- }
-
- /**
- * Returns an array of documents. See Cursor.prototype.toArray for more information.
- *
- * @method
- * @param {Cursor} cursor The Cursor instance from which to get the next document.
- * @param {Cursor~toArrayResultCallback} [callback] The result callback.
- */
- function toArray(cursor, callback) {
- let Cursor = loadCursor();
-
- const items = [];
-
- // Reset cursor
- cursor.rewind();
- cursor.s.state = Cursor.INIT;
-
- // Fetch all the documents
- const fetchDocs = () => {
- cursor._next((err, doc) => {
- if (err) {
- return cursor._endSession
- ? cursor._endSession(() => handleCallback(callback, err))
- : handleCallback(callback, err);
- }
- if (doc == null) {
- return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, items));
- }
-
- // Add doc to items
- items.push(doc);
-
- // Get all buffered objects
- if (cursor.bufferedCount() > 0) {
- let docs = cursor.readBufferedDocuments(cursor.bufferedCount());
-
- // Transform the doc if transform method added
- if (cursor.s.transforms && typeof cursor.s.transforms.doc === 'function') {
- docs = docs.map(cursor.s.transforms.doc);
- }
-
- push.apply(items, docs);
- }
-
- // Attempt a fetch
- fetchDocs();
- });
- };
-
- fetchDocs();
- }
-
- module.exports = { count, each, hasNext, next, toArray };
|