Commit 89598bc9 by Ondrej Machala Committed by James Cropcho

New 'lastValue' property. (#142)

* Add lastValue variable

* Use dot as defaultLast value.

* Update doc.

* Better example in doc.

* Fix convert results method, when lastValue config is false.

* Eliminate 'else' branch.

* Include lastValue into BasicAnalysisTest.

* Fix format by eslint.

* Binary data support. Change 'dot' to square brackets + type combination. Update doc.

* Update docs.

* Fix typo by eslint.
parent 2d8debca
...@@ -131,6 +131,31 @@ One can apply a "sort" constraint, which analyzes documents in the specified ord ...@@ -131,6 +131,31 @@ One can apply a "sort" constraint, which analyzes documents in the specified ord
$ mongo test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js $ mongo test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js
### Include Last Value ###
You can add ```lastValue``` property to show values of the last document.
$ mongo test --eval "var collection = 'orders', lastValue = true" variety.js
+--------------------------------------------------------------------------------------------+
| key | types | occurrences | percents | lastValue |
| --------------- | ------------ | ----------- | -------- | -------------------------------- |
| _id | ObjectId | 1 | 100.0 | 5a834b76f4d3fa6e578a67f6 |
| age | Number | 1 | 100.0 | 38.2569 |
| animals | Array | 1 | 100.0 | [Array] |
| animals.XX.type | String | 1 | 100.0 | dog |
| balance | NumberLong | 1 | 100.0 | 1236458945684846 |
| date | Date | 1 | 100.0 | 1513539969000 |
| fn | Object | 1 | 100.0 | [Object] |
| fn.code | String | 1 | 100.0 | function (x, y){ return x + y; } |
| name | String | 1 | 100.0 | John |
| nil | null | 1 | 100.0 | [null] |
| uid | BinData-UUID | 1 | 100.0 | 3b241101e2bb42558caf4136c566a962 |
+--------------------------------------------------------------------------------------------+
If use without ```sort``` it will fetch values of the last natural sorted document.
Date is converted into timestamp, ObjectId into string and binary data as hex. Other types shown in square brackets.
### Render Output As JSON For Easy Ingestion and Parsing ### ### Render Output As JSON For Easy Ingestion and Parsing ###
Variety supports two different output formats: Variety supports two different output formats:
......
...@@ -16,14 +16,14 @@ describe('Basic Analysis', () => { ...@@ -16,14 +16,14 @@ describe('Basic Analysis', () => {
}); });
it('should return JSON results', async () => { it('should return JSON results', async () => {
const results = await test.runJsonAnalysis({collection:'users'}, true); const results = await test.runJsonAnalysis({collection:'users', lastValue:true}, true);
results.validateResultsCount(7); results.validateResultsCount(7);
results.validate('_id', 5, 100.0, {ObjectId: 5}); results.validate('_id', 5, 100.0, {ObjectId: 5});
results.validate('name', 5, 100.0, {String: 5}); results.validate('name', 5, 100.0, {String: 5}, 'Jim');
results.validate('bio', 3, 60.0, {String: 3}); results.validate('bio', 3, 60.0, {String: 3}, 'Ça va?');
results.validate('birthday', 2, 40.0, {Date: 2}); results.validate('birthday', 2, 40.0, {Date: 2}, 448070400000);
results.validate('pets', 2, 40.0, {String: 1, Array: 1}); results.validate('pets', 2, 40.0, {String: 1, Array: 1}, 'egret');
results.validate('someBinData', 1, 20.0, {'BinData-generic': 1}); results.validate('someBinData', 1, 20.0, {'BinData-generic': 1});
results.validate('someWeirdLegacyKey', 1, 20.0, {String: 1}); results.validate('someWeirdLegacyKey', 1, 20.0, {String: 1}, 'I like Ike!');
}); });
}); });
...@@ -85,6 +85,7 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -85,6 +85,7 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
read('logKeysContinuously', false); read('logKeysContinuously', false);
read('excludeSubkeys', []); read('excludeSubkeys', []);
read('arrayEscape', 'XX'); read('arrayEscape', 'XX');
read('lastValue', false);
//Translate excludeSubkeys to set like object... using an object for compatibility... //Translate excludeSubkeys to set like object... using an object for compatibility...
config.excludeSubkeys = config.excludeSubkeys.reduce(function (result, item) { result[item+'.'] = true; return result; }, {}); config.excludeSubkeys = config.excludeSubkeys.reduce(function (result, item) { result[item+'.'] = true; return result; }, {});
...@@ -166,6 +167,7 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -166,6 +167,7 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
binDataTypes[0x01] = 'function'; binDataTypes[0x01] = 'function';
binDataTypes[0x02] = 'old'; binDataTypes[0x02] = 'old';
binDataTypes[0x03] = 'UUID'; binDataTypes[0x03] = 'UUID';
binDataTypes[0x04] = 'UUID';
binDataTypes[0x05] = 'MD5'; binDataTypes[0x05] = 'MD5';
binDataTypes[0x80] = 'user'; binDataTypes[0x80] = 'user';
return 'BinData-' + binDataTypes[thing.subtype()]; return 'BinData-' + binDataTypes[thing.subtype()];
...@@ -227,8 +229,23 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -227,8 +229,23 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
result[key] = {}; result[key] = {};
} }
var type = varietyTypeOf(value); var type = varietyTypeOf(value);
result[key][type] = true; result[key][type] = null;
if(config.lastValue){
if (type in {'String': true, 'Boolean': true}) {
result[key][type] = value.toString();
}else if (type in {'Number': true, 'NumberLong': true}) {
result[key][type] = value.valueOf();
}else if(type == 'ObjectId'){
result[key][type] = value.str;
}else if(type == 'Date'){
result[key][type] = new Date(value).getTime();
}else if(type.startsWith('BinData')){
result[key][type] = value.hex();
}
}
} }
return result; return result;
}; };
...@@ -249,14 +266,19 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -249,14 +266,19 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
} }
existing.totalOccurrences = existing.totalOccurrences + 1; existing.totalOccurrences = existing.totalOccurrences + 1;
} else { } else {
var lastValue = null;
var types = {}; var types = {};
for (var newType in docResult[key]) { for (var newType in docResult[key]) {
types[newType] = 1; types[newType] = 1;
lastValue = docResult[key][newType];
if (config.logKeysContinuously) { if (config.logKeysContinuously) {
log('Found new key type "' + key + '" type "' + newType + '"'); log('Found new key type "' + key + '" type "' + newType + '"');
} }
} }
interimResults[key] = {'types': types,'totalOccurrences':1}; interimResults[key] = {'types': types,'totalOccurrences':1};
if (config.lastValue) {
interimResults[key]['lastValue'] = lastValue ? lastValue : '['+newType+']';
}
} }
} }
}; };
...@@ -274,12 +296,19 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -274,12 +296,19 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
//now convert the interimResults into the proper format //now convert the interimResults into the proper format
for(var key in interimResults) { for(var key in interimResults) {
var entry = interimResults[key]; var entry = interimResults[key];
varietyResults.push({
var obj = {
'_id': {'key':key}, '_id': {'key':key},
'value': {'types':getKeys(entry.types)}, 'value': {'types':getKeys(entry.types)},
'totalOccurrences': entry.totalOccurrences, 'totalOccurrences': entry.totalOccurrences,
'percentContaining': entry.totalOccurrences * 100 / documentsCount 'percentContaining': entry.totalOccurrences * 100 / documentsCount
}); };
if(config.lastValue){
obj.lastValue = entry.lastValue;
}
varietyResults.push(obj);
} }
return varietyResults; return varietyResults;
}; };
...@@ -343,6 +372,10 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -343,6 +372,10 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
var createAsciiTable = function(results) { var createAsciiTable = function(results) {
var headers = ['key', 'types', 'occurrences', 'percents']; var headers = ['key', 'types', 'occurrences', 'percents'];
if (config.lastValue) {
headers.push('lastValue');
}
// return the number of decimal places or 1, if the number is int (1.23=>2, 100=>1, 0.1415=>4) // return the number of decimal places or 1, if the number is int (1.23=>2, 100=>1, 0.1415=>4)
var significantDigits = function(value) { var significantDigits = function(value) {
var res = value.toString().match(/^[0-9]+\.([0-9]+)$/); var res = value.toString().match(/^[0-9]+\.([0-9]+)$/);
...@@ -363,10 +396,14 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */ ...@@ -363,10 +396,14 @@ Released by Maypop Inc, © 2012-2018, under the MIT License. */
types = typeKeys; types = typeKeys;
} }
return [row._id.key, types, row.totalOccurrences, row.percentContaining.toFixed(Math.min(maxDigits, 20))]; var rawArray = [row._id.key, types, row.totalOccurrences, row.percentContaining.toFixed(Math.min(maxDigits, 20))];
if (config.lastValue && row['lastValue']) {
rawArray.push(row['lastValue']);
}
return rawArray;
}); });
var table = [headers, headers.map(function(){return '';})].concat(rows); var table = [headers, headers.map(function(){return '';})].concat(rows);
var colMaxWidth = function(arr, index) {return Math.max.apply(null, arr.map(function(row){return row[index].toString().length;}));}; var colMaxWidth = function(arr, index) {return Math.max.apply(null, arr.map(function(row){return row[index] ? row[index].toString().length : 0;}));};
var pad = function(width, string, symbol) { return width <= string.length ? string : pad(width, isNaN(string) ? string + symbol : symbol + string, symbol); }; var pad = function(width, string, symbol) { return width <= string.length ? string : pad(width, isNaN(string) ? string + symbol : symbol + string, symbol); };
table = table.map(function(row, ri){ table = table.map(function(row, ri){
return '| ' + row.map(function(cell, i) {return pad(colMaxWidth(table, i), cell.toString(), ri === 1 ? '-' : ' ');}).join(' | ') + ' |'; return '| ' + row.map(function(cell, i) {return pad(colMaxWidth(table, i), cell.toString(), ri === 1 ? '-' : ' ');}).join(' | ') + ' |';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment