Объединить исходный массив объектов в поле «как» после $ lookup - PullRequest
0 голосов
/ 22 февраля 2019

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

{
    _id:'the-name-of-the-hero',
    name: 'Name of Hero',
    (...), //other properties to this hero

    relations: [
        {
            hero: 'the-id-of-another-hero',
            relationType: 'trust'
        },
        {
            hero: 'yet-another-id-of-another-hero',
            relationType: 'hate'
        }
    ]
}

relations.hero указывает на _id другого героя.Мне нужно было собрать больше информации о связанных героях, поэтому я использовал агрегат $lookup, чтобы сопоставить каждого из них с коллекцией "hero", чтобы собрать ее name (и другие данные, но проект упрощен для вопроса).Вот действующий в данный момент запрос:

let aggregate = db.collection('hero').aggregate([
    // grabbing an specific hero
    { $match: { _id } },
    //populate relations
    {
        $lookup: {
            from: 'hero',
            let: { letId: '$relations.hero' }, //create a local variable for the pipeline to use
            // localField: "relations.hero", //this would bring entire hero data, which is unnecessary
            // foreignField: "_id",  //this would bring entire hero data, which is unnecessary
            pipeline: [
                //match each $relations.hero (as "$$letId") in collection hero's (as "from") $_id
                { $match: { $expr: { $in: ['$_id', '$$letId'] } } },
                //grab only the _id and name of the matched heroes
                { $project: { name: 1, _id: 1 } },
                //sort by name
                { $sort:{ name: 1 } }
            ],
            //replace the current relations with the new relations
            as: 'relations',
        },
    }
]).toArray(someCallbackHere);

Короче говоря, $lookup на hero коллекции с использованием pipeline, которые соответствуют каждому из relations.hero и возвращают только _id и name (который имеет настоящее имя для печати в пользовательском интерфейсе) и замените текущий relations новым relations, создав документ как:

{
    _id:'the-name-of-the-hero',
    name: 'Name of Hero',
    (...), //other properties to this hero
    relations: [
        {
            _id: 'the-id-of-another-hero',
            name: 'The Real Name of Another Hero',
        },
        {
            _id: 'yet-another-id-of-another-hero',
            name: 'Yet Another Real Name of Another Hero',
        }
    ]
}

Вопрос:

Что я могу добавить к конвейеру, чтобы он сливал подобранных героев с оригинальным relations, чтобы иметь не только проецируемые _id и name, но и оригинальный relationType?То есть получим следующий результат:

{
    _id:'the-name-of-the-hero',
    name: 'Name of Hero',
    (...), //other properties to this hero
    relations: [
        {
            _id: 'the-id-of-another-hero',
            name: 'The Real Name of Another Hero',
            relationType: 'trust' //<= kept from the original relations
        },
        {
            _id: 'yet-another-id-of-another-hero',
            name: 'Yet Another Real Name of Another Hero',
            relationType: 'hate' //<= kept from the original relations
        }
    ]
}

Я попытался экспортировать as: 'relationsFull', а затем попытался $push с $mergeObjects как часть следующего шага в агрегации, но безуспешно.Я пытался сделать то же самое, что и шаг конвейера (вместо нового шага агрегирования), но всегда получал relations как пустой массив.

Как мне написать новый шаг агрегации для объединения старого relationsобъекты с новым поиском relations?

Примечание: рассмотрим MongoDB 3.6 или новее (то есть массив $unwind не нужен, по крайней мере для $lookup).Я запрашиваю использование драйвера Node.js, если эта информация имеет значение.

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Ну, я думаю, мы должны использовать другое имя для поля как . Отсюда мы можем использовать следующее выражение для этапа $addFields.

{
   "$addFields": {
      "relations": {
         "$reduce": {
            "input": {
               "$reduce": {
                  "input": {
                     "$zip": {
                        "inputs": [
                           "$relations",
                           "$relheros"
                        ]
                     }
                  },
                  "initialValue": [

                  ],
                  "in": {
                     "$concatArrays": [
                        "$$value",
                        "$$this"
                     ]
                  }
               }
            },
            "initialValue": {

            },
            "in": {
               "$mergeObjects": [
                  "$$value",
                  "$$this"
               ]
            }
         }
      }
   }
}

Обратите внимание, что relheros здесь - это поле as .

Мы действительно не должны $unwind и $group здесьдо того, как $unwind дешево, но $group дорого.

0 голосов
/ 22 февраля 2019

Вы можете использовать ниже агрегации

db.collection("hero").aggregate([
  { "$match": { _id } },
  { "$unwind": "$relations" },
  { "$lookup": {
    "from": "hero",
    "let": { "letId": "$relations.hero" },
    "pipeline": [
      { "$match": { "$expr": { "$eq": ["$_id", "$$letId"] } } },
      { "$project": { "name": 1 } }
    ],
    "as": "relation"
  }},
  { "$unwind": "$relation" },
  { "$addFields": { "relations.name": "$relation.name" }},
  { "$group": {
    "_id": "$_id",
    "relations": { "$push": "$relations" },
    "name": { "$first": "$name" },
    "rarity": { "$first": "$rarity" },
    "classType": { "$first": "$classType" }
  }}
])

Или альтернативно, вы можете использовать это также

db.collection("hero").aggregate([
  { "$match": { _id } },
  { "$lookup": {
    "from": "hero",
    "let": { "letId": "$relations.hero" },
    "pipeline": [
      { "$match": { "$expr": { "$in": ["$_id", "$$letId"] } } },
      { "$project": { "name": 1 } }
    ],
    "as": "lookupRelations"
  }},
  { "$addFields": {
    "relations": {
      "$map": { 
        "input": "$relations",
        "as": "rel",
        "in": {
          "$mergeObjects": [
            "$$rel",
            { "name": { "$arrayElemAt": ["$lookupRelations.name", { "$indexOfArray": ["$lookupRelations._id", "$$rel._id"] }] }}
          ]
        }
      }
    }
  }}
])
...