使用 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 聚合填充值的主要内容,如果未能解决你的问题,请参考以下文章
与 mongoDb 和 mongoose 聚合后填充单个 ObjectId 引用