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

Условия за съответствие и последна дата от масива

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

Във всички случаи това е резултатът:

{
    "_id" : ObjectId("593921425ccc8150f35e7664"),
    "user1" : 1,
    "user2" : 4,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-09T10:04:50Z"),
            "body" : "hiii 1"
    }
}
{
    "_id" : ObjectId("593921425ccc8150f35e7663"),
    "user1" : 1,
    "user2" : 3,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-10T10:04:50Z"),
            "body" : "hiii 2"
    }
}
{
    "_id" : ObjectId("593921425ccc8150f35e7662"),
    "user1" : 1,
    "user2" : 2,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-08T10:04:50Z"),
            "body" : "hiii 0"
    }
}

MongoDB 3.4 и по-нова версия

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$replaceRoot": {
    "newRoot": {
      "$let": {
        "vars": {
          "messages": {
            "$filter": {
              "input": "$messages",
              "as": "m",
              "cond": { "$eq": [ "$$m.sender", 1 ] }
            }
          },
          "maxDate": {
            "$max": {
              "$map": {
                "input": {
                  "$filter": {
                    "input": "$messages",
                    "as": "m",
                    "cond": { "$eq": [ "$$m.sender", 1 ] }
                  }
                },
                "as": "m",
                "in": "$$m.datetime"
              }
            }
          }
        },
        "in": {
          "_id": "$_id",
          "user1": "$user1",
          "user2": "$user2",
          "messages": {
            "$arrayElemAt": [
              { "$filter": {
                "input": "$$messages",
                "as": "m",
                "cond": { "$eq": [ "$$m.datetime", "$$maxDate" ] }
              }},
              0
            ]
          }    
        }
      }
    }
  }}
])

Това е най-ефективният начин, който се възползва от $replaceRoot което ни позволява да декларираме променливи, които да използваме в „заменената“ структура, използвайки $let . Основното предимство тук е, че това изисква само "два" етапа на конвейера.

За да съответствате на съдържанието на масива, вие използвате $filter където прилагате $eq логическа операция за тестване на стойността на "sender" . Когато условието съвпада, тогава се връщат само съответстващите записи в масива.

Използвайки същия $filter така че да се вземат предвид само съответстващите записи на „подател“, тогава искаме да приложим $max над „филтрирания“ списък до стойностите в „datetime“ . $max ]5 стойността е "най-новата" дата според условията.

Искаме тази стойност, за да можем по-късно да сравним върнатите резултати от „филтрирания“ масив с този „maxDate“. Което се случва вътре в "in" блок от $let където двете „променливи“, декларирани по-рано за филтрираното съдържание и „maxDate“, отново се прилагат към $filter за да върне това, което трябва да бъде единствената стойност, която отговаря на двете условия, като има и "най-нова дата".

Тъй като искате само „един“ резултат, ние използваме $arrayElemAt за да използвате стойността вместо масива.

MongoDB 3.2

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$project": {
    "user1": 1,
    "user2": 1,
    "messages": {
      "$filter": {
        "input": "$messages",
        "as": "m",
        "cond": { "$eq": [ "$$m.sender", 1 ] }
      }
    },
    "maxDate": {
      "$max": {
        "$map": {
          "input": {
            "$filter": {
              "input": "$messages",
              "as": "m",
              "cond": { "$eq": [ "$$m.sender", 1 ] }
            }
          },
          "as": "m",
          "in": "$$m.datetime"
        }
      }
    }         
  }},
  { "$project": {
    "user1": 1,
    "user2": 1,
    "messages": {
      "$arrayElemAt":[
       { "$filter": {
         "input": "$messages",
          "as": "m",
          "cond": { "$eq": [ "$$m.datetime", "$maxDate" ] }
       }},
       0
      ]
    }
  }}
])

Това в общи линии е същият процес като описания, но без $ replaceRoot етап на тръбопровод, трябва да приложим в два $project етапи. Причината за това е, че се нуждаем от „изчислената стойност“ от „maxDate“, за да направим това окончателно $filter , и не е налично да се направи в съставен оператор, така че вместо това разделяме тръбопроводите. Това има малко влияние върху общата цена на операцията.

В MongoDB 2.6 до 3.0 можем да използваме повечето от техниката тук, с изключение на $arrayElemAt и или приемете резултата от "масив" с един запис, или поставете $unwind етап, за да се справи с това, което сега трябва да бъде единичен запис.

По-ранни версии на MongoDB

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$unwind": "$messages" },
  { "$match": { "messages.sender": 1 } },
  { "$sort": { "_id": 1, "messages.datetime": -1 } },
  { "$group": {
    "_id": "$_id",
    "user1": { "$first": "$user1" },
    "user2": { "$first": "$user2" },
    "messages": { "$first": "$messages" }
  }}
])

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

Вторият $match етапът тук отхвърля всички елементи (сега "документи"), които не отговарят на условието "подател". След това прилагаме $sort за да поставите най-новата дата отгоре за всеки документ чрез _id , оттук и двата ключа за сортиране.

Накрая прилагаме $group за да се обърнете само към оригиналния документ, като използвате $first като акумулатор, за да получите елемента, който е "отгоре".




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Yii2 Активен запис как се създава връзка между mongo моделите

  2. Въпрос за агрегиране на MongoDB Map/Reduce Array

  3. MongoDB Aggregation:Как да получите общия брой записи?

  4. Сортиране на резултатите от заявката по реда на елементите в предоставения масив с условия в Mongoose

  5. Търсенето в MongoDB по тип DateTime не работи