Можно ли легко вернуть все поля поддокумента в виде полей в документе верхнего уровня, используя структуру агрегации?

У меня есть документ, подобный следующему, из которого я хочу вернуть подполя текущего поля верхнего уровня в качестве полей верхнего уровня в каждом документе массива результатов:

{ 
  field1: {
    subfield1: {},
    subfield2: [],
    subfield3: 44,
    subfield5: xyz
  },
  field2: {
    othercontent: {}
  }
}

Я хочу, чтобы результаты моего запроса агрегации возвращали следующее (содержимое поля field1 как документа верхнего уровня):

{
  subfield1: {},
  subfield2: [],
  subfield3: 44,
  subfield5: xyz
}

Можно ли это сделать с помощью $project и структуры агрегации, не определяя все подполя для возврата в качестве поля верхнего уровня?

Ответ 1

Вы можете использовать $replaceRoot оператор агрегации с 3.4:

db.getCollection('sample').aggregate([
    {
        $replaceRoot: {newRoot: "$field1"}
    }
])

Обеспечивает вывод как ожидается:

{
    "subfield" : {},
    "subfield2" : [],
    "subfield3" : 44,
    "subfield5" : "xyz"
}

Ответ 2

В целом трудно заставить MongoDB работать с двусмысленными или параметризованными ключами json. Я столкнулся с аналогичной проблемой, и лучшим решением было изменение схемы таким образом, чтобы члены поддокумента стали элементами массива.

Однако я думаю, что это приблизит вас к тому, что вы хотите (весь код должен запускаться непосредственно в оболочке Mongo). Предполагая, что у вас есть такие документы, как:

db.collection.insert({
  "_id": "doc1",
  "field1": {
           "subfield1": {"key1": "value1"},
           "subfield2": ["a", "b", "c"],
           "subfield3": 1,
           "subfield4": "a"
         },
  "field2": "other content"
})

db.collection.insert({ 
  "_id": "doc2",
  "field1": {
           "subfield1": {"key2": "value2"},
           "subfield2": [1, 2, 3],
           "subfield3": 2,
           "subfield4": "b"

         },
  "field2": "yet more content"
})

Затем вы можете запустить команду агрегации, которая продвигает содержимое field1, игнорируя остальную часть документа:

db.collection.aggregate({
"$group":{
    "_id": "$_id",
    "value": {"$push": "$field1"}
}})

Это делает все ключи subfield* в поля верхнего уровня объекта, и этот объект является единственным элементом в массиве. Это неуклюжий, но работоспособный:

"result" : [
        {
                "_id" : "doc2",
                "value" : [
                        {
                                "subfield1" : {"key2" : "value2"},
                                "subfield2" : [1, 2, 3],
                                "subfield3" : 2,
                                "subfield4" : "b"
                        }
                ]
        },
        {
                "_id" : "doc1",
                "value" : [
                        {
                                "subfield1" : {"key1" : "value1"},
                                "subfield2" : ["a","b","c"],
                                "subfield3" : 1,
                                "subfield4" : "a"
                        }
                ]
        }
],
"ok" : 1

Ответ 3

Начиная с Mongo 4.2, оператор агрегации $replaceWith можно использовать для замены документа другим (в нашем случае вложенным документом) в качестве синтаксического сахара для $replaceRoot:

// { field1: { a: 1, b: 2, c: 3 }, field2: { d: 4, e: 5 } }
// { field1: { a: 6, b: 7 }, field2: { d: 8 } }
db.collection.aggregate({ $replaceWith: "$field1" })
// { a: 1, b: 2, c: 3 }
// { a: 6, b: 7 }