使用 mongoose 聚合填充值

Posted

技术标签:

【中文标题】使用 mongoose 聚合填充值【英文标题】:Aggregate on populated value with 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 与大陆的组合是否是一个更聪明的主意。

【问题讨论】:

【参考方案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,

以上是关于使用 mongoose 聚合填充值的主要内容,如果未能解决你的问题,请参考以下文章

在 mongoose 的聚合中填充数组的 id

与 mongoDb 和 mongoose 聚合后填充单个 ObjectId 引用

MongoDB Mongoose 聚合查询深度嵌套数组删除空结果并填充引用

有引用时不调用 Mongoose 模型填充回调

有引用时不调用 Mongoose 模型填充回调

在 mongoose 聚合中修改 $cond 的结果值?