如何计算ObjectIds数组中的ObjectId出现次数'并将结果附加到正在返回的每个文档?

时间:2017-11-29 14:04:16

标签: javascript node.js mongodb mongoose

我有一个Tag架构:

const TagSchema = new Schema({
  label: {type: String, unique: true, minlength: 2},
  updatedAt: {type: Date, default: null},
  createdAt: {type: Date, default: new Date(), required: true},
  createdBy: {type: mongoose.Schema.Types.ObjectId, ref: 'Account', default: null}
});

const Tag = mongoose.model('Tag', TagSchema);

然后我有一个Page架构:

const PageSchema = new Schema({
  tags: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'Tag'}], default: [], maxlength: 5}
});

const Page = mongoose.model('Page', PageSchema);

如您所见,它的Tag数组中包含tags引用。

现在我需要做的是当我通过/tags获取所有标签时,我还需要计算每个标签在所有Page中使用的次数。

因此,如果Tag在所有2中使用Page次,则应在返回的标记上设置.occurrences属性。例如,这将是来自/tags的回复:

[
 {_id: '192398dsf7asdsdds8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 2},
 {_id: '19239asdasd8dsf7ds8f7d', label: 'tag 2', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 1},
 {_id: '192398dsf7zxccxads8f7d', label: 'tag 1', updatedAt: '20170102', createdAt: '20170101', createdBy: '01238198dsad8s7d78ad7', occurrences: 5},
]

我会想象我可以在像这样的mongoose pre('find')钩子中轻松实现这一点:

TagSchema.pre('find', function() {

  Page.count({tags: this._id}, (err, total) => {
    this.occurrences = total;
  });
});

然而,这里有两个问题:

  • Page.count发出错误声称它不是一个功能,我不明白为什么,因为我在其他地方使用它完全没问题。 Page已正确导入。建议您不能在钩子或类似的东西中使用count

  • this不是Tag文件。

所以我想我这样做的方式是完全错误的。

由于我是MongoDB的新手,也许有人可以为我提供更好,更有效的解决方案吗?

2 个答案:

答案 0 :(得分:1)

db.Page.aggregate([ 
{
  $unwind:"$tags"
},
{
  $group:{ 
    _id:"$tags", 
    occurrences:{$sum:1}
  }
},
{
  $lookup:{ //get data for each tag
    from:"tags",
    localField:"_id",
    foreignField:"_id",
    as:"tagsData"
  }
},
{
$project:{
  tagData:{$arrayElemAt: [ "$tagsData", 0 ]},
  occurrences:1
}
}
{
  $addFields:{ 
    "tagData._id":"$_id",
    "tagData.occurrences":"$occurrences"
  }
},
{
   $replaceRoot: { newRoot: "$tagData" }
}
])

答案 1 :(得分:0)

好的,首先,您需要getQuery(),这样您就可以访问找到tag的属性了。因此,如果您按_id找到该标记,则pre

可以在this.getQuery()._id挂钩中访问该标记

其次,你将需要使用" Parallel"中间件,可以在docs中找到。这允许find方法等到调用done函数,因此它将等到标记使用新的出现次数更新。

由于无法在pre挂钩中访问该对象,因此尚未找到它,您将不得不在findOneAndUpdate挂钩中使用pre方法。

因此代码应如下所示:

find方法:

Tag.find({ _id: "foo"}, function (err, foundTag) {
  console.log(foundTag)
})

pre挂钩:

TagSchema.pre('find', true, function (next, done) {
  mongoose.models['Page'].count({ tags: this.getQuery()._id }, (err, total) => {
    mongoose.models['Tag'].findOneAndUpdate({ _id: this.getQuery()._id }, { occurrences: total }, function () {
      next()
      done()
    })
  });
});