[SERVER-82427] Improve the mongo shell so that objects containing strings with newlines or tabs render properly Created: 24/Oct/23  Updated: 31/Oct/23

Status: Open
Project: Core Server
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: David Storch Assignee: Backlog - Query Optimization
Resolution: Unresolved Votes: 2
Labels: neweng
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to SERVER-61497 Improve the readability of explain pl... Backlog
Assigned Teams:
Query Optimization
Participants:

 Description   

Multi-line strings render in the way I would expect in the mongo shell when they are scalar values:

> let multilineStr = "two\nlines";
> multilineStr
two
lines

However, when the very same multi-line string is embedded in an object, the newline now appears as "\n":

> let objWithMultilineStr = {f: multilineStr};
> objWithMultilineStr
{ "f" : "two\nlines" }

This is particularly problematic for engineers working with explain output. The current explain output format embeds an SBE plan printed in a string format. If you use the mongo shell to run explain in the simplest way, you get a pretty useless representation of the plan:

> db.c.find({a: {$elemMatch: {b: 1, c: 1}}}, {"a.b": 1, "a.c": 1}).explain()
{
	"explainVersion" : "2",
	"queryPlanner" : {
        ...
			"slotBasedPlan" : {
				"slots" : "$$RESULT=s7 env: { s1 = Nothing (nothing), s5 = 1, s6 = 1 }",
				"stages" : "[2] project [s7 = makeBsonObj(MakeObjSpec([\"_id\", \"a\" = MakeObj([\"b\", \"c\"], Closed, ReturnNothing)], Closed, ReturnNothing), s3)] \n[1] filter {((isArray(s2) && traverseF(s2, lambda(l101.0) { (typeMatch(l101.0, 24) && (traverseF(getField(l101.0, \"b\"), lambda(l102.0) { ((move(l102.0) == s5) ?: false) }, false) && traverseF(getField(l101.0, \"c\"), lambda(l103.0) { ((move(l103.0) == s6) ?: false) }, false))) }, false)) ?: false)} \n[1] scan s3 s4 none none none none none none lowPriority [s2 = a] @\"c4494949-e91d-4678-8c71-f05bceac7c8e\" true false "
			}
		},
		"rejectedPlans" : [ ]
	},
        ...
	"ok" : 1
}

One workaround is to run explain in such a way as to extract the contents of the plan string directly, in which case the newlines are replaced correctly:

> db.c.find({a: {$elemMatch: {b: 1, c: 1}}}, {"a.b": 1, "a.c": 1}).explain().queryPlanner.winningPlan.slotBasedPlan.stages
[2] project [s7 = makeBsonObj(MakeObjSpec(["_id", "a" = MakeObj(["b", "c"], Closed, ReturnNothing)], Closed, ReturnNothing), s3)]
[1] filter {((isArray(s2) && traverseF(s2, lambda(l101.0) { (typeMatch(l101.0, 24) && (traverseF(getField(l101.0, "b"), lambda(l102.0) { ((move(l102.0) == s5) ?: false) }, false) && traverseF(getField(l101.0, "c"), lambda(l103.0) { ((move(l103.0) == s6) ?: false) }, false))) }, false)) ?: false)}
[1] scan s3 s4 none none none none none none lowPriority [s2 = a] @"c4494949-e91d-4678-8c71-f05bceac7c8e" true false

There may be other workarounds, like using jq or some other tooling. But ideally the mongo shell would provide a natural solution for query or server developers who need to interpret explain output.



 Comments   
Comment by David Storch [ 24/Oct/23 ]

mongosh does a bit better with this than the mongo shell. Although it includes a "\n" it does split the string across multiple lines. The indentation it uses can still damage readability of the plan, though:

      slotBasedPlan: {
        slots: '$$RESULT=s7 env: { s1 = Nothing (nothing), s5 = 1, s6 = 1 }',
        stages: '[2] project [s7 = makeBsonObj(MakeObjSpec(["_id", "a" = MakeObj(["b", "c"], Closed, ReturnNothing)], Closed, ReturnNothing), s3)] \n' +
          '[1] filter {((isArray(s2) && traverseF(s2, lambda(l101.0) { (typeMatch(l101.0, 24) && (traverseF(getField(l101.0, "b"), lambda(l102.0) { ((move(l102.0) == s5) ?: false) }, false) && traverseF(getField(l101.0, "c"), lambda(l103.0) { ((move(l103.0) == s6) ?: false) }, false))) }, false)) ?: false)} \n' +
          '[1] scan s3 s4 none none none none none none lowPriority [s2 = a] @"c4494949-e91d-4678-8c71-f05bceac7c8e" true false '
      }

Generated at Thu Feb 08 06:49:12 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.