/node_modules/mongoose/node_modules/mongodb-core/lib/connection/commands.js

https://bitbucket.org/coleman333/smartsite · JavaScript · 531 lines · 343 code · 84 blank · 104 comment · 37 complexity · a1e7043fbdc21b598a43b0c8f23799e5 MD5 · raw file

  1. 'use strict';
  2. var retrieveBSON = require('./utils').retrieveBSON;
  3. var BSON = retrieveBSON();
  4. var Long = BSON.Long;
  5. // Incrementing request id
  6. var _requestId = 0;
  7. // Wire command operation ids
  8. var opcodes = require('../wireprotocol/shared').opcodes;
  9. // Query flags
  10. var OPTS_TAILABLE_CURSOR = 2;
  11. var OPTS_SLAVE = 4;
  12. var OPTS_OPLOG_REPLAY = 8;
  13. var OPTS_NO_CURSOR_TIMEOUT = 16;
  14. var OPTS_AWAIT_DATA = 32;
  15. var OPTS_EXHAUST = 64;
  16. var OPTS_PARTIAL = 128;
  17. // Response flags
  18. var CURSOR_NOT_FOUND = 0;
  19. var QUERY_FAILURE = 2;
  20. var SHARD_CONFIG_STALE = 4;
  21. var AWAIT_CAPABLE = 8;
  22. /**************************************************************
  23. * QUERY
  24. **************************************************************/
  25. var Query = function(bson, ns, query, options) {
  26. var self = this;
  27. // Basic options needed to be passed in
  28. if (ns == null) throw new Error('ns must be specified for query');
  29. if (query == null) throw new Error('query must be specified for query');
  30. // Validate that we are not passing 0x00 in the collection name
  31. if (ns.indexOf('\x00') !== -1) {
  32. throw new Error('namespace cannot contain a null character');
  33. }
  34. // Basic options
  35. this.bson = bson;
  36. this.ns = ns;
  37. this.query = query;
  38. // Ensure empty options
  39. this.options = options || {};
  40. // Additional options
  41. this.numberToSkip = options.numberToSkip || 0;
  42. this.numberToReturn = options.numberToReturn || 0;
  43. this.returnFieldSelector = options.returnFieldSelector || null;
  44. this.requestId = Query.getRequestId();
  45. // Serialization option
  46. this.serializeFunctions =
  47. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  48. this.ignoreUndefined =
  49. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
  50. this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
  51. this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  52. this.batchSize = self.numberToReturn;
  53. // Flags
  54. this.tailable = false;
  55. this.slaveOk = typeof options.slaveOk === 'boolean' ? options.slaveOk : false;
  56. this.oplogReplay = false;
  57. this.noCursorTimeout = false;
  58. this.awaitData = false;
  59. this.exhaust = false;
  60. this.partial = false;
  61. };
  62. //
  63. // Assign a new request Id
  64. Query.prototype.incRequestId = function() {
  65. this.requestId = _requestId++;
  66. };
  67. //
  68. // Assign a new request Id
  69. Query.nextRequestId = function() {
  70. return _requestId + 1;
  71. };
  72. //
  73. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  74. Query.prototype.toBin = function() {
  75. var self = this;
  76. var buffers = [];
  77. var projection = null;
  78. // Set up the flags
  79. var flags = 0;
  80. if (this.tailable) {
  81. flags |= OPTS_TAILABLE_CURSOR;
  82. }
  83. if (this.slaveOk) {
  84. flags |= OPTS_SLAVE;
  85. }
  86. if (this.oplogReplay) {
  87. flags |= OPTS_OPLOG_REPLAY;
  88. }
  89. if (this.noCursorTimeout) {
  90. flags |= OPTS_NO_CURSOR_TIMEOUT;
  91. }
  92. if (this.awaitData) {
  93. flags |= OPTS_AWAIT_DATA;
  94. }
  95. if (this.exhaust) {
  96. flags |= OPTS_EXHAUST;
  97. }
  98. if (this.partial) {
  99. flags |= OPTS_PARTIAL;
  100. }
  101. // If batchSize is different to self.numberToReturn
  102. if (self.batchSize !== self.numberToReturn) self.numberToReturn = self.batchSize;
  103. // Allocate write protocol header buffer
  104. var header = new Buffer(
  105. 4 * 4 + // Header
  106. 4 + // Flags
  107. Buffer.byteLength(self.ns) +
  108. 1 + // namespace
  109. 4 + // numberToSkip
  110. 4 // numberToReturn
  111. );
  112. // Add header to buffers
  113. buffers.push(header);
  114. // Serialize the query
  115. var query = self.bson.serialize(this.query, {
  116. checkKeys: this.checkKeys,
  117. serializeFunctions: this.serializeFunctions,
  118. ignoreUndefined: this.ignoreUndefined
  119. });
  120. // Add query document
  121. buffers.push(query);
  122. if (self.returnFieldSelector && Object.keys(self.returnFieldSelector).length > 0) {
  123. // Serialize the projection document
  124. projection = self.bson.serialize(this.returnFieldSelector, {
  125. checkKeys: this.checkKeys,
  126. serializeFunctions: this.serializeFunctions,
  127. ignoreUndefined: this.ignoreUndefined
  128. });
  129. // Add projection document
  130. buffers.push(projection);
  131. }
  132. // Total message size
  133. var totalLength = header.length + query.length + (projection ? projection.length : 0);
  134. // Set up the index
  135. var index = 4;
  136. // Write total document length
  137. header[3] = (totalLength >> 24) & 0xff;
  138. header[2] = (totalLength >> 16) & 0xff;
  139. header[1] = (totalLength >> 8) & 0xff;
  140. header[0] = totalLength & 0xff;
  141. // Write header information requestId
  142. header[index + 3] = (this.requestId >> 24) & 0xff;
  143. header[index + 2] = (this.requestId >> 16) & 0xff;
  144. header[index + 1] = (this.requestId >> 8) & 0xff;
  145. header[index] = this.requestId & 0xff;
  146. index = index + 4;
  147. // Write header information responseTo
  148. header[index + 3] = (0 >> 24) & 0xff;
  149. header[index + 2] = (0 >> 16) & 0xff;
  150. header[index + 1] = (0 >> 8) & 0xff;
  151. header[index] = 0 & 0xff;
  152. index = index + 4;
  153. // Write header information OP_QUERY
  154. header[index + 3] = (opcodes.OP_QUERY >> 24) & 0xff;
  155. header[index + 2] = (opcodes.OP_QUERY >> 16) & 0xff;
  156. header[index + 1] = (opcodes.OP_QUERY >> 8) & 0xff;
  157. header[index] = opcodes.OP_QUERY & 0xff;
  158. index = index + 4;
  159. // Write header information flags
  160. header[index + 3] = (flags >> 24) & 0xff;
  161. header[index + 2] = (flags >> 16) & 0xff;
  162. header[index + 1] = (flags >> 8) & 0xff;
  163. header[index] = flags & 0xff;
  164. index = index + 4;
  165. // Write collection name
  166. index = index + header.write(this.ns, index, 'utf8') + 1;
  167. header[index - 1] = 0;
  168. // Write header information flags numberToSkip
  169. header[index + 3] = (this.numberToSkip >> 24) & 0xff;
  170. header[index + 2] = (this.numberToSkip >> 16) & 0xff;
  171. header[index + 1] = (this.numberToSkip >> 8) & 0xff;
  172. header[index] = this.numberToSkip & 0xff;
  173. index = index + 4;
  174. // Write header information flags numberToReturn
  175. header[index + 3] = (this.numberToReturn >> 24) & 0xff;
  176. header[index + 2] = (this.numberToReturn >> 16) & 0xff;
  177. header[index + 1] = (this.numberToReturn >> 8) & 0xff;
  178. header[index] = this.numberToReturn & 0xff;
  179. index = index + 4;
  180. // Return the buffers
  181. return buffers;
  182. };
  183. Query.getRequestId = function() {
  184. return ++_requestId;
  185. };
  186. /**************************************************************
  187. * GETMORE
  188. **************************************************************/
  189. var GetMore = function(bson, ns, cursorId, opts) {
  190. opts = opts || {};
  191. this.numberToReturn = opts.numberToReturn || 0;
  192. this.requestId = _requestId++;
  193. this.bson = bson;
  194. this.ns = ns;
  195. this.cursorId = cursorId;
  196. };
  197. //
  198. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  199. GetMore.prototype.toBin = function() {
  200. var length = 4 + Buffer.byteLength(this.ns) + 1 + 4 + 8 + 4 * 4;
  201. // Create command buffer
  202. var index = 0;
  203. // Allocate buffer
  204. var _buffer = new Buffer(length);
  205. // Write header information
  206. // index = write32bit(index, _buffer, length);
  207. _buffer[index + 3] = (length >> 24) & 0xff;
  208. _buffer[index + 2] = (length >> 16) & 0xff;
  209. _buffer[index + 1] = (length >> 8) & 0xff;
  210. _buffer[index] = length & 0xff;
  211. index = index + 4;
  212. // index = write32bit(index, _buffer, requestId);
  213. _buffer[index + 3] = (this.requestId >> 24) & 0xff;
  214. _buffer[index + 2] = (this.requestId >> 16) & 0xff;
  215. _buffer[index + 1] = (this.requestId >> 8) & 0xff;
  216. _buffer[index] = this.requestId & 0xff;
  217. index = index + 4;
  218. // index = write32bit(index, _buffer, 0);
  219. _buffer[index + 3] = (0 >> 24) & 0xff;
  220. _buffer[index + 2] = (0 >> 16) & 0xff;
  221. _buffer[index + 1] = (0 >> 8) & 0xff;
  222. _buffer[index] = 0 & 0xff;
  223. index = index + 4;
  224. // index = write32bit(index, _buffer, OP_GETMORE);
  225. _buffer[index + 3] = (opcodes.OP_GETMORE >> 24) & 0xff;
  226. _buffer[index + 2] = (opcodes.OP_GETMORE >> 16) & 0xff;
  227. _buffer[index + 1] = (opcodes.OP_GETMORE >> 8) & 0xff;
  228. _buffer[index] = opcodes.OP_GETMORE & 0xff;
  229. index = index + 4;
  230. // index = write32bit(index, _buffer, 0);
  231. _buffer[index + 3] = (0 >> 24) & 0xff;
  232. _buffer[index + 2] = (0 >> 16) & 0xff;
  233. _buffer[index + 1] = (0 >> 8) & 0xff;
  234. _buffer[index] = 0 & 0xff;
  235. index = index + 4;
  236. // Write collection name
  237. index = index + _buffer.write(this.ns, index, 'utf8') + 1;
  238. _buffer[index - 1] = 0;
  239. // Write batch size
  240. // index = write32bit(index, _buffer, numberToReturn);
  241. _buffer[index + 3] = (this.numberToReturn >> 24) & 0xff;
  242. _buffer[index + 2] = (this.numberToReturn >> 16) & 0xff;
  243. _buffer[index + 1] = (this.numberToReturn >> 8) & 0xff;
  244. _buffer[index] = this.numberToReturn & 0xff;
  245. index = index + 4;
  246. // Write cursor id
  247. // index = write32bit(index, _buffer, cursorId.getLowBits());
  248. _buffer[index + 3] = (this.cursorId.getLowBits() >> 24) & 0xff;
  249. _buffer[index + 2] = (this.cursorId.getLowBits() >> 16) & 0xff;
  250. _buffer[index + 1] = (this.cursorId.getLowBits() >> 8) & 0xff;
  251. _buffer[index] = this.cursorId.getLowBits() & 0xff;
  252. index = index + 4;
  253. // index = write32bit(index, _buffer, cursorId.getHighBits());
  254. _buffer[index + 3] = (this.cursorId.getHighBits() >> 24) & 0xff;
  255. _buffer[index + 2] = (this.cursorId.getHighBits() >> 16) & 0xff;
  256. _buffer[index + 1] = (this.cursorId.getHighBits() >> 8) & 0xff;
  257. _buffer[index] = this.cursorId.getHighBits() & 0xff;
  258. index = index + 4;
  259. // Return buffer
  260. return _buffer;
  261. };
  262. /**************************************************************
  263. * KILLCURSOR
  264. **************************************************************/
  265. var KillCursor = function(bson, cursorIds) {
  266. this.requestId = _requestId++;
  267. this.cursorIds = cursorIds;
  268. };
  269. //
  270. // Uses a single allocated buffer for the process, avoiding multiple memory allocations
  271. KillCursor.prototype.toBin = function() {
  272. var length = 4 + 4 + 4 * 4 + this.cursorIds.length * 8;
  273. // Create command buffer
  274. var index = 0;
  275. var _buffer = new Buffer(length);
  276. // Write header information
  277. // index = write32bit(index, _buffer, length);
  278. _buffer[index + 3] = (length >> 24) & 0xff;
  279. _buffer[index + 2] = (length >> 16) & 0xff;
  280. _buffer[index + 1] = (length >> 8) & 0xff;
  281. _buffer[index] = length & 0xff;
  282. index = index + 4;
  283. // index = write32bit(index, _buffer, requestId);
  284. _buffer[index + 3] = (this.requestId >> 24) & 0xff;
  285. _buffer[index + 2] = (this.requestId >> 16) & 0xff;
  286. _buffer[index + 1] = (this.requestId >> 8) & 0xff;
  287. _buffer[index] = this.requestId & 0xff;
  288. index = index + 4;
  289. // index = write32bit(index, _buffer, 0);
  290. _buffer[index + 3] = (0 >> 24) & 0xff;
  291. _buffer[index + 2] = (0 >> 16) & 0xff;
  292. _buffer[index + 1] = (0 >> 8) & 0xff;
  293. _buffer[index] = 0 & 0xff;
  294. index = index + 4;
  295. // index = write32bit(index, _buffer, OP_KILL_CURSORS);
  296. _buffer[index + 3] = (opcodes.OP_KILL_CURSORS >> 24) & 0xff;
  297. _buffer[index + 2] = (opcodes.OP_KILL_CURSORS >> 16) & 0xff;
  298. _buffer[index + 1] = (opcodes.OP_KILL_CURSORS >> 8) & 0xff;
  299. _buffer[index] = opcodes.OP_KILL_CURSORS & 0xff;
  300. index = index + 4;
  301. // index = write32bit(index, _buffer, 0);
  302. _buffer[index + 3] = (0 >> 24) & 0xff;
  303. _buffer[index + 2] = (0 >> 16) & 0xff;
  304. _buffer[index + 1] = (0 >> 8) & 0xff;
  305. _buffer[index] = 0 & 0xff;
  306. index = index + 4;
  307. // Write batch size
  308. // index = write32bit(index, _buffer, this.cursorIds.length);
  309. _buffer[index + 3] = (this.cursorIds.length >> 24) & 0xff;
  310. _buffer[index + 2] = (this.cursorIds.length >> 16) & 0xff;
  311. _buffer[index + 1] = (this.cursorIds.length >> 8) & 0xff;
  312. _buffer[index] = this.cursorIds.length & 0xff;
  313. index = index + 4;
  314. // Write all the cursor ids into the array
  315. for (var i = 0; i < this.cursorIds.length; i++) {
  316. // Write cursor id
  317. // index = write32bit(index, _buffer, cursorIds[i].getLowBits());
  318. _buffer[index + 3] = (this.cursorIds[i].getLowBits() >> 24) & 0xff;
  319. _buffer[index + 2] = (this.cursorIds[i].getLowBits() >> 16) & 0xff;
  320. _buffer[index + 1] = (this.cursorIds[i].getLowBits() >> 8) & 0xff;
  321. _buffer[index] = this.cursorIds[i].getLowBits() & 0xff;
  322. index = index + 4;
  323. // index = write32bit(index, _buffer, cursorIds[i].getHighBits());
  324. _buffer[index + 3] = (this.cursorIds[i].getHighBits() >> 24) & 0xff;
  325. _buffer[index + 2] = (this.cursorIds[i].getHighBits() >> 16) & 0xff;
  326. _buffer[index + 1] = (this.cursorIds[i].getHighBits() >> 8) & 0xff;
  327. _buffer[index] = this.cursorIds[i].getHighBits() & 0xff;
  328. index = index + 4;
  329. }
  330. // Return buffer
  331. return _buffer;
  332. };
  333. var Response = function(bson, message, msgHeader, msgBody, opts) {
  334. opts = opts || { promoteLongs: true, promoteValues: true, promoteBuffers: false };
  335. this.parsed = false;
  336. this.raw = message;
  337. this.data = msgBody;
  338. this.bson = bson;
  339. this.opts = opts;
  340. // Read the message header
  341. this.length = msgHeader.length;
  342. this.requestId = msgHeader.requestId;
  343. this.responseTo = msgHeader.responseTo;
  344. this.opCode = msgHeader.opCode;
  345. this.fromCompressed = msgHeader.fromCompressed;
  346. // Read the message body
  347. this.responseFlags = msgBody.readInt32LE(0);
  348. this.cursorId = new Long(msgBody.readInt32LE(4), msgBody.readInt32LE(8));
  349. this.startingFrom = msgBody.readInt32LE(12);
  350. this.numberReturned = msgBody.readInt32LE(16);
  351. // Preallocate document array
  352. this.documents = new Array(this.numberReturned);
  353. // Flag values
  354. this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
  355. this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
  356. this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
  357. this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
  358. this.promoteLongs = typeof opts.promoteLongs === 'boolean' ? opts.promoteLongs : true;
  359. this.promoteValues = typeof opts.promoteValues === 'boolean' ? opts.promoteValues : true;
  360. this.promoteBuffers = typeof opts.promoteBuffers === 'boolean' ? opts.promoteBuffers : false;
  361. };
  362. Response.prototype.isParsed = function() {
  363. return this.parsed;
  364. };
  365. Response.prototype.parse = function(options) {
  366. // Don't parse again if not needed
  367. if (this.parsed) return;
  368. options = options || {};
  369. // Allow the return of raw documents instead of parsing
  370. var raw = options.raw || false;
  371. var documentsReturnedIn = options.documentsReturnedIn || null;
  372. var promoteLongs =
  373. typeof options.promoteLongs === 'boolean' ? options.promoteLongs : this.opts.promoteLongs;
  374. var promoteValues =
  375. typeof options.promoteValues === 'boolean' ? options.promoteValues : this.opts.promoteValues;
  376. var promoteBuffers =
  377. typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : this.opts.promoteBuffers;
  378. var bsonSize, _options;
  379. // Set up the options
  380. _options = {
  381. promoteLongs: promoteLongs,
  382. promoteValues: promoteValues,
  383. promoteBuffers: promoteBuffers
  384. };
  385. // Position within OP_REPLY at which documents start
  386. // (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
  387. this.index = 20;
  388. //
  389. // Single document and documentsReturnedIn set
  390. //
  391. if (this.numberReturned === 1 && documentsReturnedIn != null && raw) {
  392. // Calculate the bson size
  393. bsonSize =
  394. this.data[this.index] |
  395. (this.data[this.index + 1] << 8) |
  396. (this.data[this.index + 2] << 16) |
  397. (this.data[this.index + 3] << 24);
  398. // Slice out the buffer containing the command result document
  399. var document = this.data.slice(this.index, this.index + bsonSize);
  400. // Set up field we wish to keep as raw
  401. var fieldsAsRaw = {};
  402. fieldsAsRaw[documentsReturnedIn] = true;
  403. _options.fieldsAsRaw = fieldsAsRaw;
  404. // Deserialize but keep the array of documents in non-parsed form
  405. var doc = this.bson.deserialize(document, _options);
  406. // Get the documents
  407. this.documents = doc.cursor[documentsReturnedIn];
  408. this.numberReturned = this.documents.length;
  409. // Ensure we have a Long valie cursor id
  410. this.cursorId =
  411. typeof doc.cursor.id === 'number' ? Long.fromNumber(doc.cursor.id) : doc.cursor.id;
  412. // Adjust the index
  413. this.index = this.index + bsonSize;
  414. // Set as parsed
  415. this.parsed = true;
  416. return;
  417. }
  418. //
  419. // Parse Body
  420. //
  421. for (var i = 0; i < this.numberReturned; i++) {
  422. bsonSize =
  423. this.data[this.index] |
  424. (this.data[this.index + 1] << 8) |
  425. (this.data[this.index + 2] << 16) |
  426. (this.data[this.index + 3] << 24);
  427. // If we have raw results specified slice the return document
  428. if (raw) {
  429. this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
  430. } else {
  431. this.documents[i] = this.bson.deserialize(
  432. this.data.slice(this.index, this.index + bsonSize),
  433. _options
  434. );
  435. }
  436. // Adjust the index
  437. this.index = this.index + bsonSize;
  438. }
  439. // Set parsed
  440. this.parsed = true;
  441. };
  442. module.exports = {
  443. Query: Query,
  444. GetMore: GetMore,
  445. Response: Response,
  446. KillCursor: KillCursor
  447. };