[SERVER-35647] $let inside $map not working properly Created: 18/Jun/18  Updated: 08/Feb/23  Resolved: 20/Jun/18

Status: Closed
Project: Core Server
Component/s: Aggregation Framework
Affects Version/s: 3.6.0
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Daniel Hegener Assignee: Asya Kamsky
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Operating System: ALL
Steps To Reproduce:

 

 

Given the following document:

db.test.insertOne({
    "_id" : ObjectId("5b27d2c5dd24433254a066a6"),
    "input" : "ab"
})

When I run:

 

db.test.aggregate({
    $project: {
        "result": {
            $map: {
                input: { $range: [ 0, { $strLenCP: "$input" } ] }, // array: 0 to number of chars in input string
                in: { 
                    $let: {
                        vars: { "char": { $substrCP: [ "$input", "$$this", 1 ] } }, // nth character will be stored in "$$char"
                        in: {
                            $switch: {
                                branches: [{
                                    case: { $eq: [ "$$char", "a" ] }, then: 1, // this should get matched
                                    case: { $eq: [ "$$char", "b" ] }, then: 2 // but only this one does get matched
                                }],
                                default: "$$char"
                            }
                        }
                    }
                }
            }
        } 
    }
})

I would expect to receive:

 

{
    "_id" : ObjectId("5b27d2c5dd24433254a066a6"), 
    "result" : [ 
        1.0, 
        2.0
    ]
}

However, the result is this:

{
    "_id" : ObjectId("5b27d2c5dd24433254a066a6"),
    "result" : [ 
        "a", 
        2.0
    ]
}

 

 

Participants:

 Description   

In v3.6.0, $let inside $map appears to get evaluated at the wrong point in time. See "Steps To Reproduce" for details.

 



 Comments   
Comment by Asya Kamsky [ 18/Jun/18 ]

dnickless,

I realized that there is a hard-to-see syntax error in your aggregation. The switch expression has an array called branches, each element of which is a subdocument. In your case you have a single document with two fields called "case". What happens in JS is that the second one overrides the first one and by the time the server gets the expression, it's as if the first "case:" field was not there at all.

Corrected, the right result is returned:

db.test.aggregate({
    $project: {
        "result": {
            $map: {
                input: { $range: [ 0, { $strLenCP: "$input" } ] },
                in: {
                    $let: {
                        vars: { "char": { $substrCP: [ "$input", "$$this", 1 ] } },
                        in: {
                            $switch: {
                                branches: [
                                    {case: { $eq: [ "$$char", "a" ] }, then: 1},
                                    {case: { $eq: [ "$$char", "b" ] }, then: 2}
                                ],
                                default: "$$char"
                            }
                        }
                    }
                }
            }
        }
    }
})
{ "_id" : ObjectId("5b27d2c5dd24433254a066a6"), "result" : [ 1, 2 ] }

Comment by Asya Kamsky [ 18/Jun/18 ]

db.test.aggregate( {     $project: {         "result": {             $map: {
	input: { $range: [ 0, { $strLenCP: "$input" } ] },
	as:"i",
	in:{
	   char:"a",
	   i:"$$i",
	   substr:{$substrCP:["$input","$$i",1]},
           isSubstrEqualA: {$eq:["a",{$substrCP:["$input","$$i",1]}]},
           isSubstrEqualB: {$eq:["b",{$substrCP:["$input","$$i",1]}]},
	   switch:{
		$switch: { branches: [{
			case: { $eq: [ {$substrCP:["$input","$$i",1]}, "a" ] }, then: "Answer is 1",
			case: { $eq: [ {$substrCP:["$input","$$i",1]}, "b"] }, then: "Answer is 2"
		}], default:"Default branch char is -> neither"
		}
	   }
	}
}}}} ).pretty()
{
	"_id" : ObjectId("5b27d2c5dd24433254a066a6"),
	"result" : [
		{
			"char" : "a",
			"i" : 0,
			"substr" : "a",
			"isSubstrEqualA" : true,
			"isSubstrEqualB" : false,
			"switch" : "Default branch char is -> neither"
		},
		{
			"char" : "a",
			"i" : 1,
			"substr" : "b",
			"isSubstrEqualA" : false,
			"isSubstrEqualB" : true,
			"switch" : "Answer is 2"
		}
	]
} 

Comment by Asya Kamsky [ 18/Jun/18 ]

I can reproduce this without using $let so I think something else is going on.

 

Generated at Thu Feb 08 04:40:30 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.