Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions scout-brain/lib/models/_index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// @todo: When schema for Index finalized in server,
// make them real props here.
module.exports = require('ampersand-model').extend({
extraProperties: 'allow'
idAttribute: '_id',
props: {
key: 'object',
name: 'string',
ns: 'string',
v: 'number',
size: 'number'
},
derived: {
_id: {
deps: ['name', 'ns'],
fn: function() {
return this.ns + '.' + this.name;
}
}
}
});
126 changes: 100 additions & 26 deletions scout-brain/lib/models/collection.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
var AmpersandState = require('ampersand-state');
var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-collection');
var debug = require('debug')('scout-brain:models:collection');

var _ = require('underscore');
var types = require('../types');

// @todo: When schema for Index finalized in server,
// make them real props here.
var CollectionIndex = AmpersandState.extend({
extraProperties: 'allow'
});

var CollectionIndexes = AmpersandCollection.extend({
model: CollectionIndex
});
var IndexCollection = require('./_index-collection');

var Collection = AmpersandModel.extend({
idAttribute: '_id',
Expand All @@ -23,7 +11,6 @@ var Collection = AmpersandModel.extend({
required: true
},
database: 'string',
index_sizes: 'number',
document_count: 'number',
document_size: 'number',
storage_size: 'number',
Expand All @@ -32,31 +19,118 @@ var Collection = AmpersandModel.extend({
padding_factor: 'number',
extent_count: 'number',
extent_last_size: 'number',
/**
* http://docs.mongodb.org/manual/reference/command/collStats/#collStats.userFlags
*/
flags_user: 'number',
flags_system: 'number'
flags_system: 'number',
/**
* Is this a capped collection?
*/
capped: {
type: 'boolean',
default: false
},
/**
* Is this collection using power of 2 allocation?
*
* http://docs.mongodb.org/manual/core/storage/#power-of-2-allocation
*/
power_of_two: {
type: 'boolean',
default: true
},
/**
* The total size in memory of all records in a collection. This value does
* not include the record header, which is 16 bytes per record, but does
* include the record’s padding. Additionally size does not include the
* size of any indexes associated with the collection, which the
* totalIndexSize field reports..
*
* http://docs.mongodb.org/manual/reference/command/collStats/#collStats.size
*/
size: 'number',
/**
* New in version 3.0.0.
*
* A document that reports data from the storage engine for each index
* in the collection.
*
* The fields in this document are the names of the indexes, while the
* values themselves are documents that contain statistics for the index
* provided by the storage engine. These statistics are for
* internal diagnostic use.
*
* http://docs.mongodb.org/manual/reference/command/collStats/#collStats.indexDetails
*/
index_details: 'object',
/**
* New in version 3.0.0.
*
* wiredTiger only appears when using the wiredTiger storage engine. This
* document contains data reported directly by the WiredTiger engine and
* other data for internal diagnostic use.
*
* http://docs.mongodb.org/manual/reference/command/collStats/#collStats.wiredTiger
*/
wired_tiger: 'object'
},
// extraProperties: 'reject',
collections: {
indexes: CollectionIndexes
indexes: IndexCollection
},
derived: {
document_size_average: {
deps: ['document_size', 'document_count'],
fn: function() {
if (!this.document_size || !this.document_count) return;
return this.document_size / this.document_count;
}
},
index_size_average: {
deps: ['index_size', 'index_count'],
fn: function() {
if (!this.index_size || !this.index_count) return;
return this.index_size / this.index_count;
}
},
name: {
deps: ['_id'],
fn: function() {
debug('%s -> %j', this._id, types.ns(this._id));
if (!this._id) return undefined;
return types.ns(this._id).collection;
}
},
specialish: {
name: {
deps: ['_id'],
fn: function() {
debug('%s -> %j', this._id, types.ns(this._id));
return types.ns(this._id).specialish;
}
deps: ['_id'],
fn: function() {
if (!this._id) return undefined;
return types.ns(this._id).specialish;
}
}
}
},
parse: function(d) {
// @todo: update scout-server to just do this.
if (d.index_sizes) {
_.each(d.indexes, function(data, name) {
d.indexes[name].size = d.index_sizes[name];
});
}
return d;
},
serialize: function() {
var res = this.getAttributes({
props: true,
derived: true
}, true);

_.each(this._children, function(value, key) {
res[key] = this[key].serialize();
}, this);
_.each(this._collections, function(value, key) {
res[key] = this[key].serialize();
}, this);
return res;
},
});

module.exports = Collection;
87 changes: 43 additions & 44 deletions scout-server/lib/routes/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,21 @@ function getCollectionFeatures(req, fn) {
req.db.command({
collStats: req.ns.collection
}, {
verbose: 1
}, function(err, data) {
if (err) return fn(err);

req.collection_features = {
capped: data.capped,
max: data.max,
size: data.size,
power_of_two: data.userFlags === 1
};
fn(null, req.collection_features);
});
verbose: 1
}, function(err, data) {
if (err) return fn(err);

req.collection_features = {
capped: data.capped,
max_document_count: data.max,
max_document_size: data.maxSize,
size: data.size,
power_of_two: data.userFlags === 1,
index_details: data.indexDetails || {},
wired_tiger: data.wiredTiger || {}
};
fn(null, req.collection_features);
});
}

function getCollectionStats(req, fn) {
Expand All @@ -47,33 +50,29 @@ function getCollectionStats(req, fn) {
req.db.command({
collStats: req.ns.collection
}, {
verbose: 1
}, function(err, data) {
if (err) return fn(err);

req.collection_stats = {
index_sizes: data.indexSizes,
document_count: data.count,
document_size: data.size,
storage_size: data.storageSize,
index_count: data.nindexes,
index_size: data.totalIndexSize,
padding_factor: data.paddingFactor,
extent_count: data.numExtents,
extent_last_size: data.lastExtentSize,
flags_user: data.userFlags,
flags_system: data.systemFlags
};
fn(null, req.collection_stats);
});
verbose: 1
}, function(err, data) {
if (err) return fn(err);

req.collection_stats = {
index_sizes: data.indexSizes,
document_count: data.count,
document_size: data.size,
storage_size: data.storageSize,
index_count: data.nindexes,
index_size: data.totalIndexSize,
padding_factor: data.paddingFactor,
extent_count: data.numExtents,
extent_last_size: data.lastExtentSize,
flags_user: data.userFlags,
flags_system: data.systemFlags
};
fn(null, req.collection_stats);
});
}

function getCollectionIndexes(req, fn) {
req.db.collection('system.indexes')
.find({
ns: req.ns.toString()
})
.toArray(fn);
req.db.collection(req.ns.collection).listIndexes().toArray(fn);
}

// @todo: Move to scount-sync.collection.fetch().
Expand Down Expand Up @@ -128,14 +127,14 @@ module.exports = {
query: req.json('query'),
size: req.int('size', 5)
})
.pipe(_idToDocument(req.db, req.ns.collection, {
fields: req.json('fields')
}))
.pipe(EJSON.createStringifyStream())
.pipe(setHeaders(req, res, {
'content-type': 'application/json'
}))
.pipe(res);
.pipe(_idToDocument(req.db, req.ns.collection, {
fields: req.json('fields')
}))
.pipe(EJSON.createStringifyStream())
.pipe(setHeaders(req, res, {
'content-type': 'application/json'
}))
.pipe(res);

},
bulk: function(req, res, next) {
Expand Down
17 changes: 17 additions & 0 deletions scout-ui/src/collection-stats/index.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
div.row.hidden
.col-md-6
dl.dl-horizontal
dt # documents
dd(data-hook='document_count')
dt total document size
dd(data-hook='document_size')
dt average document size
dd(data-hook='document_size_average')
.col-md-6
dl.dl-horizontal
dt # indexes
dd(data-hook='index_count')
dt total index size
dd(data-hook='index_size')
dt average index size
dd(data-hook='index_size_average')
69 changes: 69 additions & 0 deletions scout-ui/src/collection-stats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
var AmpersandView = require('ampersand-view');
var numeral = require('numeral');

var CollectionStatsView = AmpersandView.extend({
bindings: {
'model._id': {
hook: 'name'
},
document_count: {
hook: 'document_count'
},
document_size: {
hook: 'document_size'
},
document_size_average: {
hook: 'document_size_average'
},
index_count: {
hook: 'index_count'
},
index_size: {
hook: 'index_size'
},
index_size_average: {
hook: 'index_size_average'
},
},
derived: {
document_count: {
deps: ['model.document_count'],
fn: function() {
return numeral(this.model.document_count).format('0.0a');
}
},
document_size: {
deps: ['model.document_size'],
fn: function() {
return numeral(this.model.document_size).format('0.0b');
}
},
document_size_average: {
deps: ['model.document_size_average'],
fn: function() {
return numeral(this.model.document_size_average).format('0.0b');
}
},
index_count: {
deps: ['model.index_count'],
fn: function() {
return numeral(this.model.index_count).format('0.0a');
}
},
index_size: {
deps: ['model.index_size'],
fn: function() {
return numeral(this.model.index_size).format('0.0b');
}
},
index_size_average: {
deps: ['model.index_size_average'],
fn: function() {
return numeral(this.model.index_size_average).format('0.0b');
}
}
},
template: require('./index.jade')
});

module.exports = CollectionStatsView;
1 change: 1 addition & 0 deletions scout-ui/src/home/collection.jade
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
.panel
.panel-heading
h3(data-hook='name')
div(data-hook='stats-container')
.panel-body
div(data-hook='fields-container')
Loading