这是我与MongoDB的第一天,所以请跟我一起轻松:)
我无法理解$unwind
运算符,可能是因为英语不是我的母语。
db.article.aggregate(
{ $project : {
author : 1 ,
title : 1 ,
tags : 1
}},
{ $unwind : "$tags" }
);
项目运算符是我能理解的,我想(就像SELECT
,不是吗?)。但是,$unwind
(引用)为每个源文档中的展开数组的每个成员返回一个文档。
这是JOIN
吗?如果是,可以将$project
(包含_id
,author
,title
和tags
字段)的结果与tags
数组进行比较?
注意:我从MongoDB网站上采用了这个例子,我不知道tags
数组的结构。我认为这是一个简单的标签名称数组。
答案 0 :(得分:164)
首先,欢迎来到MongoDB!
要记住的是MongoDB采用“NoSQL”方法进行数据存储,因此会消除您心中的选择,联接等想法。它存储数据的方式是文档和集合的形式,这允许动态的方式从存储位置添加和获取数据。
话虽如此,为了理解$ 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的功能是允许您为每个元素剥离文档并返回生成的文档。 要想在经典方法中使用它,它将是“对于tags数组中的每个项目,仅返回仅包含该项目的文档”的等效项。
因此,运行以下结果:
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
}
请注意,结果数组中唯一更改的内容是在tags值中返回的内容。如果您需要有关其工作原理的其他参考,我已添加了一个链接here。希望这对您进入迄今为止遇到的最好的NoSQL系统之一有所帮助,祝你好运。
答案 1 :(得分:35)
$unwind
复制管道中的每个文档,每个数组元素一次。
因此,如果您的输入管道包含一个文章文档,其中包含tags
中的两个元素,{$unwind: '$tags'}
会将管道转换为两个文章文档,除tags
字段外,它们是相同的。在第一个文档中,tags
将包含原始doc数组中的第一个元素,而在第二个doc中,tags
将包含第二个元素。
答案 2 :(得分:12)
让我们通过一个例子来理解它
这是公司文档的样子:
$unwind
允许我们将文档作为具有数组值字段的输入并生成输出文档,这样数组中的每个元素都有一个输出文档。 source
让我们回到我们公司的例子,看看展开阶段的使用情况。这个查询:
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"
} }
])
生成包含金额和年份数组的文档。
因为我们正在获取资金回合阵列中每个元素的筹集金额和资助年度。要解决这个问题,我们可以在此聚合管道中的项目阶段之前包含一个展开阶段,并通过说我们想要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"
} }
])
如果我们查看funding_rounds
数组,我们知道每个funding_rounds
都有一个raised_amount
和一个funded_year
字段。因此,unwind
将为funding_rounds
数组的每个元素生成一个输出文档。现在,在此示例中,我们的值为string
s。但是,无论数组中元素的值类型如何,unwind
都会为这些值中的每一个生成一个输出文档,这样所讨论的字段就只有该元素。对于funding_rounds
,该元素将成为这些文档之一,作为传递到funding_rounds
阶段的每个文档的project
的值。结果,然后运行此,现在我们得到amount
和year
。一个用于每个公司的每个资金回合。这意味着我们的比赛产生了许多公司文件,而这些公司文件中的每一个都会产生许多文件。每个公司文件中的每个资金回合一个。 unwind
使用从match
阶段传递给它的文档执行此操作。然后,每个公司的所有这些文档都会传递到project
阶段。
因此,资助者 Greylock 的所有文件(如查询示例中)将被分成若干文件,等于每个与过滤器匹配的公司的资金轮数{ {1}}。然后,每个生成的文档都将传递给我们的$match: {"funding_rounds.investments.financial_org.permalink": "greylock" }
。现在,project
为它作为输入接收的每个文档生成一个精确的副本。所有字段都具有相同的键和值,但有一个例外,即unwind
字段而不是funding_rounds
文档数组,而是具有单个文档的值,这是一个个人资金回合。因此,拥有 4 资金回合的公司将导致funding_rounds
创建 4 文档。除了unwind
字段之外,每个字段都是精确副本,而不是每个字段的数组,而是来自公司文档中funding_rounds
数组的单个元素{ {1}}目前正在处理中。因此,funding_rounds
具有向下一阶段输出的文档多于它作为输入接收的文档的效果。这意味着我们的unwind
阶段现在再次获得unwind
字段,不是数组,而是具有project
和funding_rounds
的嵌套文档领域。因此,raised_amount
将为每个公司funded_year
收到多个文档,因此可以单独处理每个文档,并为每个公司的每个资金回合确定个人金额和年份强>
答案 3 :(得分:2)
根据mongodb官方文档:
$ unwind 从输入文档中解构一个数组字段,以输出每个元素的文档。每个输出文档都是输入文档,其中array字段的值被元素替换。
通过基本示例进行解释:
馆藏清单包含以下文件:
{ "_id" : 1, "item" : "ABC", "sizes": [ "S", "M", "L"] }
{ "_id" : 2, "item" : "EFG", "sizes" : [ ] }
{ "_id" : 3, "item" : "IJK", "sizes": "M" }
{ "_id" : 4, "item" : "LMN" }
{ "_id" : 5, "item" : "XYZ", "sizes" : null }
以下$ 展开操作是等效的,并为 size 字段中的每个元素返回一个文档。如果sizes字段不能解析为数组,但不丢失,为null或为空数组,则$ unwind会将非数组操作数视为单个元素数组。
db.inventory.aggregate( [ { $unwind: "$sizes" } ] )
或
db.inventory.aggregate( [ { $unwind: { path: "$sizes" } } ]
以上查询输出:
{ "_id" : 1, "item" : "ABC", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "sizes" : "L" }
{ "_id" : 3, "item" : "IJK", "sizes" : "M" }
为什么需要它?
$ unwind在执行聚合时非常有用。在执行排序,搜寻等各种操作之前,它将复杂/嵌套的文档分解为简单文档。
要了解有关$ unwind的更多信息:
https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
要了解有关聚合的更多信息:
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
答案 4 :(得分:2)
请考虑以下示例以了解这一点 集合中的数据
{
"_id" : 1,
"shirt" : "Half Sleeve",
"sizes" : [
"medium",
"XL",
"free"
]
}
查询-db.test1.aggregate([{$ unwind:“ $ sizes”}]);
输出
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "medium" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "XL" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "free" }
答案 5 :(得分:1)
Let me explain in a way corelated to RDBMS way. This is the statement:
db.article.aggregate(
{ $project : {
author : 1 ,
title : 1 ,
tags : 1
}},
{ $unwind : "$tags" }
);
to apply to the document / record:
{
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 }
}
The $project / Select simply returns these field/columns as
SELECT author, title, tags FROM article
Next is the fun part of Mongo, consider this array tags : [ "fun" , "good" , "fun" ]
as another related table (can't be a lookup/reference table because values has some duplication) named "tags". Remember SELECT generally produces things vertical, so unwind the "tags" is to split() vertically into table "tags".
The end result of $project + $unwind:
Translate the output to 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"}
Because we didn't tell Mongo to omit "_id" field, so it's auto-added.
The key is to make it table-like to perform aggregation.