使用Mongodb聚合查询获取子文档中同级元素的数量

3
我有一个包含子文档标签的文件集合。
{
    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 聚合函数可以轻松地获取每个标签的数量。

然而,我想找到一种方法来计算哪些标签会同时出现。更准确地说,我想找到哪些相邻的标签最常出现,并按数量排序。我没有找到示例,也无法在不进行多次查询的情况下完成此操作。

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

{'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
    [...]}}

[...]

我认为你的意思是 "tags": [{}],因为这是数组语法,与你输入的内容相比,它才是正确的文档结构。你不能使用聚合框架创建任意的“键名”。而且看起来也不太可能得到你想要的结果(或接近),但我只是猜测,因为你的问题缺乏一个可以产生清晰预期结果的明确示例。请参见:如何创建一个最小化、完整和可验证的示例 - Neil Lunn
这只是简化的口头说明,原始文档非常大,子文档也非常大。 - tweak2
看到你已经更正了数组符号。我并不要求你提交整个代码,只需要提交一个能够从样本数据中实际获得(期望的)结果即可。这将使你的问题比现在更清晰明了。 - Neil Lunn
现在是否已经足够清楚,还是需要进一步的澄清? - tweak2
我们无法得到您所展示的期望结果,因为您提供的单个文档数据样本不足以支持。因此,我要求您提供足够的文档来产生您期望的输出示例。 - Neil Lunn
1个回答

2

如先前所述,聚合框架无法从数据中生成任意键名。也不可能在单个查询中执行此类分析。

但是,有一种通用方法可以针对整个集合进行此操作,而且标签名称数量不确定。基本上,您需要获取“标签”的不同列表,并针对每个不同值处理另一个查询,以获取该标签的“兄弟”和计数。

总体而言:

// 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版本以来,聚合框架可以返回一个游标,因此即使有大量标记,这也可以以高效的方式工作。

因此,这就是您处理此问题的方式,但确实没有办法在单个查询中完成此操作。为了缩短运行时间,您可以查看允许并行运行多个查询并合并结果或发出到流的框架。


这与我之前的方法类似,该方法是一个不同的查询,然后是 unwind 和 group。没有办法在单个 aggregate 中完成吗? - tweak2
@tweak2,由于答案中已经描述的原因,无法在单次遍历中完成。还要注意的是,.distinct() 命令返回一个数组而不是游标,因此您可以获取的结果大小有限制。使用游标和输出流可以消除这种限制。此外,.distinct() 无法像实际“兄弟姐妹”所需的那样“过滤”数组内容。 - Neil Lunn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接