#mongodb #mongodb-query
#mongodb #mongodb-запрос
Вопрос:
Я пытаюсь объединить два поля массива, расположенные в коллекции встроенных документов, на этапе конвейера. Но я застрял на том, как ссылаться на два «внутренних» массива встроенного документа.
Коллекция
[{
name: "first",
docs: [
{ a1: ["a", "b"], a2: ["c"] },
{ a1: ["d", "e"], a2: ["f"] }
]
},
{
name: "second",
docs: [
{ a1: [1, 2], a2: [3] },
{ a1: [4, 5], a2: [6] }
]
}]
Ожидаемый результат
[{
name: "first",
docs: [
{ merged: ["a", "b", "c"] },
{ merged: ["d", "e", "f"] }
]
},
{
name: "second",
docs: [
{ merged: [1, 2, 3] },
{ merged: [4, 5, 6] }
]
}]
Подход
Общий подход, который я пробовал до сих пор, таков: (с 2 жестко закодированными массивами для целей тестирования)
db.getCollection("collection").aggregate([{
$set: {
"docs.merged": {
$concatArrays: [["hello"], ["world"]]
}
}
}])
Что дает ожидаемые результаты:
[{
name : "first",
docs : [
{
a1 : ["a", "b"],
a2 : ["c"],
merged : ["hello", "world"] // <- OK
},
{
a1 : ["d", "e"],
a2 : ["f"],
merged : ["hello", "world"] // <- OK
}
]
},{
name : "second",
docs : [
{
a1 : [1.0, 2.0],
a2 : [3.0],
merged : ["hello", "world"] // <- OK
},
{
a1 : [4.0, 5.0],
a2 : [6.0],
merged : ["hello", "world"] // <- OK
}
]
}]
Но у меня возникают трудности с пониманием того, как ссылаться на поля в текущем встроенном документе:
// Using the "$" reference causes following error:
// Invalid $set :: caused by :: FieldPath field names may not start with '$'.
{
$set: {
"docs.merged": { $concatArrays: ["$docs.$.a1", "$docs.$.a2"] }
}
}
// $$this is only available with a MAP operator
{
$set: {
"docs.merged": { $concatArrays: ["$$this.a1", "$$this.a2"] }
}
}
Соображения
Я не могу использовать update
запрос, поскольку исходные документы не должны быть изменены. Поэтому это должно быть достигнуто в aggregate
конвейере.
Я стараюсь избегать использования unwind
операций на этом этапе, так как это окажет значительное влияние на производительность. Фактические документы содержат довольно много (переменных) полей в своем корне; что делает group
этап после unwind
довольно сложным. (Пример был значительно упрощен для удобства чтения)
Я использую MongoDB v4.4
.
Ответ №1:
Я думаю, что это поможет, пожалуйста, дайте мне знать, если я чего-то не хватает:
db.collection.aggregate([{
$project: {
_id: 0,
"name": 1,
"docs": {
$function: {
body: function(docs) {
docs.forEach(function(doc) {
var merged = [];
Object.keys(doc).forEach(function(k) {
merged = merged.concat(doc[k]);
delete doc[k];
});
doc.merged = merged;
});
return docs;
},
args: [ "$docs" ],
lang: "js"
}
}
}
}])
Комментарии:
1. Я ожидал, что это будет решаемо с помощью встроенной функциональности. Но
$function
оператор решил это отлично с приемлемым воздействием на производительность. Спасибо!
Ответ №2:
Вы можете сделать что-то вроде следующего.
- Сначала
$unwind
нужно выровнять массив документов. - Поскольку a1 и a2 являются динамическими, мы превращаем это в массив. (Несколько динамических ключей могут быть сконструированы для вашего вывода, если мы используем это).
- Затем
$reduce
добавить данные в массив. - И перегруппируйте его, чтобы получить желаемый результат.
Сценарий агрегации
[
{
"$unwind": "$docs"
},
{
$project: {
name: 1,
data: {
$objectToArray: "$docs"
}
}
},
{
$project: {
name: 1,
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: [
"$$this.v",
"$$value"
]
}
}
}
}
},
{
$group: {
_id: "$_id",
name: {
$first: "$name"
},
docs: {
$push: {
merged: "$data"
}
}
}
}
]
Рабочая игровая площадка Mongo
Комментарии:
1. Я стараюсь избегать
unwind
, поскольку это заставило бы меня перегруппировать документы. Это сложнее, поскольку мои фактические документы содержат множество (переменных) свойств в его корне. Вопрос был больше о том, как ссылаться на поля во встроенном документе.2. Я тоже пробовал по-другому, но я все равно чувствую, что вам нужно использовать group. Но вы можете сгруппировать его
$$ROOT
и заменить данными. Но я с нетерпением жду ответа от кого-то, кто не использует unwind и group3. Я не уверен, но, возможно, $function может помочь в этом — поскольку вы используете MongoDB версии 4.4 .