MongoDB中的$ unwind运算符是什么?

这是我第一次使用MongoDB,所以请容易与我:)

我无法理解$unwind操作符,也许是因为英文不是我的母语。

 db.article.aggregate( { $project : { author : 1 , title : 1 , tags : 1 }}, { $unwind : "$tags" } ); 

项目操作员是我能理解的,我想(这就像SELECT ,不是吗?)。 但是, $unwind (citing)会为每个源文档中展开的数组的每个成员返回一个文档

这就像一个JOIN ? 如果是的话, $project (带_idauthortitletags字段)的结果如何与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,并生成输出文档,以便数组中的每个元素都有一个输出文档。 资源

$ unwind阶段

那么让我们回到我们公司的例子,并看看放松阶段的使用。 这个查询:

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价值。 结果,然后运行这个,现在我们得到一个amountyear 。 为我们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将为每个matchfilter的公司接收多个文件,因此可以单独处理每个文件,并为每个公司的每个资助轮次确定一个单独的金额和年份

让我以一种与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”字段,所以它是自动添加的。

关键是让它像表一样进行聚合。