find_cursor.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FindCursor = exports.FLAGS = void 0;
  4. const error_1 = require("../error");
  5. const count_1 = require("../operations/count");
  6. const execute_operation_1 = require("../operations/execute_operation");
  7. const find_1 = require("../operations/find");
  8. const sort_1 = require("../sort");
  9. const utils_1 = require("../utils");
  10. const abstract_cursor_1 = require("./abstract_cursor");
  11. /** @internal */
  12. const kFilter = Symbol('filter');
  13. /** @internal */
  14. const kNumReturned = Symbol('numReturned');
  15. /** @internal */
  16. const kBuiltOptions = Symbol('builtOptions');
  17. /** @public Flags allowed for cursor */
  18. exports.FLAGS = [
  19. 'tailable',
  20. 'oplogReplay',
  21. 'noCursorTimeout',
  22. 'awaitData',
  23. 'exhaust',
  24. 'partial'
  25. ];
  26. /** @public */
  27. class FindCursor extends abstract_cursor_1.AbstractCursor {
  28. /** @internal */
  29. constructor(client, namespace, filter, options = {}) {
  30. super(client, namespace, options);
  31. this[kFilter] = filter || {};
  32. this[kBuiltOptions] = options;
  33. if (options.sort != null) {
  34. this[kBuiltOptions].sort = (0, sort_1.formatSort)(options.sort);
  35. }
  36. }
  37. clone() {
  38. const clonedOptions = (0, utils_1.mergeOptions)({}, this[kBuiltOptions]);
  39. delete clonedOptions.session;
  40. return new FindCursor(this.client, this.namespace, this[kFilter], {
  41. ...clonedOptions
  42. });
  43. }
  44. map(transform) {
  45. return super.map(transform);
  46. }
  47. /** @internal */
  48. _initialize(session, callback) {
  49. const findOperation = new find_1.FindOperation(undefined, this.namespace, this[kFilter], {
  50. ...this[kBuiltOptions],
  51. ...this.cursorOptions,
  52. session
  53. });
  54. (0, execute_operation_1.executeOperation)(this.client, findOperation, (err, response) => {
  55. if (err || response == null)
  56. return callback(err);
  57. // TODO: We only need this for legacy queries that do not support `limit`, maybe
  58. // the value should only be saved in those cases.
  59. if (response.cursor) {
  60. this[kNumReturned] = response.cursor.firstBatch.length;
  61. }
  62. else {
  63. this[kNumReturned] = response.documents ? response.documents.length : 0;
  64. }
  65. // TODO: NODE-2882
  66. callback(undefined, { server: findOperation.server, session, response });
  67. });
  68. }
  69. /** @internal */
  70. _getMore(batchSize, callback) {
  71. // NOTE: this is to support client provided limits in pre-command servers
  72. const numReturned = this[kNumReturned];
  73. if (numReturned) {
  74. const limit = this[kBuiltOptions].limit;
  75. batchSize =
  76. limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize;
  77. if (batchSize <= 0) {
  78. return this.close(callback);
  79. }
  80. }
  81. super._getMore(batchSize, (err, response) => {
  82. if (err)
  83. return callback(err);
  84. // TODO: wrap this in some logic to prevent it from happening if we don't need this support
  85. if (response) {
  86. this[kNumReturned] = this[kNumReturned] + response.cursor.nextBatch.length;
  87. }
  88. callback(undefined, response);
  89. });
  90. }
  91. count(options, callback) {
  92. (0, utils_1.emitWarningOnce)('cursor.count is deprecated and will be removed in the next major version, please use `collection.estimatedDocumentCount` or `collection.countDocuments` instead ');
  93. if (typeof options === 'boolean') {
  94. throw new error_1.MongoInvalidArgumentError('Invalid first parameter to count');
  95. }
  96. if (typeof options === 'function')
  97. (callback = options), (options = {});
  98. options = options !== null && options !== void 0 ? options : {};
  99. return (0, execute_operation_1.executeOperation)(this.client, new count_1.CountOperation(this.namespace, this[kFilter], {
  100. ...this[kBuiltOptions],
  101. ...this.cursorOptions,
  102. ...options
  103. }), callback);
  104. }
  105. explain(verbosity, callback) {
  106. if (typeof verbosity === 'function')
  107. (callback = verbosity), (verbosity = true);
  108. if (verbosity == null)
  109. verbosity = true;
  110. return (0, execute_operation_1.executeOperation)(this.client, new find_1.FindOperation(undefined, this.namespace, this[kFilter], {
  111. ...this[kBuiltOptions],
  112. ...this.cursorOptions,
  113. explain: verbosity
  114. }), callback);
  115. }
  116. /** Set the cursor query */
  117. filter(filter) {
  118. (0, abstract_cursor_1.assertUninitialized)(this);
  119. this[kFilter] = filter;
  120. return this;
  121. }
  122. /**
  123. * Set the cursor hint
  124. *
  125. * @param hint - If specified, then the query system will only consider plans using the hinted index.
  126. */
  127. hint(hint) {
  128. (0, abstract_cursor_1.assertUninitialized)(this);
  129. this[kBuiltOptions].hint = hint;
  130. return this;
  131. }
  132. /**
  133. * Set the cursor min
  134. *
  135. * @param min - Specify a $min value to specify the inclusive lower bound for a specific index in order to constrain the results of find(). The $min specifies the lower bound for all keys of a specific index in order.
  136. */
  137. min(min) {
  138. (0, abstract_cursor_1.assertUninitialized)(this);
  139. this[kBuiltOptions].min = min;
  140. return this;
  141. }
  142. /**
  143. * Set the cursor max
  144. *
  145. * @param max - Specify a $max value to specify the exclusive upper bound for a specific index in order to constrain the results of find(). The $max specifies the upper bound for all keys of a specific index in order.
  146. */
  147. max(max) {
  148. (0, abstract_cursor_1.assertUninitialized)(this);
  149. this[kBuiltOptions].max = max;
  150. return this;
  151. }
  152. /**
  153. * Set the cursor returnKey.
  154. * If set to true, modifies the cursor to only return the index field or fields for the results of the query, rather than documents.
  155. * If set to true and the query does not use an index to perform the read operation, the returned documents will not contain any fields.
  156. *
  157. * @param value - the returnKey value.
  158. */
  159. returnKey(value) {
  160. (0, abstract_cursor_1.assertUninitialized)(this);
  161. this[kBuiltOptions].returnKey = value;
  162. return this;
  163. }
  164. /**
  165. * Modifies the output of a query by adding a field $recordId to matching documents. $recordId is the internal key which uniquely identifies a document in a collection.
  166. *
  167. * @param value - The $showDiskLoc option has now been deprecated and replaced with the showRecordId field. $showDiskLoc will still be accepted for OP_QUERY stye find.
  168. */
  169. showRecordId(value) {
  170. (0, abstract_cursor_1.assertUninitialized)(this);
  171. this[kBuiltOptions].showRecordId = value;
  172. return this;
  173. }
  174. /**
  175. * Add a query modifier to the cursor query
  176. *
  177. * @param name - The query modifier (must start with $, such as $orderby etc)
  178. * @param value - The modifier value.
  179. */
  180. addQueryModifier(name, value) {
  181. (0, abstract_cursor_1.assertUninitialized)(this);
  182. if (name[0] !== '$') {
  183. throw new error_1.MongoInvalidArgumentError(`${name} is not a valid query modifier`);
  184. }
  185. // Strip of the $
  186. const field = name.substr(1);
  187. // NOTE: consider some TS magic for this
  188. switch (field) {
  189. case 'comment':
  190. this[kBuiltOptions].comment = value;
  191. break;
  192. case 'explain':
  193. this[kBuiltOptions].explain = value;
  194. break;
  195. case 'hint':
  196. this[kBuiltOptions].hint = value;
  197. break;
  198. case 'max':
  199. this[kBuiltOptions].max = value;
  200. break;
  201. case 'maxTimeMS':
  202. this[kBuiltOptions].maxTimeMS = value;
  203. break;
  204. case 'min':
  205. this[kBuiltOptions].min = value;
  206. break;
  207. case 'orderby':
  208. this[kBuiltOptions].sort = (0, sort_1.formatSort)(value);
  209. break;
  210. case 'query':
  211. this[kFilter] = value;
  212. break;
  213. case 'returnKey':
  214. this[kBuiltOptions].returnKey = value;
  215. break;
  216. case 'showDiskLoc':
  217. this[kBuiltOptions].showRecordId = value;
  218. break;
  219. default:
  220. throw new error_1.MongoInvalidArgumentError(`Invalid query modifier: ${name}`);
  221. }
  222. return this;
  223. }
  224. /**
  225. * Add a comment to the cursor query allowing for tracking the comment in the log.
  226. *
  227. * @param value - The comment attached to this query.
  228. */
  229. comment(value) {
  230. (0, abstract_cursor_1.assertUninitialized)(this);
  231. this[kBuiltOptions].comment = value;
  232. return this;
  233. }
  234. /**
  235. * Set a maxAwaitTimeMS on a tailing cursor query to allow to customize the timeout value for the option awaitData (Only supported on MongoDB 3.2 or higher, ignored otherwise)
  236. *
  237. * @param value - Number of milliseconds to wait before aborting the tailed query.
  238. */
  239. maxAwaitTimeMS(value) {
  240. (0, abstract_cursor_1.assertUninitialized)(this);
  241. if (typeof value !== 'number') {
  242. throw new error_1.MongoInvalidArgumentError('Argument for maxAwaitTimeMS must be a number');
  243. }
  244. this[kBuiltOptions].maxAwaitTimeMS = value;
  245. return this;
  246. }
  247. /**
  248. * Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
  249. *
  250. * @param value - Number of milliseconds to wait before aborting the query.
  251. */
  252. maxTimeMS(value) {
  253. (0, abstract_cursor_1.assertUninitialized)(this);
  254. if (typeof value !== 'number') {
  255. throw new error_1.MongoInvalidArgumentError('Argument for maxTimeMS must be a number');
  256. }
  257. this[kBuiltOptions].maxTimeMS = value;
  258. return this;
  259. }
  260. /**
  261. * Add a project stage to the aggregation pipeline
  262. *
  263. * @remarks
  264. * In order to strictly type this function you must provide an interface
  265. * that represents the effect of your projection on the result documents.
  266. *
  267. * By default chaining a projection to your cursor changes the returned type to the generic
  268. * {@link Document} type.
  269. * You should specify a parameterized type to have assertions on your final results.
  270. *
  271. * @example
  272. * ```typescript
  273. * // Best way
  274. * const docs: FindCursor<{ a: number }> = cursor.project<{ a: number }>({ _id: 0, a: true });
  275. * // Flexible way
  276. * const docs: FindCursor<Document> = cursor.project({ _id: 0, a: true });
  277. * ```
  278. *
  279. * @remarks
  280. *
  281. * **Note for Typescript Users:** adding a transform changes the return type of the iteration of this cursor,
  282. * it **does not** return a new instance of a cursor. This means when calling project,
  283. * you should always assign the result to a new variable in order to get a correctly typed cursor variable.
  284. * Take note of the following example:
  285. *
  286. * @example
  287. * ```typescript
  288. * const cursor: FindCursor<{ a: number; b: string }> = coll.find();
  289. * const projectCursor = cursor.project<{ a: number }>({ _id: 0, a: true });
  290. * const aPropOnlyArray: {a: number}[] = await projectCursor.toArray();
  291. *
  292. * // or always use chaining and save the final cursor
  293. *
  294. * const cursor = coll.find().project<{ a: string }>({
  295. * _id: 0,
  296. * a: { $convert: { input: '$a', to: 'string' }
  297. * }});
  298. * ```
  299. */
  300. project(value) {
  301. (0, abstract_cursor_1.assertUninitialized)(this);
  302. this[kBuiltOptions].projection = value;
  303. return this;
  304. }
  305. /**
  306. * Sets the sort order of the cursor query.
  307. *
  308. * @param sort - The key or keys set for the sort.
  309. * @param direction - The direction of the sorting (1 or -1).
  310. */
  311. sort(sort, direction) {
  312. (0, abstract_cursor_1.assertUninitialized)(this);
  313. if (this[kBuiltOptions].tailable) {
  314. throw new error_1.MongoTailableCursorError('Tailable cursor does not support sorting');
  315. }
  316. this[kBuiltOptions].sort = (0, sort_1.formatSort)(sort, direction);
  317. return this;
  318. }
  319. /**
  320. * Allows disk use for blocking sort operations exceeding 100MB memory. (MongoDB 3.2 or higher)
  321. *
  322. * @remarks
  323. * {@link https://docs.mongodb.com/manual/reference/command/find/#find-cmd-allowdiskuse | find command allowDiskUse documentation}
  324. */
  325. allowDiskUse(allow = true) {
  326. (0, abstract_cursor_1.assertUninitialized)(this);
  327. if (!this[kBuiltOptions].sort) {
  328. throw new error_1.MongoInvalidArgumentError('Option "allowDiskUse" requires a sort specification');
  329. }
  330. // As of 6.0 the default is true. This allows users to get back to the old behaviour.
  331. if (!allow) {
  332. this[kBuiltOptions].allowDiskUse = false;
  333. return this;
  334. }
  335. this[kBuiltOptions].allowDiskUse = true;
  336. return this;
  337. }
  338. /**
  339. * Set the collation options for the cursor.
  340. *
  341. * @param value - The cursor collation options (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  342. */
  343. collation(value) {
  344. (0, abstract_cursor_1.assertUninitialized)(this);
  345. this[kBuiltOptions].collation = value;
  346. return this;
  347. }
  348. /**
  349. * Set the limit for the cursor.
  350. *
  351. * @param value - The limit for the cursor query.
  352. */
  353. limit(value) {
  354. (0, abstract_cursor_1.assertUninitialized)(this);
  355. if (this[kBuiltOptions].tailable) {
  356. throw new error_1.MongoTailableCursorError('Tailable cursor does not support limit');
  357. }
  358. if (typeof value !== 'number') {
  359. throw new error_1.MongoInvalidArgumentError('Operation "limit" requires an integer');
  360. }
  361. this[kBuiltOptions].limit = value;
  362. return this;
  363. }
  364. /**
  365. * Set the skip for the cursor.
  366. *
  367. * @param value - The skip for the cursor query.
  368. */
  369. skip(value) {
  370. (0, abstract_cursor_1.assertUninitialized)(this);
  371. if (this[kBuiltOptions].tailable) {
  372. throw new error_1.MongoTailableCursorError('Tailable cursor does not support skip');
  373. }
  374. if (typeof value !== 'number') {
  375. throw new error_1.MongoInvalidArgumentError('Operation "skip" requires an integer');
  376. }
  377. this[kBuiltOptions].skip = value;
  378. return this;
  379. }
  380. }
  381. exports.FindCursor = FindCursor;
  382. //# sourceMappingURL=find_cursor.js.map