#mongodb #aggregation-framework
#mongodb #агрегация-фреймворк
Вопрос:
Я работаю над объединением значений вложенных полей, но не могу добиться успеха с помощью агрегации
Я написал код для объединения 5 вложенных полей, которые находятся в моей базе данных
Поле адреса в коллекции с именем ‘level’ выглядит следующим образом
"address":{
"flatNo" : "101",
"street" : "amprapali",
"city" : "jaipur",
"zip" : "123456",
"state" : "rajasthan",
"country" : "INDIA"
}
Какой результат я хочу
"address":"#101, amprapali, jaipur, 123456, rajasthan, INDIA"
Мой код:
aggregation_pipeline = [
{
"$project":
{
"address": { "$concat": [ "#", "$address.flatNo", ", ", "$address.street", ", ", "$address.city", ", ", "$address.zip", ", ", "$address.state", ", ", "$address.country" ] }
}
},
{
"$out": "mod_collection"
}
]
cursor = db['level'].aggregate(aggregation_pipeline, allowDiskUse=True)
cursor.close()
Я получаю этот ожидаемый результат, но если какое-либо из 5 полей отсутствует в исходной базе данных, поле адреса заполняется null
Мое требование — объединить все существующие значения полей адресов, разделенных символом ‘,’
Как я могу опустить null
значения, если не все поля в разделе адрес существуют?
Ответ №1:
Здесь у вас есть несколько вариантов в зависимости от вашей версии MongoDB:
Для версий 3.4.4 и выше используйте $objectToArray
и выбирайте поля динамически:
db.collection.aggregate([
{ "$addFields": {
"address": {
"$let": {
"vars": {
"address": {
"$reduce": {
"input": { "$objectToArray": "$address" },
"initialValue": "",
"in": { "$concat": [ "$value", "$this.v", ", " ] }
}
}
},
"in": {
"$concat": [ "#",
{ "$substrCP": [
"$address",
0,
{ "$subtract": [{ "$strLenCP": "$address" }, 2] }
]}
]
}
}
}
}},
{ "$out": "newcollection" }
])
Для версии 3.4, предшествующей выпуску minor, используйте a $filter
для удаления null
значений
db.collection.aggregate([
{ "$addFields": {
"address": {
"$let": {
"vars": {
"address": {
"$reduce": {
"input": {
"$filter": {
"input": [
"$address.flatNo", "$address.street", "$address.city",
"$address.zip","$address.state","$address.country"
],
"cond": { "$ne": [ "$this", null ] }
}
},
"initialValue": "",
"in": { "$concat": [ "$value", "$this", ", " ] }
}
}
},
"in": {
"$concat": [ "#",
{ "$substrCP": [
"$address",
0,
{ "$subtract": [{ "$strLenCP": "$address" }, 2] }
]}
]
}
}
}
}},
{ "$out": "newcollection" }
])
До версии 3.4 у вас не было $reduce
или $strLenCP
, что позволяет «объединять» динамическим способом. Поэтому вы, вероятно, хотите сделать это в коде вместо этого:
var batch = [];
db.collection.find({}, { _id: 0, address: 1 }).forEach(doc => {
doc.address = "#" Object.keys(doc.address).map(k => doc.address[k]).join( ", ");
batch.push(doc);
if ( batch.length >= 1000 ) {
db.newcollection.insertMany(batch);
batch = [];
}
})
if ( batch.length > 0 ) {
db.newcollection.insertMany(batch);
batch = [];
}
Или действительно длинный с $ifNull
и $cond
:
db.collection.aggregate([
{ "$project": {
"address": {
"$concat": [
"#",
{ "$ifNull": [ "$address.flatNo", ""] },
{ "$cond": [{ "$ifNull": [ "$address.flatNo", false ] }, ", ", ""] },
{ "$ifNull": [ "$address.street", "" ] },
{ "$cond": [{ "$ifNull": [ "$address.street", false ] }, ", ", ""] },
{ "$ifNull": [ "$address.city", "" ] },
{ "$cond": [{ "$ifNull": [ "$address.city", false ] }, ", ", ""] },
{ "$ifNull": [ "$address.zip", "" ] },
{ "$cond": [{ "$ifNull": [ "$address.zip", false ] }, ", ", ""] },
{ "$ifNull": [ "$address.state", "" ] },
{ "$cond": [{ "$ifNull": [ "$address.state", false ] }, ", ", ""] },
{ "$ifNull": [ "$address.country", "" ] }
]
}
}}
])
Подход с кодом был бы более чистым, но если вы записываете в другую коллекцию, то $ifNull
, по $cond
крайней мере, with позволяет использовать $out
, чтобы избежать возврата всех документов «по проводам» перед их повторной записью.
Комментарии:
1. Я использую MongoDB 4.0, и ваш ответ для версий 3.4.4 и выше в значительной степени соответствовал моим требованиям. Спасибо!!
2. @RaghavendraSwaroop Вместо того, чтобы просто оставлять комментарий, чтобы поблагодарить вас, общепринятой практикой является принятие ваших ответов , когда они касаются заданного вами вопроса. Это указывает другим, что информация полезна и действительно решает проблему.
3. Мои извинения. Я новичок на этом сайте, и это был первый вопрос, который я опубликовал. Я принял ответ, как вы сказали, и в дальнейшем буду следовать соглашениям сайта. Спасибо.