sql >> Database teknologi >  >> NoSQL >> MongoDB

Matchbetingelser og seneste dato fra array

Det grundlæggende koncept her er, at du har brug for aggregeringsrammen for at anvende betingelser for at "filtrere" array-elementerne til betingelserne. Afhængigt af den tilgængelige version er der forskellige teknikker, der kan anvendes.

I alle tilfælde er dette resultatet:

{
    "_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 og nyere

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

Dette er den mest effektive måde, som udnytter $replaceRoot som giver os mulighed for at erklære variabler til brug i den "erstattede" struktur ved hjælp af kode>$let . Den største fordel her er, at dette kun kræver "to" pipeline-trin.

For at matche array-indholdet bruger du $filter hvor du anvender $eq logisk operation for at teste værdien af ​​"sender" . Hvor betingelsen matcher, returneres kun de matchende array-indgange.

Bruger den samme $filter så kun de matchende "afsender"-indgange tages i betragtning, vil vi så anvende $max over den "filtrerede" liste til værdierne i "datetime" . $max ]5 værdi er den "seneste" dato ifølge betingelserne.

Vi ønsker denne værdi, så vi senere kan sammenligne de returnerede resultater fra det "filtrerede" array med denne "maxDate". Hvilket er, hvad der sker inde i "in" blok af $let hvor de to tidligere erklærede "variabler" for det filtrerede indhold og "maxDate" igen anvendes på $filter for at returnere, hvad der skulle være den eneste værdi, der opfyldte begge betingelser med "seneste dato".

Da du kun ønsker "et" resultat, bruger vi $arrayElemAt at bruge værdien i stedet for matrixen.

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

Dette er grundlæggende den samme proces som beskrevet, men uden $ replaceRoot pipeline fase, skal vi ansøge i to $project niveauer. Grunden til dette er, at vi har brug for den "beregnede værdi" fra "maxDate" for at gøre den sidste $filter , og det er ikke tilgængeligt at gøre i en sammensat erklæring, så i stedet opdeler vi pipelines. Dette har en lille indvirkning på de samlede omkostninger ved operationen.

I MongoDB 2.6 til 3.0 kan vi bruge det meste af teknikken her undtagen >$arrayElemAt og enten acceptere "array"-resultatet med en enkelt indtastning eller indsætte en $unwind etape for at beskæftige sig med det, der nu skulle være en enkelt post.

MongoDB tidligere versioner

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

Selvom det ser kort ud, er dette langt den dyreste operation. Her skal du bruge $unwind for at anvende betingelserne på array-elementerne. Dette er en meget bekostelig proces, da den producerer en kopi af hvert dokument for hver array-indgang og i det væsentlige erstattes af de moderne operatører, der undgår dette i tilfælde af "filtrering".

Den anden $match fase kasserer her alle elementer (nu "dokumenter"), som ikke matchede "afsender"-betingelsen. Så anvender vi en $sort for at sætte den "seneste" dato øverst for hvert dokument ved _id , deraf de to "sorteringstaster".

Til sidst anvender vi $group for blot at henvise til det originale dokument ved at bruge $first som akkumulator for at få det element, der er "på toppen".




  1. Pymongo :insert_many + unikt indeks

  2. Sådan adskilles redis-database for samme to app i node.js

  3. Mongodb finds returnerende dokumentbestilling

  4. Sorter et indlejret array i mongoose