【问题标题】:How to query all subdocuments如何查询所有子文档
【发布时间】:2012-08-21 18:50:11
【问题描述】:

我从 MongoDb 和 nodejs(使用 mongoose)开始。

我有一组Stories,每个故事都可以有一个或多个Tags,大​​概是这样:

{
    title: "The red fox",
    content: "The red fox jumps away...",
    tags: [
        {
            tagname: "fairytale",
            user: "pippo"
        },
        {
            tagname: "funny",
            user: "pluto"
        },
        {
            tagname: "fox",
            user: "paperino"
        }
    ]
},

... other stories

现在我想制作一个标签云

这意味着查询所有标签的故事。

在关系世界(例如 MySQL)中,我将有一个 Stories 表、一个 Tags 表和一个 Stories_Tags 表(多对多)。然后我会查询标签表或类似的东西。

有没有办法做到这一点? (我确定是的)

如果是,这是一个好习惯吗?还是打破了 nosql 范式?

你能想出一个更好的方式来设计我的架构吗?

【问题讨论】:

    标签: mongodb mongoose tags many-to-many nosql


    【解决方案1】:

    嗯,有不同的方法。而且我认为你的解决方案和this one没有区别。

    你也可以复制粘贴它的map_reduce方法来输出tag-count hash。

    【讨论】:

    • 我无法理解它(python)。你能在答案中简要解释一下吗?
    【解决方案2】:

    欢迎来到蒙哥

    您的数据的最佳“架构”将是这样的。

    您创建了一个名为 stories 的集合,每个故事都将是该集合中的一个文档。 然后,您可以使用类似的方式轻松查询您的数据。

    db.stories.find({ "tags.tagname": "fairytale"}); // will find all documents that have fairytale as a tagname.
    

    更新

    db.stories.find({ "tags.tagname": { $exists : true }}); // will find all documents that have a tagname.
    

    注意 find 查询中的点符号,这就是您在 mongo 中访问数组/对象的方式。

    【讨论】:

    • 好帖子......但你根本没有明白这一点!我不想按标签获取单个故事。我想获取所有标签(所有标签名)。
    • 好的,我已经更新以查找所有带有标记名的文档,而不是指定实际的标记名。
    • 嗯.... 你让我对我的英语产生了严重的怀疑:) 我的意思只是标签。是否有必要获取所有带有标签的文档?然后什么?如何“选择 DISTINCT 标记名”?
    • 您可以这样做以不从故事文档中检索标题和内容:db.stories.find({ "tags.tagname": { $exists : true }, {"title": 0, content: 0 }}) 但您需要计算具有这些标签的故事的数量。如果您遵循本教程,您可以执行一个名为 mapped-reduce 的函数:cookbook.mongodb.org/patterns/count_tags
    • @GianPaJ 该教程既好又简单,但是......它结束了创建另一个包含所有标签的集合......我只想让一个查询提取所有标签,然后我将循环、计数和区分他们。有这么奇怪吗?
    【解决方案3】:

    您可以使用 MR 来完成此操作。在 MR 中,您只需挑选标签并投影它们:

    var map = function(){
         for(var i=0;i<this.tags.length;i++){
             emit(this.tags[i].tagname, {count: 1});
         }
    }
    

    然后您的 reduce 将遍历发出的文档,基本上总结了该标签被看到的次数。

    如果您升级到最新的不稳定 2.2,您还可以使用聚合框架。您将使用聚合框架的 $project 和 $sum piplines 将标签从每个帖子中投影出来,然后将它们相加以创建基于分数的标签云,允许您根据总和调整每个标签的文本大小。

    如果是,这是一个好习惯吗?还是打破了 nosql 范式?

    这是 MongoDB 中的一个非常标准的问题,而且您不会逃避。随着可重用结构的出现,不可避免地需要对其进行一些复杂的查询。幸运的是,在 2.2 中可以保存aggregationm 框架。

    至于这是一个好还是坏的方法,这是一个非常标准的方法,因此它既不好也不坏。

    为了使结构更好,您可以将带有计数的唯一标签预先聚合到单独的集合中。这样可以更轻松地实时构建您的标签云。

    预聚合是创建通常从 MR 获得的其他集合的一种形式,无需使用 MR 或聚合框架。它通常是基于您的应用程序的事件,因此当用户创建帖子或重新标记帖子时,它将触发预聚合事件到“tag_count”集合,如下所示:

    {
        _id: {},
        tagname: "",
        count: 1
    }
    

    当事件被触发时,您的应用程序将遍历帖子上的标签,基本上像这样执行 $inc upserts:

    db.tag_count.update({tagname: 'whoop'}, {$inc: {count: 1}}, true);
    

    因此,您现在将在整个博客中拥有一组标签及其计数。从那里你走与 MR 相同的路线,只需查询此集合即可获取您的数据。您当然需要处理删除和更新事件,但您大致了解。

    【讨论】:

    • 所以,如果我现在明白了,mongodb 文档中的“计数标签”教程会创建另一个包含计算结果的集合“标签”吗?每次我想“刷新”标签云时,我都必须重新执行 MR?我不明白你对“预聚合唯一标签”的意思......如果你能帮我解决这个问题,那么正确的答案就是你的:)
    • @FabioB。好的,加了一点解释
    • @FabioB。尽管您需要确保标签集合中的区别,但这可能会起作用,您可以使用我在答案中显示的 upsert 来确保唯一性
    • @FabioB。我很惊讶 Stennie 给出了这个答案,distinct 是出了名的慢,我的意思是它确实有效,但至于它是否会在足够快的时间内完成一个大型集合......嗯 `:` 结果并不乐观.
    • 您应该查看 2.2 中发布的聚合框架。看看 Asya Kamsky 的回答。这是正确的做法。
    【解决方案4】:

    这是使用聚合框架执行此操作的方法(您需要使用刚刚发布的 2.2)。

    db.stories.aggregate(
    [
        {
            "$unwind" : "$tags"
        },
        {
            "$group" : {
                "_id" : "$tags.tagname",
                "total" : {
                    "$sum" : 1
                }
            }
        },
        {
            "$sort" : {
                "total" : -1
            }
        }
    ])
    

    您的结果将如下所示:

    {
        "result" : [
            {
                "_id" : "fairytale",
                "total" : 3
            },
            {
                "_id" : "funny",
                "total" : 2
            },
            {
                "_id" : "silly",
                "total" : 1
            },
            {
                "_id" : "fox",
                "total" : 1
            }
        ],
        "ok" : 1
    }
    

    【讨论】:

    • 这比 map reduce 快得多,在我看来更容易理解。
    猜你喜欢
    • 2017-02-28
    • 2013-10-21
    • 2019-04-14
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    相关资源
    最近更新 更多