sql >> Databáze >  >> NoSQL >> MongoDB

Podmínky shody a nejnovější datum z pole

Základním konceptem je, že potřebujete agregační rámec, abyste mohli použít podmínky pro "filtrování" prvků pole na podmínky. V závislosti na dostupné verzi existují různé techniky, které lze použít.

Ve všech případech je to výsledek:

{
    "_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 a vyšší

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

Toto je nejúčinnější způsob, který využívá $replaceRoot což nám umožňuje deklarovat proměnné k použití ve struktuře "nahrazeno" pomocí $let . Hlavní výhodou je, že to vyžaduje pouze „dvě“ fáze potrubí.

Aby se obsah pole shodoval, používáte $filter kde použijete $eq logická operace pro testování hodnoty "odesílatel" . Pokud se podmínka shoduje, vrátí se pouze odpovídající položky pole.

Pomocí stejného $filter aby byly brány v úvahu pouze odpovídající položky „odesílatele“, pak chceme použít $max přes "filtrovaný" seznam na hodnoty v "datetime" . $max ]5 hodnota je "nejnovější" datum podle podmínek.

Chceme tuto hodnotu, abychom mohli později porovnat vrácené výsledky z "filtrovaného" pole s tímto "maxDate". Což se děje uvnitř "in" blok $let kde se dvě „proměnné“ deklarované dříve pro filtrovaný obsah a „maxDate“ opět použijí na $filter aby bylo možné vrátit to, co by mělo být jedinou hodnotou, která splnila obě podmínky a měla také "nejpozději datum".

Protože chcete pouze „jeden“ výsledek, používáme $arrayElemAt použít hodnotu spíše než pole.

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

Toto je v podstatě stejný proces, jak je popsán, ale bez $ nahradit kořen fázi pipeline, musíme podat žádost ve dvou $project etapy. Důvodem je, že potřebujeme "vypočítanou hodnotu" z "maxDate", abychom mohli provést poslední $filter a není k dispozici ve složeném příkazu, takže místo toho rozdělíme potrubí. To má malý dopad na celkové náklady na operaci.

V MongoDB 2.6 až 3.0 zde můžeme použít většinu techniky kromě $arrayElemAt a buď přijměte výsledek „pole“ jediným záznamem, nebo vložte $unwind fázi zabývat se tím, co by nyní mělo být jediným záznamem.

Starší verze 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" }
  }}
])

I když to vypadá krátce, jde o zdaleka nejnákladnější operaci. Zde musíte použít $unwind za účelem aplikování podmínek na prvky pole. Jedná se o velmi nákladný proces, protože vytváří kopii každého dokumentu pro každou položku pole a je v podstatě nahrazen moderními operátory, které se tomu vyhýbají v případě „filtrování“.

Druhý $match stage zde zahodí všechny prvky (nyní „dokumenty“), které neodpovídají podmínce „odesílatel“. Poté použijeme $sort aby se "nejnovější" datum u každého dokumentu umístilo na začátek pomocí _id , tedy dvě klávesy "třídění".

Nakonec použijeme $group abyste mohli pouze odkazovat na původní dokument pomocí $first jako akumulátor pro získání prvku, který je „navrchu“.




  1. Najděte vnořený SubDocument MongoDB na n-úrovni

  2. Existuje způsob, jak použít objekty dotazů MongoDB k filtrování běžných polí JavaScriptu?

  3. Jak převést mongo ObjectId .toString bez zahrnutí 'ObjectId()' – pouze hodnoty?

  4. Musím ručně zavřít připojení mongoose?