2

转载:https ://mongoplayground.net/p/f9BraX4emor

操场查询完成了一半的工作。它找到匹配的文档,但它不聚合bounties并且emotes使用 COUNT BY 操作。

我想要的结果是这样的:

[
  {
    "_id": ObjectId("5e7d7bf7d86d85088863f4de"),
    "channelId": "23435553",
    "bounties": {
        "user1": 150, // user1 had 2 bounties with 100 + 50 amounts
        "user2": 200, // user2 had 1 bounty with 200 amount
    },
    "emotes": {
        "A": 2, // There were 2 docs with emoteId == A
        "B": 1, // There was 1 doc with emoteId == B
    },
  }
]

我其实并不关心聚合结果的具体数据结构。例如。bounties如果并且emotes是数组而不是字典,我会很好。我认为$unwind运营商可以在这里使用,但我只是不确定如何。

目前,我进行了太多查询来满足 REST API 请求。

例如,我的 API 收到一个请求,要求为一个频道查找 10 条建议。首先,我运行 1 个返回 10 条建议的查询。然后对于每个建议,我运行 1 个查询来聚合bounties,另一个来聚合emotes。这是 21 个查询......在一个 REST API 调用中...... :(。我想运行 1 个查询,而不是 21 个。

4

1 回答 1

2

要根据文档值创建对象,我们需要使用$arrayToObject运算符。

优雅的解决方案

解释

  1. 我们执行$map运算符以获得[k:"...", v:"..."]结构。
  2. 另外,我们需要计算多少次emotes/bounties重复

db.suggestions.aggregate([
  {
    $match: {
      "channelId": "23435553"
    }
  },
  {
    $lookup: {
      from: "emotes",
      localField: "_id",
      foreignField: "suggestionId",
      as: "emotes"
    }
  },
  {
    $lookup: {
      from: "bounties",
      localField: "_id",
      foreignField: "suggestionId",
      as: "bounties"
    }
  },
  {
    $project: {
      _id: 1,
      channelId: 1,
      emotes: {
        $arrayToObject: {
          $map: {
            input: "$emotes",
            as: "emote",
            in: {
              k: "$$emote.emoteId",
              v: {
                $size: {
                  $filter: {
                    input: "$emotes",
                    as: "e",
                    cond: {
                      $eq: [
                        "$$emote.emoteId",
                        "$$e.emoteId"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      bounties: {
        $arrayToObject: {
          $map: {
            input: "$bounties",
            as: "bounty",
            in: {
              k: "$$bounty.userId",
              v: {
                $sum: {
                  $map: {
                    input: {
                      $filter: {
                        input: "$bounties",
                        as: "b",
                        cond: {
                          $eq: [
                            "$$bounty.userId",
                            "$$b.userId"
                          ]
                        }
                      }
                    },
                    in: "$$this.bountyAmount"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
])

Mongo游乐场

经典解决方案

解释

  1. 我们需要展平emotes/bounties之后$lookup
  2. 我们执行$group阶段以减少重复值
  3. 我们创建[{k:"...", v:"..."}]结构将其转换为Objectwith$arrayToObject运算符。

db.suggestions.aggregate([
  {
    $match: {
      "channelId": "23435553"
    }
  },
  {
    $lookup: {
      from: "emotes",
      localField: "_id",
      foreignField: "suggestionId",
      as: "emotes"
    }
  },
  {
    $unwind: {
      path: "$emotes",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $group: {
      _id: {
        "_id": "$_id",
        "emoteId": "$emotes.emoteId"
      },
      channelId: {
        $first: "$channelId"
      },
      count: {
        $sum: 1
      }
    }
  },
  {
    $group: {
      _id: "$_id._id",
      channelId: {
        $first: "$channelId"
      },
      emotes: {
        $push: {
          k: "$_id.emoteId",
          v: "$count"
        }
      }
    }
  },
  {
    $lookup: {
      from: "bounties",
      localField: "_id",
      foreignField: "suggestionId",
      as: "bounties"
    }
  },
  {
    $unwind: {
      path: "$bounties",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $group: {
      _id: {
        "_id": "$_id",
        "userId": "$bounties.userId"
      },
      channelId: {
        $first: "$channelId"
      },
      emotes: {
        $first: "$emotes"
      },
      bountyAmount: {
        $sum: "$bounties.bountyAmount"
      }
    }
  },
  {
    $group: {
      _id: "$_id._id",
      channelId: {
        $first: "$channelId"
      },
      emotes: {
        $first: "$emotes"
      },
      bounties: {
        $push: {
          k: "$_id.userId",
          v: "$bountyAmount"
        }
      }
    }
  },
  {
    $project: {
      _id: 1,
      channelId: 1,
      emotes: {
        $arrayToObject: "$emotes"
      },
      bounties: {
        $arrayToObject: "$bounties"
      },
      
    }
  }
])

Mongo游乐场

于 2020-06-03T07:41:31.373 回答