summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJason Rassi <rassi@10gen.com>2014-12-16 14:23:48 -0500
committerJason Rassi <rassi@10gen.com>2014-12-16 14:31:58 -0500
commit0aede468dc2ea475aa83bddd8be1e56a19ca6a3a (patch)
tree97e72eefddfc6f533635b38ff7b7994f6f683233 /src/mongo
parent7917b37c07d67dfb0e4bc8336942526962dceef3 (diff)
downloadmongo-0aede468dc2ea475aa83bddd8be1e56a19ca6a3a.tar.gz
SERVER-16518 listCollections response changed to cursor object form
As a temporary compatibility measure, the legacy behavior is preserved if the "cursor" option is not sent with the command. This compatibility layer will be removed as part of work for the parent ticket.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/dbclient.cpp12
-rw-r--r--src/mongo/db/commands.cpp11
-rw-r--r--src/mongo/db/commands.h12
-rw-r--r--src/mongo/db/commands/list_collections.cpp21
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp7
-rw-r--r--src/mongo/shell/collection.js7
-rw-r--r--src/mongo/shell/db.js44
7 files changed, 85 insertions, 29 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp
index 3687c076dd8..714e657760d 100644
--- a/src/mongo/client/dbclient.cpp
+++ b/src/mongo/client/dbclient.cpp
@@ -900,12 +900,20 @@ namespace mongo {
// TODO(spencer): remove fallback behavior after 2.8
{
+ // TODO: This implementation only reads the first batch of results from the
+ // listCollections command, and masserts if there are multiple batches to read. A
+ // correct implementation needs to instantiate a command cursor from the command
+ // response object, and use it to read in all of the command results.
BSONObj res;
if (runCommand(db,
- BSON("listCollections" << 1 << "filter" << filter),
+ BSON("listCollections" << 1 << "filter" << filter
+ << "cursor" << BSONObj()),
res,
QueryOption_SlaveOk)) {
- BSONObj collections = res["collections"].Obj();
+ BSONObj cursorObj = res["cursor"].Obj();
+ massert(28586, "reading multiple batches from listCollections not implemented",
+ cursorObj["id"].numberInt() == 0);
+ BSONObj collections = cursorObj["firstBatch"].Obj();
BSONObjIterator it( collections );
while ( it.more() ) {
BSONElement e = it.next();
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 37038ce0dd9..57892c764fa 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -249,6 +249,17 @@ namespace mongo {
return mongo::getStatusFromCommandResult(result);
}
+ void Command::appendCursorResponseObject(long long cursorId,
+ StringData cursorNamespace,
+ BSONArray firstBatch,
+ BSONObjBuilder* builder) {
+ BSONObjBuilder cursorObj(builder->subobjStart("cursor"));
+ cursorObj.append("id", cursorId);
+ cursorObj.append("ns", cursorNamespace);
+ cursorObj.append("firstBatch", firstBatch);
+ cursorObj.done();
+ }
+
Status Command::checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 21baf7b22bc..f36ba4fb9d9 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -283,6 +283,18 @@ namespace mutablebson {
// not look like the result of a command.
static Status getStatusFromCommandResult(const BSONObj& result);
+ /**
+ * Builds a cursor response object from the provided cursor identifiers and "firstBatch",
+ * and appends the response object to the provided builder under the field name "cursor".
+ *
+ * The response object has the following format:
+ * { id: <NumberLong>, ns: <String>, firstBatch: <Array> }.
+ */
+ static void appendCursorResponseObject(long long cursorId,
+ StringData cursorNamespace,
+ BSONArray firstBatch,
+ BSONObjBuilder* builder);
+
// Set by command line. Controls whether or not testing-only commands should be available.
static int testCommandsEnabled;
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index 874c1b7e555..3115da00e9a 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -90,8 +90,13 @@ namespace mongo {
matcher.reset( parsed.getValue() );
}
- BSONArrayBuilder arr;
+ // TODO: Handle options specified in the command request object under the "cursor"
+ // field.
+
+ // TODO: If the full result set does not fit in one batch, allocate a cursor to store
+ // the remainder of the results.
+ BSONArrayBuilder arr;
for ( list<string>::const_iterator i = names.begin(); i != names.end(); ++i ) {
string ns = *i;
@@ -115,7 +120,19 @@ namespace mongo {
arr.append( maybe );
}
- result.append( "collections", arr.arr() );
+ // TODO: As a temporary compatibility measure for drivers that have not yet been updated
+ // to handle the new cursor response object, we maintain the legacy command format if
+ // the field "cursor" is not present in the request object. This compatibility layer
+ // should be eventually removed.
+ if ( jsobj["cursor"].type() == mongo::Object ) {
+ const long long cursorId = 0LL;
+ std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name;
+ Command::appendCursorResponseObject( cursorId, cursorNamespace, arr.arr(),
+ &result );
+ }
+ else {
+ result.append( "collections", arr.arr() );
+ }
return true;
}
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index aa8438dc7ed..ae6ab10004a 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -155,11 +155,8 @@ namespace mongo {
exec->saveState();
}
- BSONObjBuilder cursorObj(result.subobjStart("cursor"));
- cursorObj.append("id", cursor ? cursor->cursorid() : 0LL);
- cursorObj.append("ns", ns);
- cursorObj.append("firstBatch", resultsArray.arr());
- cursorObj.done();
+ const long long cursorId = cursor ? cursor->cursorid() : 0LL;
+ Command::appendCursorResponseObject(cursorId, ns, resultsArray.arr(), &result);
}
diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js
index 8262710d102..a1daa717e1d 100644
--- a/src/mongo/shell/collection.js
+++ b/src/mongo/shell/collection.js
@@ -1150,11 +1150,12 @@ DBCollection.prototype.convertToCapped = function( bytes ){
DBCollection.prototype.exists = function(){
var res = this._db.runCommand( "listCollections",
- { filter : { name : this._shortName } } );
+ { filter : { name : this._shortName } , cursor : {} } );
if ( res.ok ) {
- if ( res.collections.length == 0 )
+ var cursor = new DBCommandCursor( this._mongo, res );
+ if ( !cursor.hasNext() )
return null;
- return res.collections[0];
+ return cursor.next();
}
if ( res.errmsg && res.errmsg.startsWith( "no such cmd" ) ) {
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index 7c109bfeeb8..c61e8231a5b 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -333,6 +333,7 @@ DB.prototype.help = function() {
print("\tdb.fsyncLock() flush data to disk and lock server for backups");
print("\tdb.fsyncUnlock() unlocks server following a db.fsyncLock()");
print("\tdb.getCollection(cname) same as db['cname'] or db.cname");
+ print("\tdb.getCollectionInfos()");
print("\tdb.getCollectionNames()");
print("\tdb.getLastError() - just returns the err msg string");
print("\tdb.getLastErrorObj() - return full status object");
@@ -605,27 +606,30 @@ DB.prototype.getPrevError = function(){
return this.runCommand( { getpreverror : 1 } );
}
-DB.prototype._getCollectionNamesSystemNamespaces = function(){
+DB.prototype._getCollectionInfosSystemNamespaces = function(){
var all = [];
var nsLength = this._name.length + 1;
var c = this.getCollection( "system.namespaces" ).find();
while ( c.hasNext() ){
- var name = c.next().name;
+ var infoObj = c.next();
- if ( name.indexOf( "$" ) >= 0 && name.indexOf( ".oplog.$" ) < 0 )
+ if ( infoObj.name.indexOf( "$" ) >= 0 && infoObj.name.indexOf( ".oplog.$" ) < 0 )
continue;
- all.push( name.substring( nsLength ) );
+ infoObj.name = infoObj.name.substring( nsLength );
+
+ all.push( infoObj );
}
- return all.sort();
+ // Return list of objects sorted by collection name.
+ return all.sort(function(coll1, coll2) { return coll1.name.localeCompare(coll2.name); });
}
-DB.prototype._getCollectionNamesCommand = function() {
- var res = this.runCommand( "listCollections" );
+DB.prototype._getCollectionInfosCommand = function() {
+ var res = this.runCommand( "listCollections", { cursor: {} } );
if ( res.code == 59 ) {
// command doesn't exist, old mongod
return null;
@@ -639,21 +643,27 @@ DB.prototype._getCollectionNamesCommand = function() {
throw Error( "listCollections failed: " + tojson( res ) );
}
- var all = [];
- for ( var i = 0; i < res.collections.length; i++ ) {
- var name = res.collections[i].name;
- all.push( name );
- }
-
- return all;
+ // The listCollections command returns its results sorted by collection name. There's no need
+ // to re-sort.
+ return new DBCommandCursor(this._mongo, res).toArray();
}
-DB.prototype.getCollectionNames = function(){
- var res = this._getCollectionNamesCommand();
+/**
+ * Returns this database's list of collection metadata objects, sorted by collection name.
+ */
+DB.prototype.getCollectionInfos = function() {
+ var res = this._getCollectionInfosCommand();
if ( res ) {
return res;
}
- return this._getCollectionNamesSystemNamespaces();
+ return this._getCollectionInfosSystemNamespaces();
+}
+
+/**
+ * Returns this database's list of collection names in sorted order.
+ */
+DB.prototype.getCollectionNames = function() {
+ return this.getCollectionInfos().map(function(infoObj) { return infoObj.name; });
}
DB.prototype.tojson = function(){