具有对象关系而不是数组的MongoDB查找

问题描述 投票:0回答:1

我有这样的收藏集matches。我正在使用players对象{key: ObjectId, key: ObjectID}而不是经典数组[ObjectId, ObjectID]作为参考players集合

{    
 "_id": ObjectId("5eb93f8efd259cd7fbf49d55"),
 "date": "01/01/2020",
 "players": {
   "home": ObjectId("5eb93f8efd259cd7fbf49d59"),
   "away": ObjectId("5eb93f8efd259cd7fbf49d60")
 }
},
{...}

players集合:

{    
 "_id": ObjectId("5eb93f8efd259cd7fbf49d59"),
 "name": "Roger Federer"
 "country": "Suiza"
},
{    
 "_id": ObjectId("5eb93f8efd259cd7fbf49d60"),
 "name": "Rafa Nadal"
 "country": "España"
},
{...}

做mongoDB lookup的更好方法是什么?像这样的东西正确吗?

  const rows = await db.collection('matches').aggregate([
            {
                $lookup: {
                    from: "players",
                    localField: "players.home",
                    foreignField: "_id",
                    as: "players.home"                
                }            
            },
            {
                $lookup: {
                    from: "players",
                    localField: "players.away",
                    foreignField: "_id",
                    as: "players.away"                
                },
            { $unwind: "$players.home" },
            { $unwind: "$players.away" },         
            }]).toArray()

我想要这样的输出:

{
 _id: 5eb93f8efd259cd7fbf49d55,
 date: "12/05/20",
 players: { 
   home: {
     _id: 5eb93f8efd259cd7fbf49d59,
     name: "Roger Federer",
     country: "Suiza"
   },
   away: {
     _id: 5eb93f8efd259cd7fbf49d60,
     name: "Rafa Nadal",
     country: "España"
   } 
 }
}
{...}
mongodb
1个回答
1
投票

您可以在下面的汇总查询中尝试:

db.matches.aggregate([
    {
      $lookup: {
        from: "players",
        localField: "players.home",
        foreignField: "_id",
        as: "home"
      }
    },
    {
      $lookup: {
        from: "players",
        localField: "players.away",
        foreignField: "_id",
        as: "away"
      }
    },
    /** Check output of lookup is not empty array `[]` & get first doc & write it to respective field, else write the same value as original */
    {
      $project: {
        date: 1,
        "players.home": { $cond: [ { $eq: [ "$home", [] ] }, "$players.home", { $arrayElemAt: [ "$home", 0 ] } ] },
        "players.away": { $cond: [ { $eq: [ "$away", [] ] }, "$players.away", { $arrayElemAt: [ "$away", 0 ] } ] }
      }
    }
  ])

Test: mongoplayground

当前查询的更改或问题:

1]当您一个接一个地使用两个$unwind阶段时,如果homeaway字段中的任何一个在players集合中均没有匹配的文档,则结果为甚至也没有得到实际的match文件,但是为什么呢?这是因为,如果在$unwind上执行[](由查找阶段返回),则展开将从结果中删除该父文档。要解决此问题,您需要在preservenullandemptyarrays阶段中使用unwind选项。

2]好吧,还有另一种方法可以不用实际使用$unwind。因此,请勿使用as: "players.home"as: "players.away",因为您实际上是在写回原始字段,以防万一,如果找不到匹配的文档,则会将一个空数组[]写入到"home"的实际字段中]或"away"到不匹配的地方(在这种情况下,您会丢失匹配文档中特定字段中存在的实际ObjectId()值)。因此,将查找输出写入新字段。

或者甚至更有效的方法,您可以尝试使用$lookup进行一次查找,而不是两个multiple-join-conditions-with-lookup阶段:

db.matches.aggregate([
    {
      $lookup: {
        from: "players",
        let: { home: "$players.home", away: "$players.away" },
        pipeline: [
          {
            $match: { $expr: { $or: [ { $eq: [ "$_id", "$$home" ] }, { $eq: [ "$_id", "$$away" ] } ] } }
          }
        ],
        as: "data"
      }
    }
  ])

Test:

mongoplayground

注意:

此处将所有与players匹配且与awayhome字段无关的匹配文档都推入data数组,因此,为了使数据库操作简单,可以从数据库以及实际的匹配文档&您可以分担代码的工作,以将各个对象从data数组映射到players.homeplayers.away
© www.soinside.com 2019 - 2024. All rights reserved.