假设我有一个像这样的集合:
{
_id : 544e97123c9ef694fc68e21b,
title: "First Title",
notebook: {
title: "Misc",
slug: "misc"
}
}
{
_id: 54ab035a849788d0921d8eb2,
title: "Second Title",
notebook: {
title: "Personal",
slug: "personal"
}
}
{
_id: 544e97123c9ef694fc68e21b,
title: "Third Title",
notebook: {
title: "Misc",
slug: "misc"
}
}
在我看来,我希望能够显示笔记本标题被使用了多少次,并以特定顺序显示了指向该笔记本子标题的链接。例如:
<a href="/notebooks/misc">Misc(2)</a>
<a href="/notebooks/personal">Personal(1)</a>
我已经通过遍历每个文档来实现此目的,但是问题在于它有重复项,因为它正在遍历每个文档。所以在我看来,它看起来像这样:
<a href="/notebooks/misc">Misc(2)</a>
<a href="/notebooks/personal">Personal(1)</a>
<a href="/notebooks/misc">Misc(2)</a>
我该如何获取notebook.title,notebook.slug,对其进行计数并且没有重复项?
这是我目前的骇客方式(导致重复):
function countNotebooks(notes) {
var table = Object.create(null);
for (var i = 0; i < notes.length; i++) {
if (typeof table[notes[i].notebook.slug] === 'undefined') {
table[notes[i].notebook.slug] = 1;
} else {
table[notes[i].notebook.slug] += 1;
}
}
return table;
}
app.get('/notebooks', function(req, res) {
Note.find(function(err, notes) {
if (err) {
throw err;
}
res.render('notebooks/index.html', {
title: 'All Notebooks',
jumbotron: 'Notebooks',
notes: notes,
notesTable: countNotebooks(notes)
});
});
});
notebooks / index.html:
{% for note in notes %}
<article class="note">
<h3 class="note-title">
<a href="/notebooks/{{ note.notebook.slug }}">{{ note.notebook.title }}</a> <span class="count">({{ notesTable[note.notebook.slug] }})</span>
</h3>
</article>
{% endfor %}
最佳答案
您可以通过本质上根据公用密钥对数据进行分组来实现此目的。 MongoDB的aggregation framework专为这种聚合和操作而设计。
但是首先,由于您有重复的_id
值(不允许这样做),因此我可以更正您的数据样本吗?
{
"_id" : ObjectId("544e97123c9ef694fc68e21b"),
"title" : "First Title",
"notebook" : {
"title" : "Misc",
"slug" : "misc"
}
},
{
"_id" : ObjectId("54ab035a849788d0921d8eb2"),
"title" : "Second Title",
"notebook" : {
"title" : "Personal",
"slug" : "personal"
}
},
{
"_id" : ObjectId("54ac074fa8a621d3fd49ac91"),
"title" : "Third Title",
"notebook" : {
"title" : "Misc",
"slug" : "misc"
}
}
要在数据中添加“ sl”事件的“ count”值,您可以形成如下管道:
Note.aggregate([
// Group on the slug values and put other fields in an array
{ "$group": {
"_id": "$notebook.slug",
"count": { "$sum": 1 },
"docs": {
"$push": {
"_id": "$_id",
"title": "$title",
"notebook": "$notebook"
}
}
}},
// Unwind the created array elements
{ "$unwind": "$docs" },
// Re-structure back to original form
{ "$project": {
"_id": "$docs._id",
"title": "$docs.title",
"count": "$count",
"notebook": "$docs.notebook"
}},
// Sort in original order (or as desired)
{ "$sort": { "_id": 1 } }
],function(err,result) {
});
这样的结果是:
{
"_id" : ObjectId("544e97123c9ef694fc68e21b"),
"count" : 2,
"title" : "First Title",
"notebook" : {
"title" : "Misc",
"slug" : "misc"
}
},
{
"_id" : ObjectId("54ab035a849788d0921d8eb2"),
"count" : 1,
"title" : "Second Title",
"notebook" : {
"title" : "Personal",
"slug" : "personal"
}
},
{
"_id" : ObjectId("54ac074fa8a621d3fd49ac91"),
"count" : 2,
"title" : "Third Title",
"notebook" : {
"title" : "Misc",
"slug" : "misc"
}
}
那就是如果您想“保留文档”,但是如果您只想要像“刻面计数”这样的唯一“子弹”,则只需在笔记本标题上使用带有
$group
的第一个$first
而不是使用带有$push
的其他内容:Note.aggregate([
// Group on the slug values and put other fields in an array
{ "$group": {
"_id": "$notebook.slug",
"count": { "$sum": 1 },
"title": { "$first": "$notebook.title" }
}},
],function(err,result) {
});
它应该是相当自我解释的,只是为了总结。初始
$group
使用值“ slug”完成,以使用$sum
运算符计数出现次数。为了保留其余文档数据,使用$push
将其放在“子句”下的数组中。分组后,使用
$unwind
将数组反规范化为文档,然后使用$project
将其简单地重新构造回原始格式。最终的$sort
操作提供了原始顺序或您想要的任何内容,因为在$group
流水线阶段更改了顺序。这不仅会得到结果,而且还允许您使用
$limit
和$skip
运算符对页面值进行“分页”,并将计数值放在适当的位置,甚至在需要时根据这些计数值对数据进行排序。查看完整的a ggregation pipeline operator reference以获取完整的说明和其他可以在此处完成的操作。