MongoDB中的$ unwind运算符是什么?
这是我第一次使用MongoDB,所以请容易与我:)
我无法理解$unwind
操作符,也许是因为英文不是我的母语。
db.article.aggregate( { $project : { author : 1 , title : 1 , tags : 1 }}, { $unwind : "$tags" } );
项目操作员是我能理解的,我想(这就像SELECT
,不是吗?)。 但是, $unwind
(citing)会为每个源文档中展开的数组的每个成员返回一个文档 。
这就像一个JOIN
? 如果是的话, $project
(带_id
, author
, title
和tags
字段)的结果如何与tags
数组进行比较?
注 :我已经从MongoDB网站的例子,我不知道tags
数组的结构。 我认为这是一个简单的标签名称数组。
首先,欢迎来到MongoDB!
需要记住的是,MongoDB采用了“NoSQL”的方式来存储数据,所以在你的脑海中消除了select,连接等的想法。 它存储数据的方式是以文档和集合的forms存在,这允许从存储位置添加和获取数据的dynamic方法。
这就是说,为了理解$ unwind参数背后的概念,您首先必须了解您要引用的用例是什么意思。 mongodb.org的范例文件如下:
{ title : "this is my title" , author : "bob" , posted : new Date () , pageViews : 5 , tags : [ "fun" , "good" , "fun" ] , comments : [ { author :"joe" , text : "this is cool" } , { author :"sam" , text : "this is bad" } ], other : { foo : 5 } }
注意标签实际上是一个3个项目的数组,在这种情况下是“有趣”,“好”和“有趣”。
$ unwind是允许你剥离每个元素的文档,并返回结果文档。 用传统的方法来思考这个问题,就是“为标签数组中的每个项目返回一个只包含该项目的文档”。
于是,运行结果如下:
db.article.aggregate( { $project : { author : 1 , title : 1 , tags : 1 }}, { $unwind : "$tags" } );
将返回以下文件:
{ "result" : [ { "_id" : ObjectId("4e6e4ef557b77501a49233f6"), "title" : "this is my title", "author" : "bob", "tags" : "fun" }, { "_id" : ObjectId("4e6e4ef557b77501a49233f6"), "title" : "this is my title", "author" : "bob", "tags" : "good" }, { "_id" : ObjectId("4e6e4ef557b77501a49233f6"), "title" : "this is my title", "author" : "bob", "tags" : "fun" } ], "OK" : 1 }
请注意,结果数组中唯一改变的是标签值中返回的内容。 如果你需要一个关于如何工作的额外的参考,我已经在这里包含一个链接。 希望这会有所帮助,祝你好运,进入迄今为止我所遇到的最好的NoSQL系统之一。
$unwind
重复pipe道中的每个文档,每个数组元素一次。
因此,如果您的inputpipe道包含一个包含两个tags
元素的文章文档,则{$unwind: '$tags'}
会将这个pipe道转换为两个文档文档,除了tags
字段以外。 在第一个文档中, tags
将包含原始文档数组的第一个元素,而在第二个文档中, tags
将包含第二个元素。
让我们通过一个例子来理解它
这是公司文件的样子:
$unwind
允许我们将文档作为具有数组值的字段input,并生成输出文档,以便数组中的每个元素都有一个输出文档。 资源
那么让我们回到我们公司的例子,并看看放松阶段的使用。 这个查询:
db.companies.aggregate([ { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } }, { $project: { _id: 0, name: 1, amount: "$funding_rounds.raised_amount", year: "$funding_rounds.funded_year" } } ])
db.companies.aggregate([ { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } }, { $project: { _id: 0, name: 1, amount: "$funding_rounds.raised_amount", year: "$funding_rounds.funded_year" } } ])
生成具有数量和年份数组的文档。
因为我们正在访问资金轮次列表中每个元素的募集金额和资助年份。 为了解决这个问题,我们可以在这个聚合pipe道的项目阶段之前包括一个展开阶段,并且通过说明我们想要unwind
资金轮次数组来参数化这个阶段:
db.companies.aggregate([ { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } }, { $unwind: "$funding_rounds" }, { $project: { _id: 0, name: 1, amount: "$funding_rounds.raised_amount", year: "$funding_rounds.funded_year" } } ])
db.companies.aggregate([ { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } }, { $unwind: "$funding_rounds" }, { $project: { _id: 0, name: 1, amount: "$funding_rounds.raised_amount", year: "$funding_rounds.funded_year" } } ])
如果我们看一下funding_rounds
数组,我们知道每个funding_rounds
都有一个raised_amount
和一个funded_year
字段。 因此, unwind
将为每个作为funding_rounds
数组元素的文档生成一个输出文档。 现在,在这个例子中,我们的值是string
s。 但是,不pipe数组中元素的值的types如何, unwind
将为这些值中的每一个产生一个输出文档,使得所讨论的字段将具有该元素。 在获得funding_rounds
的情况下,这些元素将成为这些文档中的一个,作为每个文档传递到project
阶段的funding_rounds
价值。 结果,然后运行这个,现在我们得到一个amount
和year
。 为我们collections中的每家公司筹集一笔资金 。 这意味着我们的比赛产生了许多公司文件,而这些公司文件中的每一个都产生了许多文件。 每个公司文件中的每一轮融资都有一个。 unwind
使用从match
阶段交给它的文件执行此操作。 然后将所有这些文件都传递给project
阶段。
因此,所有出资者是Greylock的文件 (如查询示例中)将被拆分成多个文件,等于每个与filter$match: {"funding_rounds.investments.financial_org.permalink": "greylock" }
匹配的公司的资金轮次数$match: {"funding_rounds.investments.financial_org.permalink": "greylock" }
。 然后每一个结果文件将被传递到我们的project
。 现在, unwind
为每个接收到的文档生成一个精确的副本。 所有的领域都有相同的关键和价值,只有一个例外,那就是funding_rounds
领域而不是一系列funding_rounds
文件,而是有一个单一的文件的价值,这是一个单独的资金轮。 所以,一个有4轮融资的公司将会unwind
创build4个文件。 在每个字段都是精确副本的情况下,除了funding_rounds
字段之外,将不是每个副本的数组,而是来自公司文档的funding_rounds
数组中的单个元素, unwind
当前正在处理该数组。 因此, unwind
具有输出到下一个阶段比input更多的文档的效果。 这意味着我们的project
阶段现在获得了一个funding_rounds
字段, funding_rounds
字段不是一个数组,而是一个嵌套的文档,它有一个raised_amount
和一个funded_year
字段。 因此, project
将为每个match
filter的公司接收多个文件,因此可以单独处理每个文件,并为每个公司的每个资助轮次确定一个单独的金额和年份 。
让我以一种与RDBMS方式相关的方式来解释。 这是声明:
db.article.aggregate( { $project : { author : 1 , title : 1 , tags : 1 }}, { $unwind : "$tags" } );
申请文件/logging :
{ title : "this is my title" , author : "bob" , posted : new Date () , pageViews : 5 , tags : [ "fun" , "good" , "fun" ] , comments : [ { author :"joe" , text : "this is cool" } , { author :"sam" , text : "this is bad" } ], other : { foo : 5 } }
$ project / Select只是返回这些字段/列
select作者,标题,标签从文章
接下来是Mongo的有趣的一部分,考虑这个数组tags : [ "fun" , "good" , "fun" ]
作为另一个相关的表(不能是查找/参考表,因为值有一些重复) 。 记住SELECT通常产生垂直的东西,所以展开“标签”就是垂直分割()到表格“标签”中。
$ project + $ unwind的最终结果:
将输出转换为JSON:
{ "author": "bob", "title": "this is my title", "tags": "fun"}, { "author": "bob", "title": "this is my title", "tags": "good"}, { "author": "bob", "title": "this is my title", "tags": "fun"}
因为我们没有告诉Mongo省略“_id”字段,所以它是自动添加的。
关键是让它像表一样进行聚合。