Skip to content

Instantly share code, notes, and snippets.

@gianpaj
Last active July 7, 2020 18:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gianpaj/63e04baed150f30b8976c16b16a0010c to your computer and use it in GitHub Desktop.
Save gianpaj/63e04baed150f30b8976c16b16a0010c to your computer and use it in GitHub Desktop.
console.table for mongo shell - v1.0.2
// console.table for mongo shell - v1.0.2
// https://gist.github.com/gianpaj/63e04baed150f30b8976c16b16a0010c
// originally from 447dc8b on 18 Apr 2015
// https://github.com/chinchang/konsole.table/blob/447dc8be4bcb463865f0527ed6aef4da864d6558/index.js
var SEPARATOR = '│';
/**
* Repeat provided string a given no. of times.
* @param {number} amount Number of times to repeat.
* @param {string} str Character(s) to repeat
* @return {string} Repeated string.
*/
function repeatString(amount, str) {
str = str || ' ';
return Array.apply(0, Array(amount)).join(str);
}
/**
* Formats certain type of values for more readability.
* @param {...} value Value to format.
* @param {Boolean} isHeaderValue Is this a value in the table header.
* @return {string} Formatted value.
*/
function getFormattedString(value, isHeaderValue) {
if (isHeaderValue) {} else if (typeof value === 'string') {
// Wrap strings in inverted commans.
return '"' + value.replace(/\s/g, '\\n') + '"';
} else if (typeof value === 'function') {
// Just show `function` for a function.
return 'function';
} else if (typeof value === 'object') {
// Just show `function` for a function.
return JSON.stringify(value);
} else if (typeof value === 'undefined') {
return '';
}
return value + '';
}
/**
* Colorize and format given value.
* @param {...} value Value to colorize.
* @param {Boolean} isHeaderValue Is this a value in the table header.
* @return {string} Colorized + formatted value.
*/
function getColoredAndFormattedString(value, isHeaderValue) {
var color;
if (isHeaderValue) {} else if (typeof value === 'number' || typeof value === 'boolean') {
color = 'blue';
} else if (typeof value === 'string') {
color = 'red';
}
value = getFormattedString(value, isHeaderValue);
if (color) {
return colorize(value, {
color: color
});
}
return value + '';
}
function printRows(rows) {
if (!rows.length) return;
var row,
rowString,
i,
j,
padding,
tableWidth = 0,
numCols = rows[0].length;
// For every column, calculate the maximum width in any row.
for (j = 0; j < numCols; j++) {
var maxLengthForColumn = 0;
for (i = 0; i < rows.length; i++) {
maxLengthForColumn = Math.max(getFormattedString(rows[i][j], !i || !j).length, maxLengthForColumn);
}
// Give some more padding to biggest string.
maxLengthForColumn += 4;
tableWidth += maxLengthForColumn;
// Give padding to rows for current column.
for (i = 0; i < rows.length; i++) {
padding = maxLengthForColumn - getFormattedString(rows[i][j], !i || !j).length;
// Distribute padding - 1 in starting, rest at the end.
rows[i][j] = ' ' + getColoredAndFormattedString(rows[i][j], !i || !j) + repeatString(padding - 1);
}
}
// HACK: Increase table width just by 1 to make it look good.
tableWidth += 1;
print('┌' + repeatString(tableWidth - 1, '─') + '┐');
for (i = 0; i < rows.length; i++) {
row = rows[i];
rowString = SEPARATOR;
for (var j = 0; j < row.length; j++) {
rowString += row[j] + SEPARATOR;
}
print(rowString);
// Draw border after table header.
if (!i) {
print('├' + repeatString(tableWidth - 1, '─') + '┤');
}
}
print('└' + repeatString(tableWidth - 1, '─') + '┘');
}
function table(data, keys) {
var i,
j,
rows = [],
row,
entry,
objKeys,
tempData;
// Simply print if an `object` type wasn't passed.
if (typeof data !== 'object') {
print(data);
return;
}
// If an object was passed, create data from its properties instead.
if (!(data instanceof Array)) {
tempData = [];
// `objKeys` are now used to index every row.
objKeys = Object.keys(data);
for (var key in data) {
// Avoiding `hasOwnProperty` check because Chrome shows prototype properties
// as well.
tempData.push(data[key]);
}
data = tempData;
}
if (data.length < 1) return;
// Get the keys from first data entry if custom keys are not passed.
if (!keys) {
keys = Object.keys(data[0]);
keys.sort();
}
// Create header row.
rows.push([]);
row = rows[rows.length - 1];
row.push('(index)');
for (i = 0; i < keys.length; i++) {
row.push(keys[i]);
}
for (j = 0; j < data.length; j++) {
entry = data[j];
rows.push([]);
row = rows[rows.length - 1];
// Push entry for 1st column (index).
row.push(objKeys ? objKeys[j] : j);
for (i = 0; i < keys.length; i++) {
row.push(entry[keys[i]]);
}
}
printRows(rows);
}
DBQuery.prototype.table = function(...args) {
const fieldsObj = getFields(args)
let keysArray;
if (args.length && args[0]) keysArray = args[0].split(' ');
table(this.select(fieldsObj).toArray(), keysArray)
};
function getFields(fields) {
const obj = {}
if (fields.length) {
fields = fields.toString().split(' ');
for (let i = 0; i < fields.length; i++) {
const element = fields[i];
if (element) {
obj[element] = 1;
}
}
}
return obj
}
// for quick iteration and to re-run the file after it has been updated, install nodemon globally and use:
//
// nodemon --quiet --watch consoletable.js --exec 'echo "db.test.find()" | \mongo --shell consoletable.js --quiet'
// for testing quickly, uncomment the last line and run:
//
// $ mongo --quiet consoletable.js
//
// > db.products.find().limit(10).table('createdAt uuid')
// ┌───────────────────────────────────────────────────────┐
// │ (index) │ createdAt │ uuid │
// ├───────────────────────────────────────────────────────┤
// │ 0 │ "2020-06-10T23:37:18.082Z" │ "0rXCpR_AM" │
// │ 1 │ "2020-06-12T14:33:21.834Z" │ "YriJcNVnO" │
// └───────────────────────────────────────────────────────┘
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment