如何查询所有子文档
Posted
技术标签:
【中文标题】如何查询所有子文档【英文标题】: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 范式?
你能想出一个更好的方式来设计我的架构吗?
【问题讨论】:
【参考方案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 快得多,在我看来更容易理解。以上是关于如何查询所有子文档的主要内容,如果未能解决你的问题,请参考以下文章