Что такое оператор $unwind в MongoDB?
Это мой первый день с 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
. Я думаю, что это простой массив имен тегов.
Ответы
Ответ 1
Прежде всего, добро пожаловать в 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 делает, позволяет вам очистить документ от каждого элемента и вернуть этот результирующий документ.
Чтобы думать об этом в классическом подходе, это было бы равнозначно "для каждого элемента в массиве тегов, вернуть документ только с этим элементом".
Таким образом, результат выполнения:
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, с которыми я до сих пор сталкивался.
Ответ 2
$unwind
дублирует каждый документ в конвейере, один раз для элемента массива.
Итак, если ваш входной конвейер содержит один документ статьи с двумя элементами в tags
, {$unwind: '$tags'}
преобразует конвейер в два документа документа, которые являются теми же, за исключением поля tags
. В первом документе tags
будет содержать первый элемент из исходного массива doc, а во втором документе tags
будет содержать второй элемент.
Ответ 3
Поясним это на примере
Вот как выглядит документ компании:
![исходный документ]()
$unwind
позволяет нам принимать документы в качестве входных данных, которые имеют поле с оценкой массива и выдает выходные документы, так что есть один выходной документ для каждого элемента массива. источник
![Стадия $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"
} }
])
создает документы с массивами как для суммы, так и для года.
![выход проекта]()
Потому что мы получаем доступ к увеличенной сумме и финансируемому году для каждого элемента в массиве раундов финансирования. Чтобы исправить это, мы можем включить этап размотки перед нашим этапом проекта в этом конвейере агрегации и параметризовать это, сказав, что мы хотим 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
и a funded_year
. Таким образом, unwind
будет для каждого из документов, которые являются элементами массива funding_rounds
, создает выходной документ. Теперь в этом примере наши значения string
s. Но, независимо от типа значения для элементов в массиве, unwind
создаст выходной документ для каждого из этих значений, так что поле, о котором идет речь, будет иметь только этот элемент. В случае funding_rounds
этот элемент будет одним из этих документов в качестве значения для funding_rounds
для каждого документа, который будет передан на наш этап project
. Результатом, после этого, является то, что теперь мы получаем amount
и a year
. Один для каждого раунда финансирования для каждой компании в нашей коллекции. Это означает, что в нашем матче появилось много документов компании, и каждый из этих документов компании во многих документах. Один для каждого раунда финансирования в каждом документе компании. unwind
выполняет эту операцию, используя документы, переданные ей с этапа match
. И все эти документы для каждой компании затем передаются на этап project
.
![отключить вывод]()
Итак, все документы, в которых спонсор Greylock (как в примере запроса), будут разделены на несколько документов, равных количеству раундов финансирования для каждой компании, которая соответствует фильтру $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }
. И каждый из этих результирующих документов будет передан вместе с нашим project
. Теперь unwind
создает точную копию для каждого из документов, которые он получает в качестве входных данных. Все поля имеют один и тот же ключ и значение, за одним исключением, и что поле funding_rounds
вместо массива документов funding_rounds
вместо этого имеет значение, которое представляет собой отдельный документ, который представляет собой отдельный раунд финансирования. Таким образом, компания, имеющая раунды финансирования 4, приведет к созданию unwind
документов 4. Если каждое поле является точной копией, за исключением поля funding_rounds
, которое вместо массива для каждой из этих копий будет вместо этого отдельным элементом из массива funding_rounds
из документа компании, который в настоящее время unwind
обработка. Таким образом, unwind
приводит к тому, что на следующий этап выводится больше документов, чем в качестве входных данных. Это означает, что наш этап project
теперь получает поле funding_rounds
, которое снова не является массивом, а является вложенным документом, который имеет поле raised_amount
и funded_year
. Таким образом, project
получит несколько документов для каждой компании match
фильтра и может поэтому обрабатывать каждый из документов по отдельности и идентифицировать индивидуальную сумму и год для каждого раунда финансирования для каждой компании.
Ответ 4
Позвольте мне объяснить, каким образом переводится в RDBMS. Это утверждение:
db.article.aggregate(
{ $project : {
author : 1 ,
title : 1 ,
tags : 1
}},
{ $unwind : "$tags" }
);
для применения к документу/записи:
{
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 обычно производит вещи вертикально, поэтому разворачивайте теги split() по вертикали в теги таблицы.
Конечный результат $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", поэтому оно автоматически добавлено.
Ключ должен сделать его похожим на таблицу, чтобы выполнить агрегацию.