使用mongodb聚合查询获取子文档中的兄弟节点数

时间:2015-03-03 01:28:56

标签: javascript mongodb mongodb-query aggregation-framework

我有一个带有标签子文档的文档集。

{
    title:"my title",
    slug:"my-title",
    tags:[
        {tagname:'tag1', id:1},
        {tagname:'tag2', id:2},
        {tagname:'tag3', id:3}]
}
{
    title:"my title2",
    slug:"my-title2",
    tags:[
        {tagname:'tag1', id:1},
        {tagname:'tag2', id:2}]
}
{
    title:"my title3",
    slug:"my-title3",
    tags:[
        {tagname:'tag1', id:1},
        {tagname:'tag3', id:3}]
}
{
    title:"my title4",
    slug:"my-title4",
    tags:[
        {tagname:'tag1', id:1},
        {tagname:'tag2', id:2},
        {tagname:'tag3', id:3}]
}

[...]

使用$ unwind + group count aggregate

计算每个标签非常简单

但是,我想找到一起找到哪些标签的计数,或者更准确地说,找出最常出现的兄弟姐妹,按计数排序。我没有找到一个例子也无法在没有多个查询的情况下弄清楚如何做到这一点。

理想情况下,最终结果是:

{'tag1':{
    'tag2':3, // tag1 and tag2 were found in a document together 3 times
    'tag3':3, // tag1 and tag3 were found in a document together 3 times
    [...]}}

{'tag2':{
    'tag1':3, // tag2 and tag1 were found in a document together 3 times
    'tag3':2, // tag2 and tag3 were found in a document together 2 times
    [...]}}

{'tag3':{
    'tag1':3, // tag3 and tag1 were found in a document together 3 times
    'tag2':2, // tag3 and tag2 were found in a document together 2 times
    [...]}}

[...]

1 个答案:

答案 0 :(得分:1)

如前所述,只是不可能让聚合框架从数据中生成任意键名。它也不可能在单个查询中进行这种分析。

但是对于不确定数量的标记名称,有一种通用方法可以在整个集合中执行此操作。基本上你需要获得一个明确的"标签列表"并为每个不同的值处理另一个查询以获得"兄弟姐妹"到那个标签和计数。

一般来说:

// Get a the unique tags
db.collection.aggregate([
    { "$unwind": "$tags" },
    { "$group": {
        "_id": "$tags.tagname"
    }}
]).forEach(function(tag) {
    var tagDoc = { };
    tagDoc[tag._id] = {};

    // Get the siblings count for that tag
    db.collection.aggregate([
        { "$match": { "tags.tagname": tag._id } },
        { "$unwind": "$tags" },
        { "$match": { "tags.tagname": { "$ne": tag._id } } },
        { "$group": {
            "_id": "$tags.tagname",
            "count": { "$sum": 1 }
        }}
    ]).forEach(function(sibling) {
          // Set the value in the master document
          tagDoc[tag._id][sibling._id] = sibling.count;   
    });
    // Just emitting for example purposes in some way
    printjson(tagDoc);
});

聚合框架可以在MongoDB 2.6之后的版本中返回游标,因此即使使用大量标记,这也可以有效地工作。

这就是你处理这个问题的方式,但实际上没有办法在单个查询中实现这一点。对于较短的运行时间,您可以查看允许并行运行许多查询的框架,或者将结果组合或者发送到流。