Согласованные значения вложенных полей, если они существуют - PullRequest
1 голос
/ 19 апреля 2019

Я работаю над конкатенацией значений вложенных полей, но не могу добиться успеха с агрегациями

Я написал код для конкатенации 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 Ответ

1 голос
/ 19 апреля 2019

У вас есть несколько вариантов в зависимости от версии 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 перед второстепенным выпуском используйте $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 по крайней мере позволяет использовать $out, чтобы избежать пересылки всех документов «по проводам» перед повторной записью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...