summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/count2.js36
-rw-r--r--jstests/core/crud_api.js4
-rw-r--r--jstests/libs/override_methods/set_read_preference_secondary.js22
-rw-r--r--jstests/noPassthrough/client_metadata_slowlog.js12
-rw-r--r--jstests/noPassthrough/client_metadata_slowlog_rs.js2
-rw-r--r--jstests/noPassthrough/log_find_getmore.js48
-rw-r--r--src/mongo/scripting/mozjs/dbquery.cpp15
-rw-r--r--src/mongo/scripting/mozjs/internedstring.defs6
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp97
-rw-r--r--src/mongo/shell/collection.js11
-rw-r--r--src/mongo/shell/explain_query.js17
-rw-r--r--src/mongo/shell/query.js354
12 files changed, 218 insertions, 406 deletions
diff --git a/jstests/core/count2.js b/jstests/core/count2.js
index 9c8aaca7a32..21aa75aebd0 100644
--- a/jstests/core/count2.js
+++ b/jstests/core/count2.js
@@ -1,30 +1,32 @@
// @tags: [requires_getmore, requires_fastcount]
-
-t = db.count2;
-t.drop();
+(function() {
+"use strict";
+const coll = db.count2;
+coll.drop();
for (var i = 0; i < 1000; i++) {
- t.save({num: i, m: i % 20});
+ assert.commandWorked(coll.insert({num: i, m: i % 20}));
}
-assert.eq(1000, t.count(), "A");
-assert.eq(1000, t.find().count(), "B");
-assert.eq(1000, t.find().toArray().length, "C");
+assert.eq(1000, coll.count());
+assert.eq(1000, coll.find().count());
+assert.eq(1000, coll.find().toArray().length);
-assert.eq(50, t.find({m: 5}).toArray().length, "D");
-assert.eq(50, t.find({m: 5}).count(), "E");
+assert.eq(50, coll.find({m: 5}).toArray().length);
+assert.eq(50, coll.find({m: 5}).count());
-assert.eq(40, t.find({m: 5}).skip(10).toArray().length, "F");
-assert.eq(50, t.find({m: 5}).skip(10).count(), "G");
-assert.eq(40, t.find({m: 5}).skip(10).countReturn(), "H");
+assert.eq(40, coll.find({m: 5}).skip(10).toArray().length);
+assert.eq(50, coll.find({m: 5}).skip(10).count());
+assert.eq(40, coll.find({m: 5}).skip(10).count(true));
-assert.eq(20, t.find({m: 5}).skip(10).limit(20).toArray().length, "I");
-assert.eq(50, t.find({m: 5}).skip(10).limit(20).count(), "J");
-assert.eq(20, t.find({m: 5}).skip(10).limit(20).countReturn(), "K");
+assert.eq(20, coll.find({m: 5}).skip(10).limit(20).toArray().length);
+assert.eq(50, coll.find({m: 5}).skip(10).limit(20).count());
+assert.eq(20, coll.find({m: 5}).skip(10).limit(20).count(true));
-assert.eq(5, t.find({m: 5}).skip(45).limit(20).countReturn(), "L");
+assert.eq(5, coll.find({m: 5}).skip(45).limit(20).count(true));
// Negative skip values should return error
-var negSkipResult = db.runCommand({count: 't', skip: -2});
+var negSkipResult = db.runCommand({count: coll.getName(), skip: -2});
assert(!negSkipResult.ok, "negative skip value shouldn't work, n = " + negSkipResult.n);
assert(negSkipResult.errmsg.length > 0, "no error msg for negative skip");
+}());
diff --git a/jstests/core/crud_api.js b/jstests/core/crud_api.js
index 1f4ca21aac5..a6679cde794 100644
--- a/jstests/core/crud_api.js
+++ b/jstests/core/crud_api.js
@@ -667,10 +667,6 @@ var crudAPISpecTests = function crudAPISpecTests() {
var cursor = coll.find({}).sort({a: 1}).tailable(false);
assert.eq(2, (cursor._options & ~DBQuery.Option.slaveOk));
- // Check modifiers
- var cursor = coll.find({}).modifiers({$hint: 'a_1'});
- assert.eq('a_1', cursor._query['$hint']);
-
// allowPartialResults
var cursor = coll.find({}).allowPartialResults();
assert.eq(128, (cursor._options & ~DBQuery.Option.slaveOk));
diff --git a/jstests/libs/override_methods/set_read_preference_secondary.js b/jstests/libs/override_methods/set_read_preference_secondary.js
index 2b38510d5ee..74554cc9289 100644
--- a/jstests/libs/override_methods/set_read_preference_secondary.js
+++ b/jstests/libs/override_methods/set_read_preference_secondary.js
@@ -138,19 +138,19 @@ function runCommandWithReadPreferenceSecondary(
!bsonBinaryEqual({_: commandObj.$readPreference}, {_: kReadPreferenceSecondary})) {
throw new Error("Cowardly refusing to override read preference of command: " +
tojson(commandObj));
- }
+ } else if (!commandObj.hasOwnProperty("$readPreference")) {
+ if (commandObj === commandObjUnwrapped) {
+ // We wrap the command object using a "query" field rather than a "$query" field to
+ // match the implementation of DB.prototype._attachReadPreferenceToCommand().
+ commandObj = {query: commandObj};
+ } else {
+ // We create a copy of 'commandObj' to avoid mutating the parameter the caller
+ // specified.
+ commandObj = Object.assign({}, commandObj);
+ }
- if (commandObj === commandObjUnwrapped) {
- // We wrap the command object using a "query" field rather than a "$query" field to
- // match the implementation of DB.prototype._attachReadPreferenceToCommand().
- commandObj = {query: commandObj};
- } else {
- // We create a copy of 'commandObj' to avoid mutating the parameter the caller
- // specified.
- commandObj = Object.assign({}, commandObj);
+ commandObj.$readPreference = kReadPreferenceSecondary;
}
-
- commandObj.$readPreference = kReadPreferenceSecondary;
}
const serverResponse = func.apply(conn, makeFuncArgs(commandObj));
diff --git a/jstests/noPassthrough/client_metadata_slowlog.js b/jstests/noPassthrough/client_metadata_slowlog.js
index 1b10ff0a504..ef2c6a92d66 100644
--- a/jstests/noPassthrough/client_metadata_slowlog.js
+++ b/jstests/noPassthrough/client_metadata_slowlog.js
@@ -1,8 +1,6 @@
/**
* Test that verifies client metadata is logged as part of slow query logging in MongoD.
*/
-load("jstests/libs/logv2_helpers.js");
-
(function() {
'use strict';
@@ -23,14 +21,8 @@ assert.eq(count, 1, "expected 1 document");
print(`Checking ${conn.fullOptions.logFile} for client metadata message`);
let log = cat(conn.fullOptions.logFile);
-let predicate = null;
-if (isJsonLog(conn)) {
- predicate =
- /Slow query.*test.foo.*"appName":"MongoDB Shell".*"command":{"count":"foo","query":{"\$where":{"\$code":"function\(\)/;
-} else {
- predicate =
- /COMMAND .* command test.foo appName: "MongoDB Shell" command: count { count: "foo", query: { \$where: function\(\)/;
-}
+let predicate =
+ /Slow query.*test.foo.*"appName":"MongoDB Shell".*"command":{"count":"foo","query":{"\$where":{"\$code":"function\(\)/;
// Dump the log line by line to avoid log truncation
for (var a of log.split("\n")) {
diff --git a/jstests/noPassthrough/client_metadata_slowlog_rs.js b/jstests/noPassthrough/client_metadata_slowlog_rs.js
index d23b845a9bb..665a6453ba0 100644
--- a/jstests/noPassthrough/client_metadata_slowlog_rs.js
+++ b/jstests/noPassthrough/client_metadata_slowlog_rs.js
@@ -5,8 +5,6 @@
* requires_replication,
* ]
*/
-load("jstests/libs/logv2_helpers.js");
-
(function() {
'use strict';
diff --git a/jstests/noPassthrough/log_find_getmore.js b/jstests/noPassthrough/log_find_getmore.js
index 8fc0e425d87..6bbe7ecb3fc 100644
--- a/jstests/noPassthrough/log_find_getmore.js
+++ b/jstests/noPassthrough/log_find_getmore.js
@@ -2,8 +2,6 @@
* Confirms that the log output for find and getMore are in the expected format.
* @tags: [requires_profiling]
*/
-load("jstests/libs/logv2_helpers.js");
-
(function() {
"use strict";
@@ -55,17 +53,9 @@ cursor.next(); // Perform initial query and retrieve first document in batch.
let cursorid = getLatestProfilerEntry(testDB).cursorid;
let logLine = [
- 'command log_getmore.test appName: "MongoDB Shell" command: find { find: "test", filter:' +
- ' { a: { $gt: 0.0 } }, skip: 1.0, batchSize: 5.0, limit: 10.0, singleBatch: false, sort:' +
- ' { a: 1.0 }, hint: { a: 1.0 }',
- 'queryHash:'
+ '"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"find":"test","filter":{"a":{"$gt":0}},"skip":1,"batchSize":5,"limit":10,"singleBatch":false,"sort":{"a":1},"hint":{"a":1}',
+ '"queryHash":'
];
-if (isJsonLog(conn)) {
- logLine = [
- '"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"find":"test","filter":{"a":{"$gt":0}},"skip":1,"batchSize":5,"limit":10,"singleBatch":false,"sort":{"a":1},"hint":{"a":1}',
- '"queryHash":'
- ];
-}
// Check the logs to verify that find appears as above.
assertLogLineContains(conn, logLine);
@@ -87,23 +77,12 @@ function cursorIdToString(cursorId) {
}
logLine = [
- 'command log_getmore.test appName: "MongoDB Shell" command: getMore { getMore: ' +
- cursorIdToString(cursorid) + ', collection: "test", batchSize: 5.0',
- 'originatingCommand: { find: "test", ' +
- 'filter: { a: { $gt: 0.0 } }, skip: 1.0, batchSize: 5.0, limit: 10.0, singleBatch: ' +
- 'false, sort: { a: 1.0 }, hint: { a: 1.0 }',
- 'queryHash:'
+ `"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"getMore":${
+ cursorIdToString(cursorid)},"collection":"test","batchSize":5,`,
+ '"originatingCommand":{"find":"test","filter":{"a":{"$gt":0}},"skip":1,"batchSize":5,"limit":10,"singleBatch":false,"sort":{"a":1},"hint":{"a":1}',
+ '"queryHash":'
];
-if (isJsonLog(conn)) {
- logLine = [
- `"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"getMore":${
- cursorIdToString(cursorid)},"collection":"test","batchSize":5,`,
- '"originatingCommand":{"find":"test","filter":{"a":{"$gt":0}},"skip":1,"batchSize":5,"limit":10,"singleBatch":false,"sort":{"a":1},"hint":{"a":1}',
- '"queryHash":'
- ];
-}
-
assertLogLineContains(conn, logLine);
// TEST: Verify the log format of a getMore command following an aggregation.
@@ -113,20 +92,11 @@ cursorid = getLatestProfilerEntry(testDB).cursorid;
assert.eq(cursor.itcount(), 10);
logLine = [
- 'command log_getmore.test appName: "MongoDB Shell" command: getMore { getMore: ' +
- cursorIdToString(cursorid) + ', collection: "test"',
- 'originatingCommand: { aggregate: "test", pipeline: ' +
- '[ { $match: { a: { $gt: 0.0 } } } ], cursor: { batchSize: 0.0 }, hint: { a: 1.0 }'
+ `"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"getMore":${
+ cursorIdToString(cursorid)},"collection":"test"`,
+ '"originatingCommand":{"aggregate":"test","pipeline":[{"$match":{"a":{"$gt":0}}}],"cursor":{"batchSize":0},"hint":{"a":1}'
];
-if (isJsonLog(conn)) {
- logLine = [
- `"msg":"Slow query","attr":{"type":"command","ns":"log_getmore.test","appName":"MongoDB Shell","command":{"getMore":${
- cursorIdToString(cursorid)},"collection":"test"`,
- '"originatingCommand":{"aggregate":"test","pipeline":[{"$match":{"a":{"$gt":0}}}],"cursor":{"batchSize":0},"hint":{"a":1}'
- ];
-}
-
assertLogLineContains(conn, logLine);
MongoRunner.stopMongod(conn);
diff --git a/src/mongo/scripting/mozjs/dbquery.cpp b/src/mongo/scripting/mozjs/dbquery.cpp
index e196c8d6a72..0e12f899609 100644
--- a/src/mongo/scripting/mozjs/dbquery.cpp
+++ b/src/mongo/scripting/mozjs/dbquery.cpp
@@ -59,7 +59,9 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) {
o.setValue(InternedString::_collection, args.get(2));
o.setValue(InternedString::_ns, args.get(3));
- JS::RootedObject emptyObj(cx);
+ JSObject* newPlainObject = JS_NewPlainObject(cx);
+ uassert(6887104, "failed to create new JS object", newPlainObject);
+ JS::RootedObject emptyObj{cx, newPlainObject};
JS::RootedValue emptyObjVal(cx);
emptyObjVal.setObjectOrNull(emptyObj);
@@ -67,15 +69,15 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) {
nullVal.setNull();
if (args.length() > 4 && args.get(4).isObject()) {
- o.setValue(InternedString::_query, args.get(4));
+ o.setValue(InternedString::_filter, args.get(4));
} else {
- o.setValue(InternedString::_query, emptyObjVal);
+ o.setValue(InternedString::_filter, nullVal);
}
if (args.length() > 5 && args.get(5).isObject()) {
- o.setValue(InternedString::_fields, args.get(5));
+ o.setValue(InternedString::_projection, args.get(5));
} else {
- o.setValue(InternedString::_fields, nullVal);
+ o.setValue(InternedString::_projection, nullVal);
}
if (args.length() > 6 && args.get(6).isNumber()) {
@@ -102,9 +104,10 @@ void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) {
o.setNumber(InternedString::_options, 0);
}
+ o.setValue(InternedString::_additionalCmdParams, emptyObjVal);
+
o.setValue(InternedString::_cursor, nullVal);
o.setNumber(InternedString::_numReturned, 0);
- o.setBoolean(InternedString::_special, false);
args.rval().setObjectOrNull(thisv);
}
diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs
index 2fc2a8e5eff..1b5d102f36e 100644
--- a/src/mongo/scripting/mozjs/internedstring.defs
+++ b/src/mongo/scripting/mozjs/internedstring.defs
@@ -4,6 +4,7 @@
* with different definitions of MONGO_MOZJS_INTERNED_STRING.
*/
+MONGO_MOZJS_INTERNED_STRING(_additionalCmdParams, "_additionalCmdParams")
MONGO_MOZJS_INTERNED_STRING(arrayAccess, "arrayAccess")
MONGO_MOZJS_INTERNED_STRING(authenticated, "authenticated")
MONGO_MOZJS_INTERNED_STRING(_batchSize, "_batchSize")
@@ -22,8 +23,8 @@ MONGO_MOZJS_INTERNED_STRING(dollar_db, "$db")
MONGO_MOZJS_INTERNED_STRING(dollar_id, "$id")
MONGO_MOZJS_INTERNED_STRING(dollar_ref, "$ref")
MONGO_MOZJS_INTERNED_STRING(exactValueString, "exactValueString")
-MONGO_MOZJS_INTERNED_STRING(_fields, "_fields")
MONGO_MOZJS_INTERNED_STRING(fileName, "fileName")
+MONGO_MOZJS_INTERNED_STRING(_filter, "_filter")
MONGO_MOZJS_INTERNED_STRING(flags, "flags")
MONGO_MOZJS_INTERNED_STRING(floatApprox, "floatApprox")
MONGO_MOZJS_INTERNED_STRING(_fullName, "_fullName")
@@ -48,8 +49,8 @@ MONGO_MOZJS_INTERNED_STRING(_numReturned, "_numReturned")
MONGO_MOZJS_INTERNED_STRING(_options, "_options")
MONGO_MOZJS_INTERNED_STRING(options, "options")
MONGO_MOZJS_INTERNED_STRING(password, "password")
+MONGO_MOZJS_INTERNED_STRING(_projection, "_projection")
MONGO_MOZJS_INTERNED_STRING(prototype, "prototype")
-MONGO_MOZJS_INTERNED_STRING(_query, "_query")
MONGO_MOZJS_INTERNED_STRING(readOnly, "readOnly")
MONGO_MOZJS_INTERNED_STRING(reason, "reason")
MONGO_MOZJS_INTERNED_STRING(_retryWrites, "_retryWrites")
@@ -62,7 +63,6 @@ MONGO_MOZJS_INTERNED_STRING(singleton, "singleton")
MONGO_MOZJS_INTERNED_STRING(_skip, "_skip")
MONGO_MOZJS_INTERNED_STRING(slaveOk, "slaveOk")
MONGO_MOZJS_INTERNED_STRING(source, "source")
-MONGO_MOZJS_INTERNED_STRING(_special, "_special")
MONGO_MOZJS_INTERNED_STRING(stack, "stack")
MONGO_MOZJS_INTERNED_STRING(str, "str")
MONGO_MOZJS_INTERNED_STRING(top, "top")
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index cf828514c1b..caf27e11b7d 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -34,7 +34,6 @@
#include "mongo/bson/simple_bsonelement_comparator.h"
#include "mongo/client/client_api_version_parameters_gen.h"
-#include "mongo/client/client_deprecated.h"
#include "mongo/client/dbclient_base.h"
#include "mongo/client/dbclient_rs.h"
#include "mongo/client/global_conn_pool.h"
@@ -320,95 +319,31 @@ void MongoBase::Functions::_runCommandImpl::call(JSContext* cx, JS::CallArgs arg
});
}
-namespace {
-/**
- * WARNING: Do not add new callers! This is a special-purpose function that exists only to
- * accommodate the shell.
- *
- * Although OP_QUERY find is no longer supported by either the shell or the server, the shell's
- * exhaust path still internally constructs a request that resembles on OP_QUERY find. This function
- * converts this query to a 'FindCommandRequest'.
- */
-FindCommandRequest upconvertLegacyOpQueryToFindCommandRequest(NamespaceString nss,
- const BSONObj& opQueryFormattedBson,
- const BSONObj& projection,
- int limit,
- int skip,
- int batchSize,
- int queryOptions) {
- FindCommandRequest findCommand{std::move(nss)};
-
- client_deprecated::initFindFromLegacyOptions(opQueryFormattedBson, queryOptions, &findCommand);
-
- if (!projection.isEmpty()) {
- findCommand.setProjection(projection.getOwned());
- }
-
- if (limit) {
- // To avoid changing the behavior of the shell API, we allow the caller of the JS code to
- // use a negative limit to request at most a single batch.
- if (limit < 0) {
- findCommand.setLimit(-static_cast<int64_t>(limit));
- findCommand.setSingleBatch(true);
- } else {
- findCommand.setLimit(limit);
- }
- }
- if (skip) {
- findCommand.setSkip(skip);
- }
- if (batchSize) {
- findCommand.setBatchSize(batchSize);
- }
-
- return findCommand;
-}
-} // namespace
-
void MongoBase::Functions::find::call(JSContext* cx, JS::CallArgs args) {
auto scope = getScope(cx);
- if (args.length() != 7)
- uasserted(ErrorCodes::BadValue, "find needs 7 args");
-
- if (!args.get(1).isObject())
- uasserted(ErrorCodes::BadValue, "needs to be an object");
+ tassert(6887100, "wrong number of args for find operation", args.length() == 3);
+ tassert(6887101, "first arg must be an object", args.get(0).isObject());
+ tassert(6887102, "second arg must be an object", args.get(1).isObject());
+ tassert(6887103, "third arg must be a boolean", args.get(2).isBoolean());
auto conn = getConnection(args);
- std::string ns = ValueWriter(cx, args.get(0)).toString();
-
- BSONObj fields;
- BSONObj q = ValueWriter(cx, args.get(1)).toBSON();
-
- bool haveFields = false;
+ const BSONObj cmdObj = ValueWriter(cx, args.get(0)).toBSON();
+ const BSONObj readPreference = ValueWriter(cx, args.get(1)).toBSON();
+ const bool isExhaust = ValueWriter(cx, args.get(2)).toBoolean();
- if (args.get(2).isObject()) {
- JS::RootedObject obj(cx, args.get(2).toObjectOrNull());
-
- ObjectWrapper(cx, obj).enumerate([&](jsid) {
- haveFields = true;
- return false;
- });
+ FindCommandRequest findCmdRequest =
+ FindCommandRequest::parse(IDLParserContext("FindCommandRequest"), cmdObj);
+ ReadPreferenceSetting readPref;
+ if (!readPreference.isEmpty()) {
+ readPref = uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(readPreference));
}
+ ExhaustMode exhaustMode = isExhaust ? ExhaustMode::kOn : ExhaustMode::kOff;
- if (haveFields)
- fields = ValueWriter(cx, args.get(2)).toBSON();
-
- int limit = ValueWriter(cx, args.get(3)).toInt32();
- int nToSkip = ValueWriter(cx, args.get(4)).toInt32();
- int batchSize = ValueWriter(cx, args.get(5)).toInt32();
- int options = ValueWriter(cx, args.get(6)).toInt32();
-
- auto findCmd = upconvertLegacyOpQueryToFindCommandRequest(
- NamespaceString{ns}, q, fields, limit, nToSkip, batchSize, options);
- auto readPref = uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(q));
- ExhaustMode exhaustMode =
- ((options & QueryOption_Exhaust) != 0) ? ExhaustMode::kOn : ExhaustMode::kOff;
- std::unique_ptr<DBClientCursor> cursor = conn->find(std::move(findCmd), readPref, exhaustMode);
- if (!cursor.get()) {
- uasserted(ErrorCodes::InternalError, "error doing query: failed");
- }
+ std::unique_ptr<DBClientCursor> cursor =
+ conn->find(std::move(findCmdRequest), readPref, exhaustMode);
+ uassert(ErrorCodes::InternalError, "error doing query: failed", cursor);
JS::RootedObject c(cx);
scope->getProto<CursorInfo>().newObject(&c);
diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js
index 85c6b92c09d..2afb52f04d8 100644
--- a/src/mongo/shell/collection.js
+++ b/src/mongo/shell/collection.js
@@ -219,7 +219,7 @@ DBCollection.prototype._massageObject = function(q) {
throw Error("don't know how to massage : " + type);
};
-DBCollection.prototype.find = function(query, fields, limit, skip, batchSize, options) {
+DBCollection.prototype.find = function(filter, projection, limit, skip, batchSize, options) {
// Verify that API version parameters are not supplied via the shell helper.
assert.noAPIParams(options);
@@ -227,8 +227,8 @@ DBCollection.prototype.find = function(query, fields, limit, skip, batchSize, op
this._db,
this,
this._fullName,
- this._massageObject(query),
- fields,
+ this._massageObject(filter),
+ projection,
limit,
skip,
batchSize,
@@ -253,8 +253,9 @@ DBCollection.prototype.find = function(query, fields, limit, skip, batchSize, op
return cursor;
};
-DBCollection.prototype.findOne = function(query, fields, options, readConcern, collation) {
- var cursor = this.find(query, fields, -1 /* limit */, 0 /* skip*/, 0 /* batchSize */, options);
+DBCollection.prototype.findOne = function(filter, projection, options, readConcern, collation) {
+ var cursor =
+ this.find(filter, projection, -1 /* limit */, 0 /* skip*/, 0 /* batchSize */, options);
if (readConcern) {
cursor = cursor.readConcern(readConcern);
diff --git a/src/mongo/shell/explain_query.js b/src/mongo/shell/explain_query.js
index 453751d88de..f109ba3f72d 100644
--- a/src/mongo/shell/explain_query.js
+++ b/src/mongo/shell/explain_query.js
@@ -90,25 +90,22 @@ var DBExplainQuery = (function() {
// True means to always apply the skip and limit values.
innerCmd = this._query._convertToCountCmd(this._applySkipLimit);
} else {
- var canAttachReadPref = false;
- innerCmd = this._query._convertToCommand(canAttachReadPref);
+ innerCmd = this._query._convertToCommand();
}
var explainCmd = {explain: innerCmd};
explainCmd["verbosity"] = this._verbosity;
- // If "maxTimeMS" is set on innerCmd, it needs to be propagated to the top-level
- // of explainCmd so that it has the intended effect.
+
+ // If "maxTimeMS" or "$readPreference" are set on 'innerCmd', they need to be
+ // propagated to the top-level of 'explainCmd' in order to have the intended effect.
if (innerCmd.hasOwnProperty("maxTimeMS")) {
explainCmd.maxTimeMS = innerCmd.maxTimeMS;
}
-
- var explainDb = this._query._db;
-
- if ("$readPreference" in this._query._query) {
- var prefObj = this._query._query.$readPreference;
- explainCmd = explainDb._attachReadPreferenceToCommand(explainCmd, prefObj);
+ if (innerCmd.hasOwnProperty("$readPreference")) {
+ explainCmd["$readPreference"] = innerCmd["$readPreference"];
}
+ var explainDb = this._query._db;
var explainResult = explainDb.runReadCommand(explainCmd, null, this._query._options);
return Explainable.throwOrReturn(explainResult);
diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js
index fc8dd6e8731..d5a34bb20d8 100644
--- a/src/mongo/shell/query.js
+++ b/src/mongo/shell/query.js
@@ -1,22 +1,27 @@
// query.js
if (typeof DBQuery == "undefined") {
- DBQuery = function(mongo, db, collection, ns, query, fields, limit, skip, batchSize, options) {
- this._mongo = mongo; // 0
- this._db = db; // 1
- this._collection = collection; // 2
- this._ns = ns; // 3
-
- this._query = query || {}; // 4
- this._fields = fields; // 5
- this._limit = limit || 0; // 6
- this._skip = skip || 0; // 7
+ DBQuery = function(
+ mongo, db, collection, ns, filter, projection, limit, skip, batchSize, options) {
+ this._mongo = mongo;
+ this._db = db;
+ this._collection = collection;
+ this._ns = ns;
+
+ this._filter = filter || {};
+ this._projection = projection;
+ this._limit = limit || 0;
+ this._skip = skip || 0;
this._batchSize = batchSize || 0;
this._options = options || 0;
+ // This houses find command parameters which are not passed to this constructor function or
+ // held as properties of 'this'. When the find command represented by this 'DBQuery' is
+ // assembled, this object will be appended to the find command object verbatim.
+ this._additionalCmdParams = {};
+
this._cursor = null;
this._numReturned = 0;
- this._special = false;
this._prettyShell = false;
};
print("DBQuery probably won't have array access ");
@@ -63,27 +68,18 @@ DBQuery.prototype.help = function() {
};
DBQuery.prototype.clone = function() {
- var q = new DBQuery(this._mongo,
- this._db,
- this._collection,
- this._ns,
- this._query,
- this._fields,
- this._limit,
- this._skip,
- this._batchSize,
- this._options);
- q._special = this._special;
- return q;
-};
-
-DBQuery.prototype._ensureSpecial = function() {
- if (this._special)
- return;
-
- var n = {query: this._query};
- this._query = n;
- this._special = true;
+ const cloneResult = new DBQuery(this._mongo,
+ this._db,
+ this._collection,
+ this._ns,
+ this._filter,
+ this._projection,
+ this._limit,
+ this._skip,
+ this._batchSize,
+ this._options);
+ cloneResult._additionalCmdParams = this._additionalCmdParams;
+ return cloneResult;
};
DBQuery.prototype._checkModify = function() {
@@ -117,43 +113,43 @@ DBQuery.prototype._exec = function() {
assert.eq(0, this._numReturned);
this._cursorSeen = 0;
+ const findCmd = this._convertToCommand();
+
// We forbid queries with the exhaust option from running as 'DBCommandCursor', because
// 'DBCommandCursor' does not currently support exhaust.
//
// In the future, we could unify the shell's exhaust and non-exhaust code paths.
if (!this._isExhaustCursor()) {
- var canAttachReadPref = true;
- var findCmd = this._convertToCommand(canAttachReadPref);
- var cmdRes = this._db.runReadCommand(findCmd, null, this._options);
+ const cmdRes = this._db.runReadCommand(findCmd, null, this._options);
this._cursor = new DBCommandCursor(this._db, cmdRes, this._batchSize);
} else {
// The exhaust cursor option is disallowed under a session because it doesn't work as
- // expected, but all requests from the shell use implicit sessions, so to allow users
- // to continue using exhaust cursors through the shell, they are only disallowed with
+ // expected, but all requests from the shell use implicit sessions, so to allow users to
+ // continue using exhaust cursors through the shell, they are only disallowed with
// explicit sessions.
if (this._db.getSession()._isExplicit) {
throw new Error("Explicit session is not allowed for exhaust queries");
}
- if (this._special && this._query.readConcern) {
+ if (findCmd["readConcern"]) {
throw new Error("readConcern is not allowed for exhaust queries");
}
- if (this._special && this._query.collation) {
+ if (findCmd["collation"]) {
throw new Error("collation is not allowed for exhaust queries");
}
- if (this._special && this._query._allowDiskUse) {
+ if (findCmd["allowDiskUse"]) {
throw new Error("allowDiskUse is not allowed for exhaust queries");
}
- this._cursor = this._mongo.find(this._ns,
- this._query,
- this._fields,
- this._limit,
- this._skip,
- this._batchSize,
- this._options);
+ let readPreference = {};
+ if (findCmd["$readPreference"]) {
+ readPreference = findCmd["$readPreference"];
+ }
+
+ findCmd["$db"] = this._db.getName();
+ this._cursor = this._mongo.find(findCmd, readPreference, true /*isExhaust*/);
}
}
return this._cursor;
@@ -161,21 +157,14 @@ DBQuery.prototype._exec = function() {
/**
* Internal helper used to convert this cursor into the format required by the find command.
- *
- * If canAttachReadPref is true, may attach a read preference to the resulting command using the
- * "wrapped form": { $query: { <cmd>: ... }, $readPreference: { ... } }.
*/
-DBQuery.prototype._convertToCommand = function(canAttachReadPref) {
- var cmd = {};
+DBQuery.prototype._convertToCommand = function() {
+ let cmd = {};
cmd["find"] = this._collection.getName();
- if (this._special) {
- if (this._query.query) {
- cmd["filter"] = this._query.query;
- }
- } else if (this._query) {
- cmd["filter"] = this._query;
+ if (this._filter) {
+ cmd["filter"] = this._filter;
}
if (this._skip) {
@@ -201,52 +190,8 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) {
}
}
- if ("orderby" in this._query) {
- cmd["sort"] = this._query.orderby;
- }
-
- if (this._fields) {
- cmd["projection"] = this._fields;
- }
-
- if ("$hint" in this._query) {
- cmd["hint"] = this._query.$hint;
- }
-
- if ("$comment" in this._query) {
- cmd["comment"] = this._query.$comment;
- }
-
- if ("$maxTimeMS" in this._query) {
- cmd["maxTimeMS"] = this._query.$maxTimeMS;
- }
-
- if ("$max" in this._query) {
- cmd["max"] = this._query.$max;
- }
-
- if ("$min" in this._query) {
- cmd["min"] = this._query.$min;
- }
-
- if ("$returnKey" in this._query) {
- cmd["returnKey"] = this._query.$returnKey;
- }
-
- if ("$showDiskLoc" in this._query) {
- cmd["showRecordId"] = this._query.$showDiskLoc;
- }
-
- if ("readConcern" in this._query) {
- cmd["readConcern"] = this._query.readConcern;
- }
-
- if ("collation" in this._query) {
- cmd["collation"] = this._query.collation;
- }
-
- if ("allowDiskUse" in this._query) {
- cmd["allowDiskUse"] = this._query.allowDiskUse;
+ if (this._projection) {
+ cmd["projection"] = this._projection;
}
if ((this._options & DBQuery.Option.tailable) != 0) {
@@ -265,13 +210,7 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) {
cmd["allowPartialResults"] = true;
}
- if (canAttachReadPref) {
- // If there is a readPreference, use the wrapped command form.
- if ("$readPreference" in this._query) {
- var prefObj = this._query.$readPreference;
- cmd = this._db._attachReadPreferenceToCommand(cmd, prefObj);
- }
- }
+ cmd = Object.merge(cmd, this._additionalCmdParams);
return cmd;
};
@@ -306,20 +245,19 @@ DBQuery.prototype.hasNext = function() {
this._cursor.close();
return false;
}
- var o = this._cursor.hasNext();
- return o;
+ return this._cursor.hasNext();
};
DBQuery.prototype.next = function() {
this._exec();
- var o = this._cursor.hasNext();
+ let o = this._cursor.hasNext();
if (o)
this._cursorSeen++;
else
throw Error("error hasNext: " + o);
- var ret = this._cursor.next();
+ let ret = this._cursor.next();
if (ret.$err) {
throw _getErrorWithCode(ret, "error: " + tojson(ret));
}
@@ -331,7 +269,7 @@ DBQuery.prototype.next = function() {
DBQuery.prototype.objsLeftInBatch = function() {
this._exec();
- var ret = this._cursor.objsLeftInBatch();
+ let ret = this._cursor.objsLeftInBatch();
if (ret.$err)
throw _getErrorWithCode(ret, "error: " + tojson(ret));
@@ -353,7 +291,7 @@ DBQuery.prototype.toArray = function() {
if (this._arr)
return this._arr;
- var a = [];
+ let a = [];
while (this.hasNext())
a.push(this.next());
this._arr = a;
@@ -361,42 +299,41 @@ DBQuery.prototype.toArray = function() {
};
DBQuery.prototype._convertToCountCmd = function(applySkipLimit) {
- var cmd = {count: this._collection.getName()};
+ let cmd = {count: this._collection.getName()};
- if (this._query) {
- if (this._special) {
- cmd.query = this._query.query;
- if (this._query.$maxTimeMS) {
- cmd.maxTimeMS = this._query.$maxTimeMS;
- }
- if (this._query.$hint) {
- cmd.hint = this._query.$hint;
- }
- if (this._query.readConcern) {
- cmd.readConcern = this._query.readConcern;
- }
- if (this._query.collation) {
- cmd.collation = this._query.collation;
- }
- } else {
- cmd.query = this._query;
- }
+ if (this._filter) {
+ cmd["query"] = this._filter;
+ }
+
+ if (this._additionalCmdParams["maxTimeMS"]) {
+ cmd["maxTimeMS"] = this._additionalCmdParams["maxTimeMS"];
+ }
+ if (this._additionalCmdParams["hint"]) {
+ cmd["hint"] = this._additionalCmdParams["hint"];
+ }
+ if (this._additionalCmdParams["readConcern"]) {
+ cmd["readConcern"] = this._additionalCmdParams["readConcern"];
+ }
+ if (this._additionalCmdParams["collation"]) {
+ cmd["collation"] = this._additionalCmdParams["collation"];
}
if (applySkipLimit) {
- if (this._limit)
- cmd.limit = this._limit;
- if (this._skip)
- cmd.skip = this._skip;
+ if (this._limit) {
+ cmd["limit"] = this._limit;
+ }
+ if (this._skip) {
+ cmd["skip"] = this._skip;
+ }
}
return cmd;
};
DBQuery.prototype.count = function(applySkipLimit) {
- var cmd = this._convertToCountCmd(applySkipLimit);
+ let cmd = this._convertToCountCmd(applySkipLimit);
- var res = this._db.runReadCommand(cmd);
+ let res = this._db.runReadCommand(cmd);
if (res && res.n != null)
return res.n;
throw _getErrorWithCode(res, "count failed: " + tojson(res));
@@ -406,34 +343,22 @@ DBQuery.prototype.size = function() {
return this.count(true);
};
-DBQuery.prototype.countReturn = function() {
- var c = this.count();
-
- if (this._skip)
- c = c - this._skip;
-
- if (this._limit > 0 && this._limit < c)
- return this._limit;
-
- return c;
-};
-
/**
* iterative count - only for testing
*/
DBQuery.prototype.itcount = function() {
- var num = 0;
+ let num = 0;
// Track how many bytes we've used this cursor to iterate iterated. This function can be called
// with some very large cursors. SpiderMonkey appears happy to allow these objects to
// accumulate, so regular gc() avoids an overly large memory footprint.
//
// TODO: migrate this function into c++
- var bytesSinceGC = 0;
+ let bytesSinceGC = 0;
while (this.hasNext()) {
num++;
- var nextDoc = this.next();
+ let nextDoc = this.next();
bytesSinceGC += Object.bsonsize(nextDoc);
// Garbage collect every 10 MB.
@@ -449,26 +374,28 @@ DBQuery.prototype.length = function() {
return this.toArray().length;
};
-DBQuery.prototype._addSpecial = function(name, value) {
- this._ensureSpecial();
- this._query[name] = value;
- return this;
-};
-
DBQuery.prototype.sort = function(sortBy) {
- return this._addSpecial("orderby", sortBy);
+ this._checkModify();
+ this._additionalCmdParams["sort"] = sortBy;
+ return this;
};
DBQuery.prototype.hint = function(hint) {
- return this._addSpecial("$hint", hint);
+ this._checkModify();
+ this._additionalCmdParams["hint"] = hint;
+ return this;
};
DBQuery.prototype.min = function(min) {
- return this._addSpecial("$min", min);
+ this._checkModify();
+ this._additionalCmdParams["min"] = min;
+ return this;
};
DBQuery.prototype.max = function(max) {
- return this._addSpecial("$max", max);
+ this._checkModify();
+ this._additionalCmdParams["max"] = max;
+ return this;
};
/**
@@ -479,39 +406,49 @@ DBQuery.prototype.showDiskLoc = function() {
};
DBQuery.prototype.showRecordId = function() {
- return this._addSpecial("$showDiskLoc", true);
+ this._checkModify();
+ this._additionalCmdParams["showRecordId"] = true;
+ return this;
};
DBQuery.prototype.maxTimeMS = function(maxTimeMS) {
- return this._addSpecial("$maxTimeMS", maxTimeMS);
+ this._checkModify();
+ this._additionalCmdParams["maxTimeMS"] = maxTimeMS;
+ return this;
};
DBQuery.prototype.readConcern = function(level, atClusterTime = undefined) {
- var readConcernObj =
+ this._checkModify();
+ let readConcernObj =
atClusterTime ? {level: level, atClusterTime: atClusterTime} : {level: level};
-
- return this._addSpecial("readConcern", readConcernObj);
+ this._additionalCmdParams["readConcern"] = readConcernObj;
+ return this;
};
DBQuery.prototype.collation = function(collationSpec) {
- return this._addSpecial("collation", collationSpec);
+ this._checkModify();
+ this._additionalCmdParams["collation"] = collationSpec;
+ return this;
};
DBQuery.prototype.allowDiskUse = function(value) {
- return this._addSpecial("allowDiskUse", (value === undefined ? true : value));
+ this._checkModify();
+ value = (value === undefined) ? true : value;
+ this._additionalCmdParams["allowDiskUse"] = value;
+ return this;
};
/**
* Sets the read preference for this cursor.
*
- * @param mode {string} read preference mode to use.
- * @param tagSet {Array.<Object>} optional. The list of tags to use, order matters.
- * @param hedgeOptions {<Object>} optional. The hedge options of the form {enabled: <bool>}.
+ * 'mode': A string indicating read preference mode to use.
+ * 'tagSet': An optional list of tags to use. Order matters.
+ * 'hedgeOptions': An optional object of the form {enabled: <bool>}.
*
- * @return this cursor
+ * Returns 'this'.
*/
DBQuery.prototype.readPref = function(mode, tagSet, hedgeOptions) {
- var readPrefObj = {mode: mode};
+ let readPrefObj = {mode: mode};
if (tagSet) {
readPrefObj.tags = tagSet;
@@ -521,7 +458,8 @@ DBQuery.prototype.readPref = function(mode, tagSet, hedgeOptions) {
readPrefObj.hedge = hedgeOptions;
}
- return this._addSpecial("$readPreference", readPrefObj);
+ this._additionalCmdParams["$readPreference"] = readPrefObj;
+ return this;
};
DBQuery.prototype.forEach = function(func) {
@@ -530,7 +468,7 @@ DBQuery.prototype.forEach = function(func) {
};
DBQuery.prototype.map = function(func) {
- var a = [];
+ let a = [];
while (this.hasNext())
a.push(func(this.next()));
return a;
@@ -541,16 +479,20 @@ DBQuery.prototype.arrayAccess = function(idx) {
};
DBQuery.prototype.comment = function(comment) {
- return this._addSpecial("$comment", comment);
+ this._checkModify();
+ this._additionalCmdParams["comment"] = comment;
+ return this;
};
DBQuery.prototype.explain = function(verbose) {
- var explainQuery = new DBExplainQuery(this, verbose);
+ let explainQuery = new DBExplainQuery(this, verbose);
return explainQuery.finish();
};
DBQuery.prototype.returnKey = function() {
- return this._addSpecial("$returnKey", true);
+ this._checkModify();
+ this._additionalCmdParams["returnKey"] = true;
+ return this;
};
DBQuery.prototype.pretty = function() {
@@ -560,15 +502,15 @@ DBQuery.prototype.pretty = function() {
DBQuery.prototype.shellPrint = function() {
try {
- var start = new Date().getTime();
- var n = 0;
+ let start = new Date().getTime();
+ let n = 0;
while (this.hasNext() && n < DBQuery.shellBatchSize) {
- var s = this._prettyShell ? tojson(this.next()) : tojson(this.next(), "", true);
+ let s = this._prettyShell ? tojson(this.next()) : tojson(this.next(), "", true);
print(s);
n++;
}
if (typeof _verboseShell !== 'undefined' && _verboseShell) {
- var time = new Date().getTime() - start;
+ let time = new Date().getTime() - start;
print("Fetched " + n + " record(s) in " + time + "ms");
}
if (this.hasNext()) {
@@ -583,7 +525,7 @@ DBQuery.prototype.shellPrint = function() {
};
DBQuery.prototype.toString = function() {
- return "DBQuery: " + this._ns + " -> " + tojson(this._query);
+ return "DBQuery: " + this._ns + " -> " + tojson(this._filter);
};
//
@@ -627,7 +569,7 @@ DBQuery.prototype.noCursorTimeout = function() {
*/
DBQuery.prototype.projection = function(document) {
this._checkModify();
- this._fields = document;
+ this._projection = document;
return this;
};
@@ -652,30 +594,6 @@ DBQuery.prototype.tailable = function(awaitData) {
return this;
};
-/**
- * Specify a document containing modifiers for the query.
- *
- * @method
- * @see http://docs.mongodb.org/manual/reference/operator/query-modifier/
- * @param {object} document A document containing modifers to apply to the cursor.
- * @return {DBQuery}
- */
-DBQuery.prototype.modifiers = function(document) {
- this._checkModify();
-
- for (var name in document) {
- if (name[0] != '$') {
- throw new Error('All modifiers must start with a $ such as $returnKey');
- }
- }
-
- for (var name in document) {
- this._addSpecial(name, document[name]);
- }
-
- return this;
-};
-
DBQuery.prototype.close = function() {
if (this._cursor) {
this._cursor.close();
@@ -772,11 +690,11 @@ DBCommandCursor.prototype.isExhausted = function() {
DBCommandCursor.prototype.close = function() {
if (bsonWoCompare({_: this._cursorid}, {_: NumberLong(0)}) !== 0) {
- var killCursorCmd = {
+ let killCursorCmd = {
killCursors: this._collName,
cursors: [this._cursorid],
};
- var cmdRes = this._db.runCommand(killCursorCmd);
+ let cmdRes = this._db.runCommand(killCursorCmd);
if (cmdRes.ok != 1) {
throw _getErrorWithCode(cmdRes, "killCursors command failed: " + tojson(cmdRes));
}
@@ -806,7 +724,7 @@ DBCommandCursor.prototype._updatePostBatchResumeToken = function(cursorObj) {
*/
DBCommandCursor.prototype._runGetMoreCommand = function() {
// Construct the getMore command.
- var getMoreCmd = {getMore: this._cursorid, collection: this._collName};
+ let getMoreCmd = {getMore: this._cursorid, collection: this._collName};
if (this._batchSize) {
getMoreCmd["batchSize"] = this._batchSize;
@@ -823,7 +741,7 @@ DBCommandCursor.prototype._runGetMoreCommand = function() {
}
// Deliver the getMore command, and check for errors in the response.
- var cmdRes = this._db.runCommand(getMoreCmd);
+ let cmdRes = this._db.runCommand(getMoreCmd);
assert.commandWorked(cmdRes, () => "getMore command failed: " + tojson(cmdRes));
if (this._ns !== cmdRes.cursor.ns) {