假设我有一个像这样的集合:

{
  _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以获取完整的说明和其他可以在此处完成的操作。

07-24 16:39
查看更多