You were sort of going down the right track but you are using $let in the wrong place. So the general idea is to apply the $let "around" the expressions to evaluate rather than "inside" them.
Also, you really should make the object to match an "array" before feeding to the pipeline as an input value. The operators work with "arrays" and don't handle looking up "objects" by variable names very well.
So this is is probably shortest by using $indexOfArray along with $arrayElemAt where both are available in order to match and extract the values:
var obj = {
a: 2,
b: 0,
c: 1
};
var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));
// Outputs as
// [{ "k" : "a", "v" : 2 }, { "k" : "b", "v" : 0 }, { "k" : "c", "v" : 1 }]
db.collection.aggregate([
{ "$addFields": {
"array": {
"$let": {
"vars": { "obj": input },
"in": {
"$arrayElemAt": [
"$array",
{ "$arrayElemAt": [
"$$obj.v",
{ "$indexOfArray": [ "$$obj.k", "$ref" ] }
]}
]
}
}
}
}}
])
If you actually have MongoDB 3.4.4 or greater, then you "could" apply $objectToArray internally instead, but it does not really add any value ( it's just showing off really ):
var obj = {
a: 2,
b: 0,
c: 1
};
//var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));
db.collection.aggregate([
{ "$addFields": {
"array": {
"$let": {
"vars": { "obj": { "$objectToArray": obj } },
"in": {
"$arrayElemAt": [
"$array",
{ "$arrayElemAt": [
"$$obj.v",
{ "$indexOfArray": [ "$$obj.k", "$ref" ] }
]}
]
}
}
}
}}
])
If your MongoDB does not support $indexOfArray, then you can always apply $map and $filter with $arrayElemAt extracting the "first match" from those combined statements instead:
var obj = {
a: 2,
b: 0,
c: 1
};
var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));
db.collection.aggregate([
{ "$addFields": {
"array": {
"$arrayElemAt": [
"$array",
{ "$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": input,
"cond": { "$eq": [ "$$this.k", "$ref" ] }
}
},
"in": "$$this.v"
}},
0
]}
]
}
}}
])
Which also removes any usage of $let since we aren't notating against the supplied variable for anything other than the "input" to $filter directly.
No matter which is applied, all come to the same output which is using the supplied index positions from the input obj ( coerced to input as an array ) and returning that array entry:
/* 1 */
{
"_id" : ObjectId("59a76d6fad465e105d9136dc"),
"ref" : "a",
"array" : "def"
}
/* 2 */
{
"_id" : ObjectId("59a76d6fad465e105d9136dd"),
"ref" : "b",
"array" : "sde"
}
/* 3 */
{
"_id" : ObjectId("59a76d6fad465e105d9136de"),
"ref" : "c",
"array" : "aea"
}