query.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. 'use strict';
  2. const Query = require('../connection/commands').Query;
  3. const MongoError = require('../error').MongoError;
  4. const getReadPreference = require('./shared').getReadPreference;
  5. const collectionNamespace = require('./shared').collectionNamespace;
  6. const isSharded = require('./shared').isSharded;
  7. const maxWireVersion = require('../utils').maxWireVersion;
  8. const applyCommonQueryOptions = require('./shared').applyCommonQueryOptions;
  9. const command = require('./command');
  10. function query(server, ns, cmd, cursorState, options, callback) {
  11. options = options || {};
  12. if (cursorState.cursorId != null) {
  13. return callback();
  14. }
  15. if (cmd == null) {
  16. return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`));
  17. }
  18. if (maxWireVersion(server) < 4) {
  19. const query = prepareLegacyFindQuery(server, ns, cmd, cursorState, options);
  20. const queryOptions = applyCommonQueryOptions({}, cursorState);
  21. if (typeof query.documentsReturnedIn === 'string') {
  22. queryOptions.documentsReturnedIn = query.documentsReturnedIn;
  23. }
  24. server.s.pool.write(query, queryOptions, callback);
  25. return;
  26. }
  27. const readPreference = getReadPreference(cmd, options);
  28. const findCmd = prepareFindCommand(server, ns, cmd, cursorState, options);
  29. // NOTE: This actually modifies the passed in cmd, and our code _depends_ on this
  30. // side-effect. Change this ASAP
  31. cmd.virtual = false;
  32. const commandOptions = Object.assign(
  33. {
  34. documentsReturnedIn: 'firstBatch',
  35. numberToReturn: 1,
  36. slaveOk: readPreference.slaveOk()
  37. },
  38. options
  39. );
  40. if (cmd.readPreference) commandOptions.readPreference = readPreference;
  41. command(server, ns, findCmd, commandOptions, callback);
  42. }
  43. function prepareFindCommand(server, ns, cmd, cursorState) {
  44. cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
  45. let findCmd = {
  46. find: collectionNamespace(ns)
  47. };
  48. if (cmd.query) {
  49. if (cmd.query['$query']) {
  50. findCmd.filter = cmd.query['$query'];
  51. } else {
  52. findCmd.filter = cmd.query;
  53. }
  54. }
  55. let sortValue = cmd.sort;
  56. if (Array.isArray(sortValue)) {
  57. const sortObject = {};
  58. if (sortValue.length > 0 && !Array.isArray(sortValue[0])) {
  59. let sortDirection = sortValue[1];
  60. if (sortDirection === 'asc') {
  61. sortDirection = 1;
  62. } else if (sortDirection === 'desc') {
  63. sortDirection = -1;
  64. }
  65. sortObject[sortValue[0]] = sortDirection;
  66. } else {
  67. for (let i = 0; i < sortValue.length; i++) {
  68. let sortDirection = sortValue[i][1];
  69. if (sortDirection === 'asc') {
  70. sortDirection = 1;
  71. } else if (sortDirection === 'desc') {
  72. sortDirection = -1;
  73. }
  74. sortObject[sortValue[i][0]] = sortDirection;
  75. }
  76. }
  77. sortValue = sortObject;
  78. }
  79. if (cmd.sort) findCmd.sort = sortValue;
  80. if (cmd.fields) findCmd.projection = cmd.fields;
  81. if (cmd.hint) findCmd.hint = cmd.hint;
  82. if (cmd.skip) findCmd.skip = cmd.skip;
  83. if (cmd.limit) findCmd.limit = cmd.limit;
  84. if (cmd.limit < 0) {
  85. findCmd.limit = Math.abs(cmd.limit);
  86. findCmd.singleBatch = true;
  87. }
  88. if (typeof cmd.batchSize === 'number') {
  89. if (cmd.batchSize < 0) {
  90. if (cmd.limit !== 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) {
  91. findCmd.limit = Math.abs(cmd.batchSize);
  92. }
  93. findCmd.singleBatch = true;
  94. }
  95. findCmd.batchSize = Math.abs(cmd.batchSize);
  96. }
  97. if (cmd.comment) findCmd.comment = cmd.comment;
  98. if (cmd.maxScan) findCmd.maxScan = cmd.maxScan;
  99. if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
  100. if (cmd.min) findCmd.min = cmd.min;
  101. if (cmd.max) findCmd.max = cmd.max;
  102. findCmd.returnKey = cmd.returnKey ? cmd.returnKey : false;
  103. findCmd.showRecordId = cmd.showDiskLoc ? cmd.showDiskLoc : false;
  104. if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
  105. if (cmd.tailable) findCmd.tailable = cmd.tailable;
  106. if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
  107. if (cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout;
  108. if (cmd.awaitData) findCmd.awaitData = cmd.awaitData;
  109. if (cmd.awaitdata) findCmd.awaitData = cmd.awaitdata;
  110. if (cmd.partial) findCmd.partial = cmd.partial;
  111. if (cmd.collation) findCmd.collation = cmd.collation;
  112. if (cmd.readConcern) findCmd.readConcern = cmd.readConcern;
  113. // If we have explain, we need to rewrite the find command
  114. // to wrap it in the explain command
  115. if (cmd.explain) {
  116. findCmd = {
  117. explain: findCmd
  118. };
  119. }
  120. return findCmd;
  121. }
  122. function prepareLegacyFindQuery(server, ns, cmd, cursorState, options) {
  123. options = options || {};
  124. const bson = server.s.bson;
  125. const readPreference = getReadPreference(cmd, options);
  126. cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
  127. let numberToReturn = 0;
  128. if (
  129. cursorState.limit < 0 ||
  130. (cursorState.limit !== 0 && cursorState.limit < cursorState.batchSize) ||
  131. (cursorState.limit > 0 && cursorState.batchSize === 0)
  132. ) {
  133. numberToReturn = cursorState.limit;
  134. } else {
  135. numberToReturn = cursorState.batchSize;
  136. }
  137. const numberToSkip = cursorState.skip || 0;
  138. const findCmd = {};
  139. if (isSharded(server) && readPreference) {
  140. findCmd['$readPreference'] = readPreference.toJSON();
  141. }
  142. if (cmd.sort) findCmd['$orderby'] = cmd.sort;
  143. if (cmd.hint) findCmd['$hint'] = cmd.hint;
  144. if (cmd.snapshot) findCmd['$snapshot'] = cmd.snapshot;
  145. if (typeof cmd.returnKey !== 'undefined') findCmd['$returnKey'] = cmd.returnKey;
  146. if (cmd.maxScan) findCmd['$maxScan'] = cmd.maxScan;
  147. if (cmd.min) findCmd['$min'] = cmd.min;
  148. if (cmd.max) findCmd['$max'] = cmd.max;
  149. if (typeof cmd.showDiskLoc !== 'undefined') findCmd['$showDiskLoc'] = cmd.showDiskLoc;
  150. if (cmd.comment) findCmd['$comment'] = cmd.comment;
  151. if (cmd.maxTimeMS) findCmd['$maxTimeMS'] = cmd.maxTimeMS;
  152. if (cmd.explain) {
  153. // nToReturn must be 0 (match all) or negative (match N and close cursor)
  154. // nToReturn > 0 will give explain results equivalent to limit(0)
  155. numberToReturn = -Math.abs(cmd.limit || 0);
  156. findCmd['$explain'] = true;
  157. }
  158. findCmd['$query'] = cmd.query;
  159. if (cmd.readConcern && cmd.readConcern.level !== 'local') {
  160. throw new MongoError(
  161. `server find command does not support a readConcern level of ${cmd.readConcern.level}`
  162. );
  163. }
  164. if (cmd.readConcern) {
  165. cmd = Object.assign({}, cmd);
  166. delete cmd['readConcern'];
  167. }
  168. const serializeFunctions =
  169. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  170. const ignoreUndefined =
  171. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  172. const query = new Query(bson, ns, findCmd, {
  173. numberToSkip: numberToSkip,
  174. numberToReturn: numberToReturn,
  175. pre32Limit: typeof cmd.limit !== 'undefined' ? cmd.limit : undefined,
  176. checkKeys: false,
  177. returnFieldSelector: cmd.fields,
  178. serializeFunctions: serializeFunctions,
  179. ignoreUndefined: ignoreUndefined
  180. });
  181. if (typeof cmd.tailable === 'boolean') query.tailable = cmd.tailable;
  182. if (typeof cmd.oplogReplay === 'boolean') query.oplogReplay = cmd.oplogReplay;
  183. if (typeof cmd.noCursorTimeout === 'boolean') query.noCursorTimeout = cmd.noCursorTimeout;
  184. if (typeof cmd.awaitData === 'boolean') query.awaitData = cmd.awaitData;
  185. if (typeof cmd.partial === 'boolean') query.partial = cmd.partial;
  186. query.slaveOk = readPreference.slaveOk();
  187. return query;
  188. }
  189. module.exports = query;