Reputation: 7233
I have a document structured like so:
mode: "b",
a: [0,1,2],
b: [1,4,5],
c: [2,2]
And I want to project the field that equals mode
. The end result should be something like:
data: [1,4,5] // since mode == "b", it returns b's value
I tried $$CURRENT[$mode]
, but it looks like you can't use brackets like that in mongo. I tried using a local variable like so:
$let: {
vars: {mode: "$mode"},
in: "$$CURRENT.$$mode"
}
but that doesn't work either. I'm considering using $switch
and then manually putting in all the possible modes. But I'm wondering if there is a better way to do it.
Upvotes: 1
Views: 273
Reputation: 151072
You are looking in the wrong place, but if you can use $switch
then you have MongoDB 3.4 and you can use $objectToArray
which is actually the correct thing to do. Your problem is you are trying to "dynamicaly" refer to a property by the "value" of it's "key name". You cannot do that, so $objectToArray
makes the "key" a "value"
So given your document:
{ "mode": "a", "a": [0,1,2], "b": [1,4,5], "c": [2,2] }
Then you do the aggregate, using $map
and $filter
to work with the converted elements as an array:
db.sample.aggregate([
{ "$project": {
"_id": 0,
"mode": 1,
"data": {
"$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$eq": ["$$this.k","$mode"] }
}
},
"in": "$$this.v"
}},
0
]
}
}}
])
Or using $let
and $indexOfArray
if that seems more sensible to you:
db.sample.aggregate([
{ "$project": {
"_id": 0,
"mode": 1,
"data": {
"$let": {
"vars": { "doc": { "$objectToArray": "$$ROOT" } },
"in": {
"$arrayElemAt": [
"$$doc.v",
{ "$indexOfArray": [ "$$doc.k", "$mode" ] }
]
}
}
}
}}
])
Which matches the selected field:
{
"mode" : "a",
"data" : [
0.0,
1.0,
2.0
]
}
If you look at "just" what $objectToArray
is doing here, then the reasons should be self evident:
{
"data" : [
{
"k" : "_id",
"v" : ObjectId("597915787dcd6a5f6a9b4b98")
},
{
"k" : "mode",
"v" : "a"
},
{
"k" : "a",
"v" : [
0.0,
1.0,
2.0
]
},
{
"k" : "b",
"v" : [
1.0,
4.0,
5.0
]
},
{
"k" : "c",
"v" : [
2.0,
2.0
]
}
]
}
So now instead of there being an "object" with named properties, the "array" consistently contains "k"
named as the "key" and "v"
containing the "value". This is easy to $filter
and obtain the desired results, or basically use any method that works with arrays to obtain the match.
Upvotes: 1