1

How to aggregate collection sub-array objects with their actual object which is present in the other collection?

Ex: This is how my collections look like

Collection 1 - (Teams)

const Teams = [
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a1',
        teamName: 'World XI A',
        totalPlayers: 15,
        player: [
            {
                playerId: '60f3b0b3f0b9a1b0b4f9b1a2',
                playerName: 'Player A',
                score: 10,
            },
            {
                playerId: '60f3b0b3f0b9a1b0b4f9b1a3',
                playerName: 'Player B',
                score: 20,
            },
        ],
    },
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a4',
        teamName: 'Crackers',
        totalPlayers: 15,
        player: [
            {
                playerId: '60f3b0b3f0b9a1b0b4f9b1a5',
                playerName: 'Player C',
                score: 30,
            },
            {
                playerId: '60f3b0b3f0b9a1b0b4f9b1a6',
                playerName: 'Player D',
                score: 40,
            },
        ],
    },
];

Collection 2 - (Players)

const Players = [
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a2',
        playerName: 'Player A',
        rank: 2,
        totalRuns: 5000,
    },
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a3',
        playerName: 'Player B',
        rank: 1,
        totalRuns: 3050,
    },
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a5',
        playerName: 'Player C',
        rank: 3,
        totalRuns: 2050,
    },
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a6',
        playerName: 'Player D',
        rank: 4,
        totalRuns: 1000,
    },
];

Result: This is how the resultant array should look like

Resultant array

const result = [
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a1',
        teamName: 'World XI A',
        totalPlayers: 15,
        player: [
            {
                playerInfo: {
                    _id: '60f3b0b3f0b9a1b0b4f9b1a2',
                    playerName: 'Player A',
                    rank: 2,
                    totalRuns: 5000,
                },
                playerName: 'Player A',
                score: 10,
            },
            {
                playerInfo: {
                    _id: '60f3b0b3f0b9a1b0b4f9b1a3',
                    playerName: 'Player B',
                    rank: 1,
                    totalRuns: 3050,
                },
                playerName: 'Player B',
                score: 20,
            },
        ],
    },
    {
        _id: '60f3b0b3f0b9a1b0b4f9b1a4',
        teamName: 'Crackers',
        totalPlayers: 15,
        player: [
            {
                playerInfo: {
                    _id: '60f3b0b3f0b9a1b0b4f9b1a5',
                    playerName: 'Player C',
                    rank: 3,
                    totalRuns: 2050,
                },
                playerName: 'Player C',
                score: 30,
            },
            {
                playerInfo: {
                    _id: '60f3b0b3f0b9a1b0b4f9b1a6',
                    playerName: 'Player D',
                    rank: 4,
                    totalRuns: 1000,
                },
                playerName: 'Player D',
                score: 40,
            },
        ],
    },
];
Yong Shun
  • 35,286
  • 4
  • 24
  • 46
Rajath
  • 2,611
  • 8
  • 27
  • 43
  • Does this answer your question? [JavaScript merging objects by id](https://stackoverflow.com/questions/19480008/javascript-merging-objects-by-id) – pilchard Jun 03 '23 at 10:31
  • It depends on if you want to query the result directly from Mongo or aggregate them on the front end. Answers for both below. – pilchard Jun 03 '23 at 10:43

2 Answers2

3
  1. $lookup - Join both Teams and Players collections with fields. And return the players array field.

  2. $set - Set the player field.

    2.1. $map - Iterate element in the player array and return a new array.

    2.1.1. $mergeObjects - Merge the current iterated document with the document with the playerInfo field from the result 2.1.1.1.

    2.1.1.1. $first - Get the first matching document from the result 2.1.1.1.1.

    2.1.1.1.1. $filter - Filter the document in the players array that matches the _id.

  3. $unset - Remove the fields: players field and player.playerId fields.

db.Teams.aggregate([
  {
    "$lookup": {
      "from": "Players",
      "localField": "player.playerId",
      "foreignField": "_id",
      "as": "players"
    }
  },
  {
    $set: {
      player: {
        $map: {
          input: "$player",
          as: "p",
          in: {
            $mergeObjects: [
              "$$p",
              {
                playerInfo: {
                  $first: {
                    $filter: {
                      input: "$players",
                      cond: {
                        $eq: [
                          "$$p.playerId",
                          "$$this._id"
                        ]
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $unset: [
      "players",
      "player.playerId"
    ]
  }
])

Demo @ Mongo Playground

Yong Shun
  • 35,286
  • 4
  • 24
  • 46
1

It is unclear whether you are looking to accomplish this in a MongoDB query or whether you want to aggregate the data in javascript. If the latter then it is really just a case of a nested map over the Player array within each team in the Teams array and adding the matching element from the Players array by id. Here creating a Map of players by id first for efficient access.

const Teams = [{ _id: '60f3b0b3f0b9a1b0b4f9b1a1', teamName: 'World XI A', totalPlayers: 15, player: [{ playerId: '60f3b0b3f0b9a1b0b4f9b1a2', playerName: 'Player A', score: 10, }, { playerId: '60f3b0b3f0b9a1b0b4f9b1a3', playerName: 'Player B', score: 20, },], }, { _id: '60f3b0b3f0b9a1b0b4f9b1a4', teamName: 'Crackers', totalPlayers: 15, player: [{ playerId: '60f3b0b3f0b9a1b0b4f9b1a5', playerName: 'Player C', score: 30, }, { playerId: '60f3b0b3f0b9a1b0b4f9b1a6', playerName: 'Player D', score: 40, },], },];
const Players = [{ _id: '60f3b0b3f0b9a1b0b4f9b1a2', playerName: 'Player A', rank: 2, totalRuns: 5000, }, { _id: '60f3b0b3f0b9a1b0b4f9b1a3', playerName: 'Player B', rank: 1, totalRuns: 3050, }, { _id: '60f3b0b3f0b9a1b0b4f9b1a5', playerName: 'Player C', rank: 3, totalRuns: 2050, }, { _id: '60f3b0b3f0b9a1b0b4f9b1a6', playerName: 'Player D', rank: 4, totalRuns: 1000, },];

const playerInfoMap = new Map(Players.map((p) => [p._id, p]));

const result = Teams.map(({ player, ...team }) => ({
  ...team,
  player: player.map(({ playerId, ...player }) => ({
    ...player,
    ...(playerInfoMap.has(playerId)
      ? { playerInfo: playerInfoMap.get(playerId) }
      : { playerId }),
  })),
}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23