Using MongoDB 3.4.4 and newer versions:
db.coll.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "el",
"cond": {
"$eq": [
"$$el.v.samekeyA",
"value1"
]
}
}
}
}
} }
])
The above pipeline will yield the final output
{
"key1" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
Explanations
The pipeline can be decomposed to show each individual operator's results.
$objectToArray
$objectToArray
enables you to transform the root document with dynamic keys
(denoted by the system variable $$ROOT
) into an array that contains a element for each field/value pair in the original document. Each element in the return array is a document that contains two fields k and v.
Running the pipeline with just the operator in a $project
stage
db.coll.aggregate([
{ "$project": {
"keys": { "$objectToArray": "$$ROOT" }
} }
])
yields
{
"_id" : 1,
"keys" : [
{
"k" : "_id",
"v" : 1
},
{
"k" : "key1",
"v" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
},
{
"k" : "key2",
"v" : {
"samekeyA" : "value3",
"samekeyB" : "value4"
}
},
{
"k" : "key3",
"v" : {
"samekeyA" : "value5",
"samekeyB" : "value6"
}
}
]
}
$filter
The $filter
operator acts as a filtering mechanism for the array produced by the $objectToArray
operator, works by selecting a subset of the array to return based on the specified condition which
becomes your query.
Consider the following pipeline which returns an array of the key/value pair that matches the condition { "samekeyA": "value1" }
db.coll.aggregate([
{ "$project": {
"keys": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "el",
"cond": {
"$eq": [
"$$el.v.samekeyA",
"value1"
]
}
}
}
} }
])
which yields
{
"_id" : 1,
"keys" : [
{
"k" : "key1",
"v" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
]
}
$arrayToObject
This will transform the filtered array above from
[
{
"k" : "key1",
"v" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
]
to the original document with the dynamic key
{
"key1" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
so running the pipeline
db.coll.aggregate([
{ "$project": {
"key": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "el",
"cond": {
"$eq": [
"$$el.v.samekeyA",
"value1"
]
}
}
}
}
} }
])
will produce
{
"_id" : 1,
"key" : {
"key1" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
}
$replaceRoot
This will promote the filtered dynamic key document to the top level and replaces all other fields. The operation replaces all existing fields in the input document, including the _id
field.
Essentially this transforms the above document
{
"_id" : 1,
"key" : {
"key1" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}
}
to the desired final output
{
"key1" : {
"samekeyA" : "value1",
"samekeyB" : "value2"
}
}