sql >> Database >  >> NoSQL >> MongoDB

Match voorwaarden en laatste datum van array

Het basisconcept hier is dat u het aggregatieraamwerk nodig hebt om voorwaarden toe te passen om de array-elementen op de voorwaarden te "filteren". Afhankelijk van de beschikbare versie zijn er verschillende technieken die kunnen worden toegepast.

In alle gevallen is dit het resultaat:

{
    "_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 en hoger

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
            ]
          }    
        }
      }
    }
  }}
])

Dit is de meest efficiënte manier om gebruik te maken van $replaceRoot waarmee we variabelen kunnen declareren voor gebruik in de "vervangen" structuur met behulp van $let . Het belangrijkste voordeel hier is dat dit slechts "twee" pijplijntrappen vereist.

Om de array-inhoud te matchen, gebruikt u $filter waar u de $eq toepast logische bewerking om de waarde van "sender" . te testen . Waar de voorwaarde overeenkomt, worden alleen de overeenkomende array-items geretourneerd.

Dezelfde $filter gebruiken zodat alleen de overeenkomende "afzender"-vermeldingen worden beschouwd, willen we vervolgens $max over de "gefilterde" lijst naar de waarden in "datetime" . De $max ]5 waarde is de "laatste" datum volgens de voorwaarden.

We willen deze waarde zodat we later de geretourneerde resultaten van de "gefilterde" array kunnen vergelijken met deze "maxDate". Dat is wat er gebeurt in de "in" blok van $let waarbij de twee eerder gedeclareerde "variabelen" voor de gefilterde inhoud en de "maxDate" opnieuw worden toegepast op $filter om terug te geven wat de enige waarde zou moeten zijn die aan beide voorwaarden voldeed met ook de "laatste datum".

Omdat je maar "één" resultaat wilt, gebruiken we $arrayElemAt om de waarde te gebruiken in plaats van de array.

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
      ]
    }
  }}
])

Dit is in principe hetzelfde proces als beschreven, maar zonder de $replaceRoot pijplijnfase, moeten we toepassen in twee $project stadia. De reden hiervoor is dat we de "berekende waarde" van "maxDate" nodig hebben om dat laatste te doen $filter , en het is niet beschikbaar om te doen in een samengestelde instructie, dus in plaats daarvan splitsen we de pijplijnen. Dit heeft een kleine impact op de totale kosten van de operatie.

In MongoDB 2.6 tot 3.0 kunnen we de meeste technieken hier gebruiken, behalve $arrayElemAt en accepteer het "array"-resultaat met een enkele invoer of plaats een $unwind om te gaan met wat nu een enkele invoer zou moeten zijn.

MongoDB eerdere versies

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" }
  }}
])

Hoewel het er kort uitziet, is dit verreweg de duurste operatie. Hier moet je $unwind gebruiken om de voorwaarden toe te passen op de array-elementen. Dit is een zeer kostbaar proces omdat het een kopie van elk document produceert voor elke array-invoer, en in wezen wordt vervangen door de moderne operators die dit vermijden in het geval van "filteren".

De tweede $match stage here verwijdert alle elementen (nu "documenten") die niet overeenkwamen met de voorwaarde "afzender". Dan passen we een $sort toe om de "laatste" datum bovenaan te plaatsen voor elk document door de _id , vandaar de twee "sorteer"-toetsen.

Ten slotte passen we $group toe om gewoon naar het originele document te verwijzen, met behulp van $first als de accumulator om het element te krijgen dat "bovenaan" is.




  1. In Santander's Near Real-Time Data Ingest Architecture (deel 2)

  2. Databasefout MongoDB:database verdwenen

  3. Voorkom runtime-paniek in bson.ObjectIdHex

  4. mongodb &max verbindingen