summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2015-01-09 18:04:11 -0500
committerBenety Goh <benety@mongodb.com>2015-01-15 11:01:58 -0500
commit24daf1360e9bda29bf14eb209fa32d85e673a5a7 (patch)
treed8d787fcca94023d99781fd6aeb9f156442827b7
parent14424674ca204de09b8f9b37fc007cbde81d1bc9 (diff)
downloadmongo-24daf1360e9bda29bf14eb209fa32d85e673a5a7.tar.gz
SERVER-16782 db.collection.stats() supports toggling of indexDetails and display of single index stats.
-rw-r--r--jstests/core/apitest_dbcollection.js87
-rw-r--r--src/mongo/shell/collection.js55
2 files changed, 139 insertions, 3 deletions
diff --git a/jstests/core/apitest_dbcollection.js b/jstests/core/apitest_dbcollection.js
index 22a837d2821..71af054beab 100644
--- a/jstests/core/apitest_dbcollection.js
+++ b/jstests/core/apitest_dbcollection.js
@@ -135,3 +135,90 @@ db.getCollection( "test_db" ).find();
db.getCollection( "test_db" ).drop();
assert(db.getCollection( "test_db" ).getIndexes().length == 0,24);
+
+/*
+ * stats()
+ */
+
+ (function() {
+ var t = db.apttest_dbcollection;
+
+ // Non-existent collection.
+ t.drop();
+ assert.commandFailed(t.stats(),
+ 'db.collection.stats() should fail on non-existent collection');
+
+ // scale - passed to stats() as sole numerical argument or part of an options object.
+ t.drop();
+ assert.commandWorked(db.createCollection(t.getName(), {capped: true, size: 10*1024*1024}));
+ var collectionStats = assert.commandWorked(t.stats(1024*1024));
+ assert.eq(10, collectionStats.maxSize,
+ 'db.collection.stats(scale) - capped collection size scaled incorrectly: ' +
+ tojson(collectionStats));
+ var collectionStats = assert.commandWorked(t.stats({scale: 1024*1024}));
+ assert.eq(10, collectionStats.maxSize,
+ 'db.collection.stats({scale: N}) - capped collection size scaled incorrectly: ' +
+ tojson(collectionStats));
+
+ // indexDetails - If true, includes 'indexDetails' field in results. Default: false.
+ t.drop();
+ t.save({a: 1});
+ t.ensureIndex({a: 1});
+ collectionStats = assert.commandWorked(t.stats());
+ assert(!collectionStats.hasOwnProperty('indexDetails'),
+ 'unexpected indexDetails found in db.collection.stats() result: ' +
+ tojson(collectionStats));
+ collectionStats = assert.commandWorked(t.stats({indexDetails: false}));
+ assert(!collectionStats.hasOwnProperty('indexDetails'),
+ 'unexpected indexDetails found in db.collection.stats({indexDetails: true}) result: ' +
+ tojson(collectionStats));
+ collectionStats = assert.commandWorked(t.stats({indexDetails: true}));
+ assert(collectionStats.hasOwnProperty('indexDetails'),
+ 'indexDetails missing from db.collection.stats({indexDetails: true}) result: ' +
+ tojson(collectionStats));
+
+ // Returns index name.
+ function getIndexName(indexKey) {
+ var indexes = t.getIndexes().filter(function(doc) {
+ return friendlyEqual(doc.key, indexKey);
+ });
+ assert.eq(1, indexes.length, tojson(indexKey) + ' not found in getIndexes() result: ' +
+ tojson(t.getIndexes()));
+ return indexes[0].name;
+ }
+
+ function checkIndexDetails(options, indexName) {
+ var collectionStats = assert.commandWorked(t.stats(options));
+ assert(collectionStats.hasOwnProperty('indexDetails'),
+ 'indexDetails missing from ' + 'db.collection.stats(' + tojson(options) +
+ ') result: ' + tojson(collectionStats));
+ // Currently, indexDetails is only supported with WiredTiger.
+ if (jsTest.options().storageEngine == undefined) { return; }
+ if (jsTest.options().storageEngine.toLowerCase() != "wiredtiger") { return; }
+ assert.eq(1, Object.keys(collectionStats.indexDetails).length,
+ 'indexDetails must have exactly one entry');
+ assert(collectionStats.indexDetails[indexName],
+ indexName + ' missing from indexDetails: ' + tojson(collectionStats.indexDetails));
+ assert.neq(0, Object.keys(collectionStats.indexDetails[indexName]).length,
+ indexName + ' exists in indexDetails but contains no information: ' +
+ tojson(collectionStats));
+ }
+
+ // indexDetailsKey - show indexDetails results for this index key only.
+ var indexKey = {a: 1};
+ var indexName = getIndexName(indexKey);
+ checkIndexDetails({indexDetails: true, indexDetailsKey: indexKey}, indexName);
+
+ // indexDetailsName - show indexDetails results for this index name only.
+ checkIndexDetails({indexDetails: true, indexDetailsName: indexName}, indexName);
+
+ // Cannot specify both indexDetailsKey and indexDetailsName.
+ var error = assert.throws(function() {
+ t.stats({indexDetails: true, indexDetailsKey: indexKey, indexDetailsName: indexName});
+ }, null, 'indexDetailsKey and indexDetailsName cannot be used at the same time');
+ assert.eq(Error, error.constructor,
+ 'db.collection.stats() failed when both indexDetailsKey and indexDetailsName ' +
+ 'are used but with incorrect error type');
+
+ t.drop();
+ }());
diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js
index 4ce212201b7..c0033a3dbce 100644
--- a/src/mongo/shell/collection.js
+++ b/src/mongo/shell/collection.js
@@ -62,7 +62,8 @@ DBCollection.prototype.help = function () {
print("\tdb." + shortName + ".renameCollection( newName , <dropTarget> ) renames the collection.");
print("\tdb." + shortName + ".runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name");
print("\tdb." + shortName + ".save(obj)");
- print("\tdb." + shortName + ".stats()");
+ print("\tdb." + shortName + ".stats({scale: N, indexDetails: true/false, " +
+ "indexDetailsKey: <index key>, indexDetailsName: <index name>})");
// print("\tdb." + shortName + ".diskStorageStats({[extent: <num>,] [granularity: <bytes>,] ...}) - analyze record layout on disk");
// print("\tdb." + shortName + ".pagesInRAM({[extent: <num>,] [granularity: <bytes>,] ...}) - analyze resident memory pages");
print("\tdb." + shortName + ".storageSize() - includes free space allocated to this collection");
@@ -1095,8 +1096,56 @@ DBCollection.prototype.getCollection = function( subName ){
return this._db.getCollection( this._shortName + "." + subName );
}
-DBCollection.prototype.stats = function( scale ){
- return this._db.runCommand( { collstats : this._shortName , scale : scale } );
+/**
+ * scale: The scale at which to deliver results. Unless specified, this command returns all data
+ * in bytes.
+ * indexDetails: Includes indexDetails field in results. Default: false.
+ * indexDetailsKey: If indexDetails is true, filter contents in indexDetails by this index key.
+ * indexDetailsname: If indexDetails is true, filter contents in indexDetails by this index name.
+ *
+ * It is an error to provide both indexDetailsKey and indexDetailsName.
+ */
+DBCollection.prototype.stats = function(args) {
+ 'use strict';
+
+ // For backwards compatibility with db.collection.stats(scale).
+ var scale = isObject(args) ? args.scale : args;
+
+ var options = isObject(args) ? args : {};
+ if (options.indexDetailsKey && options.indexDetailsName) {
+ throw new Error('Cannot filter indexDetails on both indexDetailsKey and ' +
+ 'indexDetailsName');
+ }
+
+ var res = this._db.runCommand({collStats: this._shortName, scale: scale});
+ if (!res.ok) {
+ return res;
+ }
+
+ var newIndexDetails = {};
+ if (res.indexDetails) {
+ if (!options.indexDetails) {
+ delete res.indexDetails;
+ }
+ else if (isObject(options.indexDetailsKey)) {
+ this.getIndexes().forEach(function(spec) {
+ if (friendlyEqual(spec.key, options.indexDetailsKey) &&
+ res.indexDetails[spec.name]) {
+ newIndexDetails[spec.name] = res.indexDetails[spec.name];
+ }
+ });
+ res.indexDetails = newIndexDetails;
+ }
+ else if (options.indexDetailsName) {
+ if (res.indexDetails[options.indexDetailsName]) {
+ newIndexDetails[options.indexDetailsName] =
+ res.indexDetails[options.indexDetailsName];
+ }
+ res.indexDetails = newIndexDetails;
+ }
+ }
+
+ return res;
}
DBCollection.prototype.dataSize = function(){