diff options
author | Benety Goh <benety@mongodb.com> | 2015-01-09 18:04:11 -0500 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2015-01-15 11:01:58 -0500 |
commit | 24daf1360e9bda29bf14eb209fa32d85e673a5a7 (patch) | |
tree | d8d787fcca94023d99781fd6aeb9f156442827b7 | |
parent | 14424674ca204de09b8f9b37fc007cbde81d1bc9 (diff) | |
download | mongo-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.js | 87 | ||||
-rw-r--r-- | src/mongo/shell/collection.js | 55 |
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(){ |