Объединить документы из 2 коллекций в MongoDB и перезаписать свойства на поле - PullRequest
1 голос
/ 24 февраля 2020

У меня есть 2 коллекции в MongoDB:

Collection1:

{
    _id:1,            
    Field1: "Some info",
    Field2: "Some other info",
    Elements: [
        {
            id: 0,
            Enabled: false
        },
        {
            id: 1,
            Enabled: false
        },
        {
            id: 2,
            Enabled: false
        }
    ]
 }

Collection2:

{
    Identifier: "identifier",
    ElementsOverride: [
        {
            id: 0,
            Enabled: true                    
        },
        {
            id: 1,
            Enabled: false
        },
        {
            id: 2,
            Enabled: true                    
        }
    ]
 }

То, что я хотел бы сделать, это выполнить операцию, которая выравнивает коллекцию «Элемент» и возвращает Collection1 со сглаженной коллекцией Element (в основном поле Enabled из коллекции 2 перезаписывает включенное поле Collection 1.

Есть ли способ чтобы добиться этого в Mongodb?


Добавим больше разъяснений о том, каким должен быть вывод: По сути, я пытаюсь объединить документ, обозначенный _id:1 в коллекции 1 (document1), с документом, идентифицированным по идентификатору: «идентификатор» в коллекции 2 (документ 2), такой что:

  • Все свойства в document1 и document2 доступны в выходных данных.
  • ElementsOverride из документа2 с теми же идентификаторами, что и для документа1 (например, id: 0), будут перезаписаны значения в документе1

Обязательный вывод:

{
    _id:1,     
    Identifier: "identifier",       
    Field1: "Some info",
    Field2: "Some other info",
    Elements: [
        {
            id: 0,
            Enabled: true
        },
        {
            id: 1,
            Enabled: false
        },
        {
            id: 2,
            Enabled: true
        }
    ]
 }

Ответы [ 2 ]

1 голос
/ 25 февраля 2020

Вы можете попробовать запрос ниже:

db.Collection1.aggregate([
    /** get only one required doc from Collection1 */
    { $match: { _id: 1 } },
    /** Join relative doc from Collection2 */
    {
        $lookup:
        {
            from: "Collection2",
            pipeline: [
                {
                    $match:
                    {
                        $expr:
                            { $eq: ["$Identifier", "identifier"] }
                    }
                }
            ],
            as: "data"
        }
    },
    /** As lookup will default to an array of objects getting an object out of array */
    { $unwind: '$data' },
    /** Replacing existing elements field of Collection1 & adding Identifier field to existing doc */
    {
        $addFields: {
            Identifier: '$data.Identifier', Elements:
            {
                $reduce: {
                    input: { $reverseArray: { $setUnion: ["$Elements", "$data.ElementsOverride"] } },
                    initialValue: [],
                    in: { $concatArrays: ["$$value", { $cond: [{ $in: ['$$this.id', '$$value.id'] }, [], ['$$this']] }] }
                }
            }
        }
    },
    /** removing unnecessary field created at lookup stage */
    { $project: { data: 0 } }
])

Тест: MongoDB-Playground

0 голосов
/ 25 февраля 2020

Я не уверен, как вы хотите вывод.

выравнивает коллекцию "Элемент"

обычно означает, что массив Element равен размотан . Пожалуйста, исправьте мою интерпретацию, в случае, если я неправильно понял.

Но, следующие шаги в Mon go Shell получат результат:

arr1 = db.c1.aggregate( [ { $unwind: "$Elements" }, { $sort: { "Elements.id": 1 } ] ).toArray()
arr2 = db.c2.aggregate( [ { $unwind: "$ElementsOverride" }, { $sort: { "ElementsOverride.id": 1 } ] ).toArray()

for (let i=0; i < arr1.length; i++) {
    updated = Object.assign(arr1[i].Elements, arr2[i].ElementsOverride);
    arr1[i].Elements = updated
}

Переменная arr1 будет иметь:

[
        {
                "_id" : 1,
                "Field1" : "Some info",
                "Field2" : "Some other info",
                "Elements" : {
                        "id" : 0,
                        "Enabled" : true
                }
        },
        {
                "_id" : 1,
                "Field1" : "Some info",
                "Field2" : "Some other info",
                "Elements" : {
                        "id" : 1,
                        "Enabled" : false
                }
        },
        {
                "_id" : 1,
                "Field1" : "Some info",
                "Field2" : "Some other info",
                "Elements" : {
                        "id" : 2,
                        "Enabled" : true
                }
        }
]



[РЕДАКТИРОВАТЬ ДОБАВИТЬ]

Обновлено с учетом требуемого результата:

arr2 = db.c2.aggregate( [ 
                       { $unwind: "$ElementsOverride" }, 
                       { $replaceRoot: { newRoot: "$ElementsOverride" } }
] ).toArray()

db.c1.aggregate( [ 
{ $unwind: "$Elements" }, 
{ $addFields: { 
       "Elements.Enabled": {
           $filter: {
               input: arr2,
               cond: { $eq: [ "$$this.ElementsOverride.id", "$Elements.id" ] }
           }
       }
} },
{ $group: { 
       _id: "$_id", 
       doc: { $first: "$$ROOT"}, 
       Identifier: { $first: "$Elements.Enabled.Identifier"}, 
       Elements: { $push: { $arrayElemAt: [ "$Elements.Enabled", 0 ] } } 
} },
{ $addFields: { 
       "doc.Elements": "$Elements.ElementsOverride",  
       "doc.Identifier": { $arrayElemAt: [ "$Identifier", 0 ]  }
} },
{ $replaceRoot: { newRoot: "$doc" } }
] )


[РЕДАКТИРОВАТЬ ДОБАВИТЬ 2]

Вот еще один способ объединения документов:

doc1 = db.c1.findOne()
arr2 = db.c2.aggregate( [ { $unwind: "$ElementsOverride" } ] ).toArray()

for (let e2 of arr2) {
  for (i = 0; i < doc1.Elements.length; i++) {
      if (doc1.Elements[i].id == e2.ElementsOverride.id) {
          doc1.Elements[i].Enabled = e2.ElementsOverride.Enabled
          doc1.Identifier = e2.Identifier
      }
  }
}

Выходные данные - это doc1 документ.

...