9

我从 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 范式?

你能想象一个更好的模式设计方法吗?

4

4 回答 4

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
}
于 2012-08-29T01:34:47.803 回答
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 中访问数组/对象的方式。

于 2012-08-27T10:32:51.337 回答
1

您可以使用 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 管道从每个帖子中投影标签,然后将它们相加以创建基于分数的标签云,允许您根据总和调整每个标签的文本大小。

如果是,这是一个好习惯吗?还是它打破了 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 相同的路线,只需查询此集合即可获取您的数据。您当然需要处理删除和更新事件,但您大致了解。

于 2012-08-27T11:45:13.693 回答
0

嗯,有不同的方法。我认为你的解决方案和这个没有区别。

您还可以复制并粘贴其 map_reduce 方法以输出标签计数哈希。

于 2012-08-27T10:24:41.913 回答