* Get the next doc from the underlying cursor and mongooseify it * (populate, etc.) * @param {any} ctx * @param {Function} cb * @api private
(ctx, cb)
| 448 | */ |
| 449 | |
| 450 | function _next(ctx, cb) { |
| 451 | let callback = cb; |
| 452 | |
| 453 | // Create a custom callback to handle transforms, async iterator, and transformNull |
| 454 | callback = function(err, doc) { |
| 455 | if (err) { |
| 456 | return cb(err); |
| 457 | } |
| 458 | |
| 459 | // Handle null documents - if asyncIterator, we need to return `done: true`, otherwise just |
| 460 | // skip. In either case, avoid transforms. |
| 461 | if (doc === null) { |
| 462 | if (ctx._mongooseOptions._asyncIterator) { |
| 463 | return cb(null, { done: true }); |
| 464 | } else { |
| 465 | return cb(null, null); |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | // Apply transforms |
| 470 | if (ctx._transforms.length && doc !== null) { |
| 471 | doc = ctx._transforms.reduce(function(doc, fn) { |
| 472 | return fn.call(ctx, doc); |
| 473 | }, doc); |
| 474 | } |
| 475 | |
| 476 | // This option is set in `Symbol.asyncIterator` code paths. |
| 477 | // For async iterator, we need to convert to {value, done} format |
| 478 | if (ctx._mongooseOptions._asyncIterator) { |
| 479 | return cb(null, { value: doc, done: false }); |
| 480 | } |
| 481 | |
| 482 | return cb(null, doc); |
| 483 | }; |
| 484 | |
| 485 | if (ctx._error) { |
| 486 | return immediate(function() { |
| 487 | callback(ctx._error); |
| 488 | }); |
| 489 | } |
| 490 | if (ctx.skipped) { |
| 491 | return immediate(() => callback(null, null)); |
| 492 | } |
| 493 | |
| 494 | if (ctx.cursor) { |
| 495 | if (ctx.query._mongooseOptions.populate && !ctx._pop) { |
| 496 | ctx._pop = helpers.preparePopulationOptionsMQ(ctx.query, |
| 497 | ctx.query._mongooseOptions); |
| 498 | ctx._pop.__noPromise = true; |
| 499 | } |
| 500 | if (ctx.query._mongooseOptions.populate && ctx.options._populateBatchSize > 1) { |
| 501 | if (ctx._batchDocs && ctx._batchDocs.length) { |
| 502 | // Return a cached populated doc |
| 503 | return _nextDoc(ctx, ctx._batchDocs.shift(), ctx._pop, callback); |
| 504 | } else if (ctx._batchExhausted) { |
| 505 | // Internal cursor reported no more docs. Act the same here |
| 506 | return callback(null, null); |
| 507 | } else { |