| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- "use strict";
- //=============================================================================
- // Exports a getMarkdown() and patches pipeline parts with a getMarkdown()
- //=============================================================================
- var pipeline = require("../pipeline"),
- matcher = require("../matcher");
- var MAX_LINE_LEN = 140,
- INDENT_STR = " ";
- function getIndentStr(level) {
- var indent = "";
- while (level-- > 0) {
- indent += INDENT_STR;
- }
- return indent;
- }
- function getSingleLineJsonStr(obj){
- return JSON.stringify(obj, 0, 1)
- .replace(/\n\s*/g, " ");
- }
- function getMultiLineJsonStr(obj) {
- return JSON.stringify(obj, 0, 4);
- }
- function toListItem(str, i) {
- return str.replace(/(^\s*)/, "$&" + (i || 0) + ". ");
- }
- (function patchMatcher(matcher) {
- //NOTE: this area especially needs finished
- // base implementation for matcher MatchExpression instances; just calls `#debugString()`
- matcher.MatchExpression.prototype.getMarkdown = function(level) {
- return this.debugString(level);
- };
- matcher.ComparisonMatchExpression.prototype.getMarkdown = function(level) {
- var retStr = this._debugAddSpace(level) + "`" + this.path() + "` ";
- switch (this._matchType) {
- case "LT": retStr += "$lt"; break;
- case "LTE": retStr += "$lte"; break;
- case "EQ": retStr += "=="; break;
- case "GT": retStr += "$gt"; break;
- case "GTE": retStr += "$gte"; break;
- default: throw new Error("Unknown comparison!");
- }
- retStr += " " + (this._rhs !== undefined ? "`" + JSON.stringify(this._rhs) + "`" : "?");
- if (this.getTag()) {
- retStr += this.getTag().debugString();
- }
- return retStr + "\n";
- };
- matcher.ListOfMatchExpression.prototype.getMarkdown = function(level) {
- var str = this._debugAddSpace(level);
- switch (this._matchType) {
- case "AND": str += "all of:\n"; break;
- case "OR": str += "one of:\n"; break;
- case "NOR": str += "none of:\n"; break;
- case "NOT": str += "not all of:\n"; break;
- default: throw new Error("Unknown match type!");
- }
- var exps = this._expressions;
- if (exps.length === 0 && this._exp) exps = [this._exp];
- for (var i = 0; i < exps.length; i++) {
- str += toListItem(exps[i].getMarkdown(level + 1), i);
- }
- return str;
- };
- matcher.ExistsMatchExpression.prototype.getMarkdown = function(level) {
- return this._debugAddSpace(level) +
- "`" + this.path() + "` exists" +
- (this.getTag() ? " " + this.getTag().debugString() : "") +
- "\n";
- };
- matcher.InMatchExpression.prototype.getMarkdown = function(level) {
- return this._debugAddSpace(level) +
- "`" + this.path() + "` in " +
- "`" + this._arrayEntries.debugString(level) + "`" +
- (this.getTag() ? this.getTag().debugString() : "") +
- "\n";
- };
- })(matcher);
- (function patchPipeline(pipeline) {
- // (function patchPipelineAccumulators(accumulators) {
- // // done in the $group handler for now
- // })(pipeline.accumulators);
- (function patchPipelineDocumentSources(documentSources) {
- //TODO: GeoNearDocumentSource
- //TODO: test single value for `_id` like `null` or `"$foo.bar.path"`
- documentSources.GroupDocumentSource.prototype.getMarkdown = function(level) {
- var i, l;
- var indent = getIndentStr(level),
- str = indent + "group docs into buckets:\n";
- if (this.idFieldNames.length === 0) {
- str += indent + INDENT_STR + "0. by `_id` which is from " + this.idExpressions[0].getMarkdown(0);
- } else {
- str += indent + INDENT_STR + "0. by `_id` which is from:\n";
- for (i = 0, l = this.idExpressions.length; i < l; i++) {
- var idKey = this.idFieldNames[i],
- idExp = this.idExpressions[i];
- str += indent + INDENT_STR + INDENT_STR + i + ". `" + idKey + "` from";
- var idExpStr = idExp.getMarkdown(level + 3).trimRight(),
- idExpStrLines = idExpStr.split("\n");
- if (idExpStrLines.length === 1) {
- str += " " + idExpStr.trimLeft() + "\n";
- } else {
- str += toListItem(idExp.getMarkdown(level + 3), i);
- }
- }
- }
- if (this.fieldNames.length > 0) {
- str += indent + INDENT_STR + "1. for each bucket keep:\n";
- for (i = 0, l = this.fieldNames.length; i < l; i++) {
- var key = this.fieldNames[i],
- acc = this.accumulatorFactories[i](),
- accName = acc.getOpName().replace(/^\$/, ""),
- accNameAliases = {
- addToSet: "unique set",
- push: "array",
- },
- exp = this.expressions[i];
- str += indent + INDENT_STR + INDENT_STR + i + ". `" + key + "` as " + (accNameAliases[accName] || accName) + " of";
- if (!exp.expressions) {
- str += " " + exp.getMarkdown(level + 2).trimLeft();
- } else {
- str += "\n" + toListItem(exp.getMarkdown(level + 3));
- }
- }
- }
- return str;
- };
- documentSources.LimitDocumentSource.prototype.getMarkdown = function(level) {
- return getIndentStr(level) + "limit to only `" + this.limit + "` output docs";
- };
- documentSources.MatchDocumentSource.prototype.getMarkdown = function(level) {
- var str = "",
- indent = getIndentStr(level),
- exp = this.matcher._expression;
- str += indent + "find docs matching:\n";
- if (exp.expressions) {
- str += exp.getMarkdown(level + 1);
- } else {
- str += indent + INDENT_STR + "0. " + exp.getMarkdown(level + 1).trimLeft();
- }
- return str;
- };
- //TODO: OutDocumentSource
- documentSources.ProjectDocumentSource.prototype.getMarkdown = function(level) {
- return "for each doc " + this.OE.getMarkdown(level);
- };
- //TODO: RedaactDocumentSource
- documentSources.SkipDocumentSource.prototype.getMarkdown = function(level) {
- return getIndentStr(level) + "skip the next `" + this.limit + "` output docs";
- };
- documentSources.SortDocumentSource.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- str = indent + "sort docs by:\n";
- for (var i = 0, l = this.vSortKey.length; i < l; i++) {
- var orderStr = this.vAscending[i] ? "in order" : "in reverse order";
- str += indent + INDENT_STR + i + ". " + this.vSortKey[i].getMarkdown().trimRight() + ", " + orderStr;
- }
- return str;
- };
- documentSources.UnwindDocumentSource.prototype.getMarkdown = function(level) {
- return getIndentStr(level) + "unwind docs by using each item in `" +
- this._unwindPath.getPath(false) + "` to create a copy that has the list item rather than the list";
- };
- })(pipeline.documentSources);
- (function patchPipelineExpressions(expressions) {
- // base implementation for expression Expression instances; just calls `#serialize()`
- expressions.Expression.prototype.getMarkdown = function(level) {
- var obj = this.serialize(),
- objStr = typeof obj === "string" ? obj : getSingleLineJsonStr(obj);
- return getIndentStr(level) + "`" + objStr + "`\n";
- };
- expressions.AddExpression.prototype.getMarkdown = function(level) {
- var str = "",
- indent = getIndentStr(level),
- opStrs = this.operands.map(function(op) {
- return op.getMarkdown(level + 1).trimRight();
- }),
- isOneLine = opStrs.length <= 2 && opStrs.every(function(opStr, i) {
- return opStr.indexOf("\n") === -1;
- });
- if (isOneLine && indent.length + opStrs.join(" + ").length < MAX_LINE_LEN) {
- str += indent + "( " + opStrs.map(Function.prototype.call.bind(String.prototype.trimLeft)).join(" + ") + " )\n";
- } else {
- str += indent + "add:\n";
- for (var i = 0, l = this.operands.length; i < l; i++) {
- str += toListItem(opStrs[i], i) + "\n";
- }
- }
- return str;
- };
- //TODO: $allElementsTrue
- expressions.AndExpression.prototype.getMarkdown = function(level) {
- var str = getIndentStr(level) + "all of:\n",
- ops = this.operands;
- for (var i = 0; i < ops.length; i++) {
- var opStr = ops[i].getMarkdown(level + 1).trimRight();
- str += toListItem(opStr, i) + "\n";
- }
- return str;
- };
- //TODO: $anyElementTrue
- //TODO: $coerceToBool
- expressions.CompareExpression.prototype.getMarkdown = function(level) {
- var str = "",
- indent = getIndentStr(level),
- opStrs = this.operands.map(function(op) {
- return op.getMarkdown(level + 1).trimRight();
- }),
- isOneLine = opStrs.length <= 2 && opStrs.every(function(opStr, i) {
- return opStr.indexOf("\n") === -1;
- });
- if (isOneLine && indent.length + opStrs.join("").length < MAX_LINE_LEN) {
- var cmpOpAliases = {
- $eq: "==",
- $ne: "!=",
- },
- cmpOpAlias = cmpOpAliases[this.cmpOp],
- cmpOpStr = cmpOpAlias ? cmpOpAlias : "`" + this.cmpOp + "`";
- if (opStrs.length === 1) str += indent + cmpOpStr + " " + opStrs[0].trim() + "\n";
- else str += indent + opStrs[0].trim() + " " + cmpOpStr + " " + opStrs[1].trim() + "\n";
- } else {
- str += indent + "is `" + this.cmpOp + "`\n";
- for (var i = 0, l = this.operands.length; i < l; i++) {
- str += toListItem(opStrs[i], i) + "\n";
- }
- }
- return str;
- };
- expressions.ConcatExpression.prototype.getMarkdown = function(level) {
- var str = getIndentStr(level) + "concatenate:\n",
- ops = this.operands;
- for (var i = 0; i < ops.length; i++) {
- var opStr = ops[i].getMarkdown(level + 1).trimRight();
- str += toListItem(opStr, i) + "\n";
- }
- return str;
- };
- expressions.CondExpression.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- str = indent + "conditional:\n";
- var names = ["if", "then", "else"];
- names.forEach(function(name, i) {
- str += indent + INDENT_STR + i + ". " + name;
- var opDocStr = this.operands[i].getMarkdown(level + 2).trimRight();
- if (opDocStr.indexOf("\n") === -1 && opDocStr.length < MAX_LINE_LEN) { // is one line
- str += " " + opDocStr.trimLeft() + "\n";
- } else {
- str += ":\n" + toListItem(opDocStr) + "\n";
- }
- }, this);
- return str;
- };
- expressions.ConstantExpression.prototype.getMarkdown = function(level) {
- return getIndentStr(level) + "the constant `" + JSON.stringify(this.value) + "`\n";
- };
- //TODO: $dayOfMonth
- //TODO: $dayOfWeek
- //TODO: $dayOfYear
- expressions.DivideExpression.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- str = indent + "divide:\n";
- var names = ["numerator", "denominator"];
- names.forEach(function(name, i) {
- str += indent + INDENT_STR + i + ". " + name;
- var opDocStr = this.operands[i].getMarkdown(level + 2).trimRight();
- if (opDocStr.indexOf("\n") === -1 && opDocStr.length < MAX_LINE_LEN) { // is one line
- str += " is " + opDocStr.trimLeft() + "\n";
- } else {
- str += ":\n" + toListItem(opDocStr) + "\n";
- }
- }, this);
- return str;
- };
- expressions.FieldPathExpression.prototype.getMarkdown = function(level) {
- var str = "",
- indent = getIndentStr(level),
- fp = this._fieldPath;
- if ((fp.fieldNames[0] === "CURRENT" || fp.fieldNames[0] === "ROOT") && fp.fieldNames.length > 1) {
- str += fp.tail().getPath(false);
- } else {
- str += "$$" + fp.getPath(false);
- }
- return indent + "`" + str + "`\n";
- };
- //TODO: $hour
- expressions.IfNullExpression.prototype.getMarkdown = function(level) {
- var str = getIndentStr(level);
- var opExpDocStr = this.operands[0].getMarkdown(0).trimRight(),
- opOtherExpDocStr = this.operands[1].getMarkdown(0).trimRight();
- str += opExpDocStr + " if not null or fallback to " + opOtherExpDocStr;
- if (str.indexOf("\n") !== -1) throw new Error("TODO: fix multiline $ifNull output");
- str += "\n";
- return str;
- };
- //TODO: $let
- //TODO: $map
- //TODO: $millisecond
- //TODO: $minute
- //TODO: $mod
- //TODO: $month
- //TODO: $multiply
- expressions.NotExpression.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- str = indent + "not:\n";
- str += toListItem(this.operands[0].getMarkdown(level + 1));
- return str;
- };
- expressions.ObjectExpression.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- exps = this._expressions,
- keys = this._order;
- if (!this.excludeId && keys.indexOf("_id") === -1) keys.unshift("_id");
- if (keys.length === 0) return indent + "empty object\n";
- var str = indent + "build object:\n";
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i],
- exp = exps[key];
- str += indent + INDENT_STR + i + ". `" + key + "` from";
- if (exp) {
- var expStr = exp.getMarkdown(level + 2).trimRight();
- if (expStr.indexOf("\n") === -1) { // is one line
- str += " " + expStr.trimLeft() + "\n";
- } else {
- str += "\n" + toListItem(expStr) + "\n";
- }
- } else {
- str += " `" + key + "` (unchanged)\n";
- }
- }
- return str;
- };
- expressions.OrExpression.prototype.getMarkdown = function(level) {
- var str = getIndentStr(level) + "one of:\n",
- ops = this.operands;
- for (var i = 0; i < ops.length; i++) {
- var opStr = ops[i].getMarkdown(level + 1).trimRight();
- str += toListItem(opStr, i) + "\n";
- }
- return str;
- };
- //TODO: $second
- //TODO: $setDifference
- //TODO: $setEquals
- //TODO: $setIntersection
- //TODO: $setIsSubset
- //TODO: $setUnion
- //TODO: $size
- //TODO: $strcasecmp
- expressions.SubstrExpression.prototype.getMarkdown = function(level) {
- var str = "",
- indent = getIndentStr(level);
- str += indent + "a substring";
- var opStringDocStr = this.operands[0].getMarkdown(0).trimRight();
- str += " from " + opStringDocStr.trimLeft();
- var opStartDocStr = this.operands[1].getMarkdown(0).trimRight();
- str += ", starting position is at " + opStartDocStr.trimLeft();
- var opLengthDocStr = this.operands[2].getMarkdown(0).trimRight();
- str += ", length is " + opLengthDocStr.trimLeft();
- if (str.indexOf("\n") !== -1) throw new Error("TODO: fix multiline $substr output");
- str += "\n";
- return str;
- };
- expressions.SubtractExpression.prototype.getMarkdown = function(level) {
- var indent = getIndentStr(level),
- str = indent + "subtract:\n";
- var names = ["minuend", "subtrahend"];
- names.forEach(function(name, i) {
- str += indent + INDENT_STR + i + ". " + name;
- var opDocStr = this.operands[i].getMarkdown(level + 2).trimRight();
- if (opDocStr.indexOf("\n") === -1 && opDocStr.length < MAX_LINE_LEN) { // is one line
- str += " is " + opDocStr.trimLeft() + "\n";
- } else {
- str += ":\n" + toListItem(opDocStr) + "\n";
- }
- }, this);
- return str;
- };
- //TODO: $toLower
- //TODO: $toUpper
- //TODO: $week
- //TODO: $year
- })(pipeline.expressions);
- })(pipeline);
- module.exports = {
- INDENT_STR: INDENT_STR,
- getIndentStr: getIndentStr,
- getSingleLineJsonStr: getSingleLineJsonStr,
- getMultiLineJsonStr: getMultiLineJsonStr,
- getMarkdown: function getMarkdown(pipelineJson) {
- var out = "",
- docSrcs = pipelineJson;
- if (!(docSrcs[0] instanceof pipeline.documentSources.DocumentSource)) {
- docSrcs = pipeline.Pipeline.parseDocumentSources(pipelineJson, {});
- }
- for (var i = 0, l = docSrcs.length; i < l; i++) {
- var docSrc = docSrcs[i];
- out += i + ". " + docSrc.getMarkdown(0).trimRight() + "\n";
- }
- return out;
- },
- };
|