MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

MongoDB проекция на вложени масиви

Актуализация за 2017 г.

Такъв добре поставен въпрос заслужава съвременен отговор. Видът на филтрирането на масива, който се иска, всъщност може да бъде направен в съвременните версии на MongoDB след 3.2 чрез просто $match и $project етапи на конвейера, подобно на първоначалната операция за обикновена заявка.

db.accounts.aggregate([
  { "$match": {
    "email" : "[email protected]",
    "groups": {
      "$elemMatch": { 
        "name": "group1",
        "contacts.localId": { "$in": [ "c1","c3", null ] }
      }
    }
  }},
  { "$addFields": {
    "groups": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$groups",
            "as": "g",
            "in": {
              "name": "$$g.name",
              "contacts": {
                "$filter": {
                  "input": "$$g.contacts",
                  "as": "c",
                  "cond": {
                    "$or": [
                      { "$eq": [ "$$c.localId", "c1" ] },
                      { "$eq": [ "$$c.localId", "c3" ] }
                    ]
                  } 
                }
              }
            }
          }
        },
        "as": "g",
        "cond": {
          "$and": [
            { "$eq": [ "$$g.name", "group1" ] },
            { "$gt": [ { "$size": "$$g.contacts" }, 0 ] }
          ]
        }
      }
    }
  }}
])

Това използва $filter и $map оператори да връщат само елементите от масивите, които отговарят на условията и е много по-добре за производителност, отколкото използването на $unwind . Тъй като етапите на конвейера ефективно отразяват структурата на "заявка" и "проект" от .find() работа, производителността тук е основно наравно с тази и операция.

Обърнете внимание, че когато намерението е действително да работите „всички документи“ за да съберете подробности от "множество" документи, а не от "един", тогава това обикновено изисква някакъв тип $unwind операция, за да се направи това, което позволява на елементите на масива да бъдат достъпни за "групиране".

Това е основно подходът:

db.accounts.aggregate([
    // Match the documents by query
    { "$match": {
        "email" : "[email protected]",
        "groups.name": "group1",
        "groups.contacts.localId": { "$in": [ "c1","c3", null ] },
    }},

    // De-normalize nested array
    { "$unwind": "$groups" },
    { "$unwind": "$groups.contacts" },

    // Filter the actual array elements as desired
    { "$match": {
        "groups.name": "group1",
        "groups.contacts.localId": { "$in": [ "c1","c3", null ] },
    }},

    // Group the intermediate result.
    { "$group": {
        "_id": { "email": "$email", "name": "$groups.name" },
        "contacts": { "$push": "$groups.contacts" }
    }},

    // Group the final result
    { "$group": {
        "_id": "$_id.email",
        "groups": { "$push": {
            "name": "$_id.name",
            "contacts": "$contacts" 
        }}
    }}
])

Това е „филтриране на масиви“ при повече от едно съвпадение, което основните възможности за прожектиране на .find() не мога да направя.

Имате "вложени" масиви, затова трябва да обработите $unwind два пъти. Заедно с другите операции.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Получавайте известия за променени документи в mongodb

  2. 3 лесни стъпки за подобряване на сигурността на вашата инсталация на MongoDB

  3. Java/MongoDB заявка по дата

  4. mongoose разлика между findOneAndUpdate и update

  5. Mongodb, обобщена заявка с $lookup