使用mongoose聚合填充值

时间:2017-05-28 20:56:03

标签: mongodb mongoose aggregation-framework mongoose-schema

我将通过以下方案真实地为您提供帮助。 我有这个架构:

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 个答案:

答案 0 :(得分:1)

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

$map

之前,$concatArrays“类型”和$lookup可能是最优的。
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操作单独处理它们(和你需要为了“计算”“大陆”的价值,然后你得到一个“笛卡尔积”,因为一个数组的内容将乘以另一个数组中的内容。

因此,我们用“类型”标记“加入”,因为它们不再位于不同的字段中,我们仍然需要区分“喜欢”和“视图”以进行计数。

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

完成$lookup后,此表单中的结果将是"data._id"路径中包含的每个结果的单个元素数组。为了继续处理我们$unwind

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

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

所有聚合都是“重型”操作,执行“连接”实际上被认为更加“沉重”。

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