123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- 'use strict';
-
- const retrieveBSON = require('../connection/utils').retrieveBSON;
- const KillCursor = require('../connection/commands').KillCursor;
- const GetMore = require('../connection/commands').GetMore;
- const Query = require('../connection/commands').Query;
- const MongoError = require('../error').MongoError;
- const getReadPreference = require('./shared').getReadPreference;
- const applyCommonQueryOptions = require('./shared').applyCommonQueryOptions;
- const isMongos = require('./shared').isMongos;
- const databaseNamespace = require('./shared').databaseNamespace;
- const collectionNamespace = require('./shared').collectionNamespace;
-
- const BSON = retrieveBSON();
- const Long = BSON.Long;
-
- class WireProtocol {
- insert(server, ns, ops, options, callback) {
- executeWrite(this, server, 'insert', 'documents', ns, ops, options, callback);
- }
-
- update(server, ns, ops, options, callback) {
- executeWrite(this, server, 'update', 'updates', ns, ops, options, callback);
- }
-
- remove(server, ns, ops, options, callback) {
- executeWrite(this, server, 'delete', 'deletes', ns, ops, options, callback);
- }
-
- killCursor(server, ns, cursorState, callback) {
- const bson = server.s.bson;
- const pool = server.s.pool;
- const cursorId = cursorState.cursorId;
- const killCursor = new KillCursor(bson, ns, [cursorId]);
- const options = {
- immediateRelease: true,
- noResponse: true
- };
-
- if (typeof cursorState.session === 'object') {
- options.session = cursorState.session;
- }
-
- if (pool && pool.isConnected()) {
- try {
- pool.write(killCursor, options, callback);
- } catch (err) {
- if (typeof callback === 'function') {
- callback(err, null);
- } else {
- console.warn(err);
- }
- }
- }
- }
-
- getMore(server, ns, cursorState, batchSize, options, callback) {
- const bson = server.s.bson;
- const getMore = new GetMore(bson, ns, cursorState.cursorId, { numberToReturn: batchSize });
- function queryCallback(err, result) {
- if (err) return callback(err);
- const response = result.message;
-
- // If we have a timed out query or a cursor that was killed
- if (response.cursorNotFound) {
- return callback(new MongoError('Cursor does not exist, was killed, or timed out'), null);
- }
-
- const cursorId =
- typeof response.cursorId === 'number'
- ? Long.fromNumber(response.cursorId)
- : response.cursorId;
-
- cursorState.documents = response.documents;
- cursorState.cursorId = cursorId;
-
- callback(null, null, response.connection);
- }
-
- const queryOptions = applyCommonQueryOptions({}, cursorState);
- server.s.pool.write(getMore, queryOptions, queryCallback);
- }
-
- query(server, ns, cmd, cursorState, options, callback) {
- if (cursorState.cursorId != null) {
- return;
- }
-
- const query = setupClassicFind(server, ns, cmd, cursorState, options);
- const queryOptions = applyCommonQueryOptions({}, cursorState);
- if (typeof query.documentsReturnedIn === 'string') {
- queryOptions.documentsReturnedIn = query.documentsReturnedIn;
- }
-
- server.s.pool.write(query, queryOptions, callback);
- }
-
- command(server, ns, cmd, options, callback) {
- if (cmd == null) {
- return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`));
- }
-
- options = options || {};
- const bson = server.s.bson;
- const pool = server.s.pool;
- const readPreference = getReadPreference(cmd, options);
-
- let finalCmd = Object.assign({}, cmd);
- if (finalCmd.readConcern) {
- if (finalCmd.readConcern.level !== 'local') {
- return callback(
- new MongoError(
- `server ${JSON.stringify(finalCmd)} command does not support a readConcern level of ${
- finalCmd.readConcern.level
- }`
- )
- );
- }
-
- delete finalCmd['readConcern'];
- }
-
- if (isMongos(server) && readPreference && readPreference.preference !== 'primary') {
- finalCmd = {
- $query: finalCmd,
- $readPreference: readPreference.toJSON()
- };
- }
-
- const commandOptions = Object.assign(
- {
- command: true,
- numberToSkip: 0,
- numberToReturn: -1,
- checkKeys: false
- },
- options
- );
-
- // This value is not overridable
- commandOptions.slaveOk = readPreference.slaveOk();
-
- try {
- const query = new Query(bson, `${databaseNamespace(ns)}.$cmd`, finalCmd, commandOptions);
- pool.write(query, commandOptions, callback);
- } catch (err) {
- callback(err);
- }
- }
- }
-
- function executeWrite(handler, server, type, opsField, ns, ops, options, callback) {
- if (ops.length === 0) throw new MongoError('insert must contain at least one document');
- if (typeof options === 'function') {
- callback = options;
- options = {};
- options = options || {};
- }
-
- const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
- const writeConcern = options.writeConcern;
-
- const writeCommand = {};
- writeCommand[type] = collectionNamespace(ns);
- writeCommand[opsField] = ops;
- writeCommand.ordered = ordered;
-
- if (writeConcern && Object.keys(writeConcern).length > 0) {
- writeCommand.writeConcern = writeConcern;
- }
-
- if (options.bypassDocumentValidation === true) {
- writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
- }
-
- const commandOptions = Object.assign(
- {
- checkKeys: type === 'insert',
- numberToReturn: 1
- },
- options
- );
-
- handler.command(server, ns, writeCommand, commandOptions, callback);
- }
-
- function setupClassicFind(server, ns, cmd, cursorState, options) {
- options = options || {};
- const bson = server.s.bson;
- const readPreference = getReadPreference(cmd, options);
- cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
-
- let numberToReturn = 0;
- if (cursorState.limit === 0) {
- numberToReturn = cursorState.batchSize;
- } else if (
- cursorState.limit < 0 ||
- cursorState.limit < cursorState.batchSize ||
- (cursorState.limit > 0 && cursorState.batchSize === 0)
- ) {
- numberToReturn = cursorState.limit;
- } else {
- numberToReturn = cursorState.batchSize;
- }
-
- const numberToSkip = cursorState.skip || 0;
-
- const findCmd = {};
- if (isMongos(server) && readPreference) {
- findCmd['$readPreference'] = readPreference.toJSON();
- }
-
- if (cmd.sort) findCmd['$orderby'] = cmd.sort;
- if (cmd.hint) findCmd['$hint'] = cmd.hint;
- if (cmd.snapshot) findCmd['$snapshot'] = cmd.snapshot;
- if (typeof cmd.returnKey !== 'undefined') findCmd['$returnKey'] = cmd.returnKey;
- if (cmd.maxScan) findCmd['$maxScan'] = cmd.maxScan;
- if (cmd.min) findCmd['$min'] = cmd.min;
- if (cmd.max) findCmd['$max'] = cmd.max;
- if (typeof cmd.showDiskLoc !== 'undefined') findCmd['$showDiskLoc'] = cmd.showDiskLoc;
- if (cmd.comment) findCmd['$comment'] = cmd.comment;
- if (cmd.maxTimeMS) findCmd['$maxTimeMS'] = cmd.maxTimeMS;
- if (cmd.explain) {
- // nToReturn must be 0 (match all) or negative (match N and close cursor)
- // nToReturn > 0 will give explain results equivalent to limit(0)
- numberToReturn = -Math.abs(cmd.limit || 0);
- findCmd['$explain'] = true;
- }
-
- findCmd['$query'] = cmd.query;
- if (cmd.readConcern && cmd.readConcern.level !== 'local') {
- throw new MongoError(
- `server find command does not support a readConcern level of ${cmd.readConcern.level}`
- );
- }
-
- if (cmd.readConcern) {
- cmd = Object.assign({}, cmd);
- delete cmd['readConcern'];
- }
-
- const serializeFunctions =
- typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
- const ignoreUndefined =
- typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
-
- const query = new Query(bson, ns, findCmd, {
- numberToSkip: numberToSkip,
- numberToReturn: numberToReturn,
- pre32Limit: typeof cmd.limit !== 'undefined' ? cmd.limit : undefined,
- checkKeys: false,
- returnFieldSelector: cmd.fields,
- serializeFunctions: serializeFunctions,
- ignoreUndefined: ignoreUndefined
- });
-
- if (typeof cmd.tailable === 'boolean') query.tailable = cmd.tailable;
- if (typeof cmd.oplogReplay === 'boolean') query.oplogReplay = cmd.oplogReplay;
- if (typeof cmd.noCursorTimeout === 'boolean') query.noCursorTimeout = cmd.noCursorTimeout;
- if (typeof cmd.awaitData === 'boolean') query.awaitData = cmd.awaitData;
- if (typeof cmd.partial === 'boolean') query.partial = cmd.partial;
-
- query.slaveOk = readPreference.slaveOk();
- return query;
- }
-
- module.exports = WireProtocol;
|