如何查询所有子文档

时间:2012-08-27 09:26:02

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

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

我有故事的集合,每个人都可以有一个或多个标签,就像这样:

{
    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范式呢?

你能为我的架构设计想象一个更好的方法吗?

4 个答案:

答案 0 :(得分:10)

以下是使用聚合框架执行此操作的方法(您需要使用刚刚发布的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
}

答案 1 :(得分:2)

欢迎来到Mongo

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

您创建了一个名为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.

注意查找查询中的点表示法,就是你如何进入mongo中的数组/对象。

答案 2 :(得分:2)

您可以使用MR来完成此任务。在MR中,您只需选择标签并进行投影:

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

然后你的减少将通过发出的文件运行,基本上总结了标签被看到的次数。

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

  

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

这是MongoDB中一个非常标准的问题,你不会逃避。使用可重用的结构,不可避免地需要对它进行一些复杂的查询。幸运的是,在2.2中有一个要保存的聚合框架。

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

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

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

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

当事件被触发时,你的应用程序将遍历帖子上的标签,基本上是这样做$ inc upsert:

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

因此,您现在将在整个博客中拥有一系列标记。从那里你走与MR一样的路线,只是查询这个集合来获取你的数据。你当然需要处理删除和更新事件,但你会得到一般的想法。

答案 3 :(得分:0)

嗯,有不同的方法。我认为您的解决方案与this one之间没有区别。

此外,您还可以将其map_reduce方法复制并粘贴到输出标记计数哈希。