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

时间:2013-05-08 18:57:22

标签: mongodb aggregation-framework

这是我与MongoDB的第一天,所以请跟我一起轻松:)

我无法理解$unwind运算符,可能是因为英语不是我的母语。

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

项目运算符是我能理解的,我想(就像SELECT,不是吗?)。但是,$unwind(引用)为每个源文档中的展开数组的每个成员返回一个文档

这是JOIN吗?如果是,可以将$project(包含_idauthortitletags字段)的结果与tags数组进行比较?

注意:我从MongoDB网站上采用了这个例子,我不知道tags数组的结构。我认为这是一个简单的标签名称数组。

6 个答案:

答案 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)

  

让我们通过一个例子来理解它

这是公司文档的样子:

original document

$unwind允许我们将文档作为具有数组值字段的输入并生成输出文档,这样数组中的每个元素都有一个输出文档。 source

The $unwind stage

让我们回到我们公司的例子,看看展开阶段的使用情况。这个查询:


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"
    } }
])

生成包含金额和年份数组的文档。

project output

因为我们正在获取资金回合阵列中每个元素的筹集金额和资助年度。要解决这个问题,我们可以在此聚合管道中的项目阶段之前包含一个展开阶段,并通过说我们想要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"
    } }
])

unwind has the effect of outputting to the next stage more documents than it receives as input

如果我们查看funding_rounds数组,我们知道每个funding_rounds都有一个raised_amount和一个funded_year字段。因此,unwind将为funding_rounds数组的每个元素生成一个输出文档。现在,在此示例中,我们的值为string s。但是,无论数组中元素的值类型如何,unwind都会为这些值中的每一个生成一个输出文档,这样所讨论的字段就只有该元素。对于funding_rounds,该元素将成为这些文档之一,作为传递到funding_rounds阶段的每个文档的project的值。结果,然后运行此,现在我们得到amountyear。一个用于每个公司的每个资金回合。这意味着我们的比赛产生了许多公司文件,而这些公司文件中的每一个都会产生许多文件。每个公司文件中的每个资金回合一个。 unwind使用从match阶段传递给它的文档执行此操作。然后,每个公司的所有这些文档都会传递到project阶段。

unwind output

因此,资助者 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字段,不是数组,而是具有projectfunding_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: enter image description here

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.