У меня есть коллекция ниже, и я использую агрегацию MongoDB для получения результата (где я объединяю все позиции заказа для заказа) и пытаюсь объединиться с другой коллекцией (order
).
[
{
"order_line_item_id": 1,
"order_id": 100,
"products": [
{
"name": "Shoe",
"hasDefect": "YES"
},
{
"name": "Pant",
"hasDefect": "NO"
},
{
"name": "Shirt",
"hasDefect": "NOT_SURE"
}
]
},
{
"order_line_item_id": 2,
"order_id": 100,
"products": [
{
"name": "Shoe",
"hasDefect": "YES"
},
{
"name": "Pant",
"hasDefect": "YES"
},
{
"name": "Shirt",
"hasDefect": "YES"
}
]
},
{
"order_line_item_id": 3,
"order_id": 101,
"products": [
{
"name": "Shoe",
"hasDefect": "YES"
},
{
"name": "Pant",
"hasDefect": "NO"
},
{
"name": "Shirt",
"hasDefect": "NOT_SURE"
}
]
}
]
Я использую приведенную ниже агрегацию для достижения результата
db.collection.aggregate([
{
$match: {
order_id: {
$eq: 100
}
}
},
{
$unwind: "$products"
},
{
$match: {
"products.name": {
$eq: "Pant"
}
}
},
{
$group: {
_id: {
order_id: "$order_id",
productName: "$products.name"
},
hasDefects: {
$push: "$products.hasDefect"
},
products: {
$push: "$products"
}
}
},
{
$set: {
hasDefect: {
$switch: {
branches: [
{
case: {
$allElementsTrue: {
$map: {
input: "$hasDefects",
as: "d",
in: {
$eq: [
"$$d",
"YES"
]
}
}
}
},
then: "YES"
},
{
case: {
$in: [
"NO",
"$hasDefects"
]
},
then: "NO"
},
{
case: {
$in: [
"NOT_SURE",
"$hasDefects"
]
},
then: "NOT_SURE"
}
],
default: "<DEFAULT>"
}
}
}
},
{
$group: {
_id: "$_id.order_id",
order_id: {
"$first": "$_id.order_id"
},
products: {
$push: {
name: "$_id.productName",
hasDefect: "$hasDefect",
lastModified: {
$dateToString: {
date: new Date(),
timezone: "America/New_York"
}
}
}
}
}
},
{
$unset: [
"_id"
]
},
{
$merge: {
into: "order_res",
on: [ "order_id" ],
whenMatched: "replace",
whenNotMatched: "insert"
}
}
])
Результатом может быть новый документ или обновление одного из продуктов в документе order
(как показано ниже).
[
{
"order_id": 100,
"products": [
{
"name": "Shoe",
"hasDefect": "YES"
},
]
}
]
Я использую этап агрегирования слиянием для вставки/обновления в другую коллекцию mongoDB.
{ $merge: { into: "order", on: "order_id", whenMatched: "replace", whenNotMatched: "insert" }}
Проблема в том, что это операция обновления, и в заказе есть предыдущие продукты, которые заменяют все существующие продукты на новые. Например, если в заказе есть указанный ниже документ, он должен обновить только товар «Обувь».
[
{
"order_id": 100,
"products": [
{
"name": "Shoe",
"hasDefect": "NO"
},
{
"name": "Pant",
"hasDefect": "NO"
},
{
"name": "Shirt",
"hasDefect": "NOT_SURE"
}
]
}
]
@cmgchess Я также обновил пост. Да, массив обновлений может содержать несколько элементов и иметь один и тот же order_id.
Вы можете использовать $mergeObjects вместо $merge, чтобы создать новую коллекцию с нужными документами. Пожалуйста, дайте мне знать, если приведенный ниже запрос работает для вас:
[
{
$match: {
order_id: 100
}
},
{
$unwind: "$products"
},
{
$match: {
"products.name": "Pant"
}
},
{
$group: {
_id: {
order_id: "$order_id",
productName: "$products.name"
},
hasDefects: {
$push: "$products.hasDefect"
}
}
},
{
$set: {
hasDefect: {
$switch: {
branches: [
{
case: {
$allElementsTrue: {
$map: {
input: "$hasDefects",
as: "d",
in: {
$eq: ["$$d", "YES"]
}
}
}
},
then: "YES"
},
{
case: {
$in: ["NO", "$hasDefects"]
},
then: "NO"
},
{
case: {
$in: ["NOT_SURE", "$hasDefects"]
},
then: "NOT_SURE"
}
],
default: "<DEFAULT>"
}
}
}
},
{
$project: {
order_id: "$_id.order_id",
productName: "$_id.productName",
hasDefect: "$hasDefect",
lastModified: {
$dateToString: {
date: new Date(),
timezone: "America/New_York"
}
}
}
},
{
$merge: {
into: "order",
whenMatched: [
{
$addFields: {
products: {
$map: {
input: "$$ROOT.products",
as: "product",
in: {
$cond: {
if: {
$eq: ["$$product.name", "$productName"]
},
then: {
$mergeObjects: [
"$$product",
{ hasDefect: "$hasDefect" }
]
},
else: "$$product"
}
}
}
}
}
}
],
whenNotMatched: "insert"
}
}
]
С наилучшими пожеланиями Асавари
В коллекции order
также будет множество товаров. Вы заменили второй $group
на $project
. Что у меня не работает.
Ссылаясь на вопрос из моего предыдущего ответа,
Вы можете предоставить конвейер агрегации для WhenMatched.
Важным ключом является ссылка на поле (результат), возвращаемое из конвейера агрегации, вам необходимо использовать переменную $$new
.
db.collection.aggregate([
{
$unwind: "$products"
},
{
$group: {
_id: {
order_id: "$order_id",
productName: "$products.name"
},
hasDefects: {
$push: "$products.hasDefect"
},
products: {
$push: "$products"
}
}
},
{
$set: {
hasDefect: {
$switch: {
branches: [
{
case: {
$allElementsTrue: {
$map: {
input: "$hasDefects",
as: "d",
in: {
$eq: [
"$$d",
"YES"
]
}
}
}
},
then: "YES"
},
{
case: {
$in: [
"NO",
"$hasDefects"
]
},
then: "NO"
},
{
case: {
$in: [
"NOT_SURE",
"$hasDefects"
]
},
then: "NOT_SURE"
}
],
default: "<DEFAULT>"
}
}
}
},
{
$group: {
_id: "$_id.order_id",
products: {
$push: {
name: "$_id.productName",
hasDefect: "$hasDefect"
}
}
}
},
{
$merge: {
into: "order",
whenMatched: [
{
$addFields: {
products: {
$let: {
vars: {
newProducts: "$$new.products"
},
in: {
$concatArrays: [
{
$map: {
input: "$products",
as: "oldProduct",
in: {
$cond: {
if: {
$in: [
"$$oldProduct.name",
"$$newProducts.name"
]
},
then: {
$first: {
$filter: {
input: "$$newProducts",
cond: {
$eq: [
"$$this.name",
"$$oldProduct.name"
]
}
}
}
},
else: "$$oldProduct"
}
}
}
},
{
$filter: {
input: "$$newProducts",
cond: {
$not: {
$in: [
"$$this.name",
"$products.name"
]
}
}
}
}
]
}
}
}
}
}
],
whenNotMatched: "insert"
}
}
])
Я попробовал, но он не обновляет существующий продукт. он просто добавляет к массиву продуктов. Например, {"order_id": 100,"products": [{"name": "Shoe","hasDefect": "YES"}]}
добавляется как еще один товар «Обувь» в коллекцию, хотя в коллекции есть товар «Обувь», и его следует только что обновить.
Можете ли вы воспроизвести свой сценарий с данными Mongo Playground? Демо, которое я добавил в ответ, не может воспроизвести ваш сценарий.
Моя вина, это действительно работает. У меня было неправильное имя одной из переменных, поэтому она не могла найти и всегда добавлялась. У меня есть один вопрос о том, как это работает: $in: [ "$$oldProduct.name", "$$newProducts.name"]
Мне интересно, как "$$newProducts.name"
преобразуется в массив?
Привет, MongoDB имеет возможность получить доступ к вложенному полю и вернуть его, поскольку newProducts
— это массив, следовательно, с помощью $$newProducts.name
он вернет все значения name
в виде массива. Цель использования $in
— проверить наличие $$oldProduct.name
в массиве $$newProducts.name
.
второй блок кода — это массив обновлений? может ли он иметь несколько элементов. если да, то order_id одинаков для всех. произойдет ли обновление обоих документов с order_line_item_id 1 и 2