【问题标题】:Aggregate on populated value with mongoose使用 mongoose 聚合填充值
【发布时间】:2017-10-29 03:22:28
【问题描述】:

我非常感谢您对以下场景的帮助。 我有这个架构:

var Song = Schema({
    author: { type: Schema.Types.ObjectId, ref: 'user' },
    title: String,
    photo: String,
    date: Date,
    duration: Number,
    views: [{ type: Schema.Types.ObjectId, ref: 'user' }],
    likes: [{ type: Schema.Types.ObjectId, ref: 'user' }]
})


var User = mongoose.Schema({
    email:String,
    name:String,
    gender: String,
    birthday: String,
  city: String,
  continent: String
});

我想编写一个查询,向用户显示每个大洲的歌曲组的总喜欢和观看次数。例如:

{continent: 'Asia', views:4000, likes:5000},
{continent: 'Europe', views:3200, likes:4500}

我也很高兴知道它是否被认为是“繁重的查询”,也许将类似的内容保存为 userId 与大陆的组合是否是一个更聪明的主意。

【问题讨论】:

    标签: mongodb mongoose aggregation-framework mongoose-schema


    【解决方案1】:

    而不是.populate()(这是“客户端”端操作),您希望数据在服务器上“加入”,其中.aggregate() 是“服务器”端操作。这就是 $lookup 运算符的用途:

    在执行$lookup 之前,首先使用$map$concatArrays 可能是最佳选择

    Song.aggregate([
      { "$project": {
        "author": "$author",
        "data": {
          "$concatArrays": [
            { "$map": {
              "input": "$views",
              "as": "el",
              "in": { "type": "views", "_id": "$$el" }
            }},
            { "$map": {
              "input": "$likes",
              "as": "el",
              "in": { "type": "likes", "_id": "$$el" }
            }}
          ]
        }
      }},
      { "$unwind": "$data" },
      { "$lookup": {
        "from": "users",
        "localField": "data._id",
        "foreignField": "_id",
        "as": "data._id"
      }},
      { "$unwind": "$data._id" },
      { "$group": {
        "_id": {
          "author": "$author",
          "continent": "$data._id.continent"
        },
        "views": {
          "$sum": { "$cond": [ { "$eq": [ "$data.type", "views" ] }, 1, 0 ] }
        },
        "likes": {
          "$sum": { "$cond": [ { "$eq": [ "$data.type", "likes" ] }, 1, 0 ] }
        }
      }}
    ], function(err, results) {
    
    })
    

    您在开始时执行“数组连接”,因为在某些时候您希望“喜欢”和“视图”在一个数组中,因为如果我们尝试通过以后的 $unwind 操作单独处理它们(并且您需要为了“计算”“continent”的值)然后你最终得到一个“笛卡尔积”,因为一个数组的内容将乘以另一个数组中的内容。

    因此,我们用“类型”标记“加入”,因为它们不再位于单独的字段中,并且我们仍然需要区分“喜欢”和“浏览量”以进行计数。

    $lookup 操作在最现代的版本中能够处理“平面”数组,但不能处理从第一阶段构造的“文档数组”。只需$unwind 先处理即可。

    一旦$lookup 完成,这种形式的结果将是"data._id" 路径中包含的每个结果的单个元素数组。为了继续处理我们再次$unwind

    最后是$group,其中“复合键”用于从连接数据中获得的“作者”和“大陆”值。为了计数,“喜欢”和“浏览”中的每一个都应用于 $cond 表达式,这是一个三元 (if/then/else) 运算符。给定第一个参数中的条件,条件为true,则返回第二个参数值,或者当false 第三个参数时。

    这些表达式的结果被传递给$sum 进行累加,因此当条件匹配时,返回正数并为分组键累加。


    所有聚合都是“繁重”的操作,而执行“joins”实际上被认为更加“繁重”。

    在很多情况下,您的应用程序在运行时执行此类查询并没有什么问题。这真的归结为这是否以有效的速度运行在您的数据上。如果数据足够大以至于此类操作花费过多时间,那么您应该通过在单独的记录中累积此类摘要数据来“预先汇总”。在这种情况下,增加每个大陆的每个作者的“喜欢”或“浏览”计数等。

    【讨论】:

    • 非常感谢,这是我得到的最好的答案 - 非常有帮助:-)
    • 再问一个问题,是否可以按顺序对各大洲进行分组以使结果如下所示:{ author: UserObject, viewsAndLikes: { {continent: "Asia", views: 1000, likes : 2000}, {大陆: "欧洲, 浏览量: 0, 喜欢: 0}, {大陆: "美国, 浏览量: 10, 喜欢: 20}, } }
    猜你喜欢
    • 2019-04-23
    • 2018-05-20
    • 1970-01-01
    • 2018-03-10
    • 2021-12-15
    • 2021-03-19
    • 2015-08-07
    • 2016-07-11
    • 2021-10-22
    相关资源
    最近更新 更多