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

Vrácení dokladu s maximálním dílčím dokladem

Nejprve ukažme vaše data tak, aby je lidé mohli použít a přinést požadovaný výsledek:

{ value: 1,
  _id: ObjectId('5cb9ea0c75c61525e0176f96'),
  name: 'Test',
  category: 'Development',
  subcategory: 'Programming Languages',
  status: 'Supported',
  description: 'Test',
  change:
   [ { version: 1,
       who: 'ATL User',
       when: new Date('2019-04-19T15:30:39.912Z'),
       what: 'Item Creation' },
     { version: 2,
       who: 'ATL Other User',
       when: new Date('2019-04-19T15:31:39.912Z'),
       what: 'Name Change' } ],
}

Všimněte si, že "kdy" data se ve skutečnosti liší, takže bude existovat $max hodnotu a nejsou jen stejné. Nyní můžeme projít případy

Případ 1 – Získejte „singulární“ $max hodnotu

Základním případem je použití $arrayElemAt a $indexOfArray operátory vrátí odpovídající $max hodnota:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$arrayElemAt": [
        "$change",
        { "$indexOfArray": [
          "$change.when",
          { "$max": "$change.when" }
        ]}
      ]
    }
  }}
])

Vrátí:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : {
                "version" : 2,
                "who" : "ATL Other User",
                "when" : ISODate("2019-04-19T15:31:39.912Z"),
                "what" : "Name Change"
        }
}

V podstatě "$max":"$change.when" vrátí hodnotu, která je "maximum" z tohoto pole hodnot. Poté najdete odpovídající "index" tohoto pole hodnot pomocí $indexOfArray který vrátí první nalezen odpovídající index. Tato pozice "indexu" (ve skutečnosti pouze z pole "kdy" hodnoty transponované ve stejném pořadí ) se pak použije s $arrayElemAt extrahovat "celý objekt" z "change" pole na zadané pozici indexu.

Případ 2 – Vraťte „multiple“ $max záznamy

V podstatě to samé s $max , kromě této doby jsme $filter vrátíte vícenásobné "možné" odpovídající hodnoty $max hodnota:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$filter": {
        "input": "$change",
        "cond": {
          "$eq": [ "$$this.when", { "$max": "$change.when" } ]
        }
      }       
    }
  }}
])

Vrátí:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
}

Takže $max je samozřejmě stejné, ale tentokrát je singulární hodnota vrácená tímto operátorem použita v $eq srovnání v rámci $filter . To zkontroluje každý prvek pole a podívá se na aktuální "kdy" hodnota ( "$$this.when" ). Kde "rovná se" pak je prvek vrácen.

V podstatě stejný jako první přístup, ale s tou výjimkou, že $filter umožňuje „více“ prvky, které mají být vráceny. Proto vše se stejným $max hodnotu.

Případ 3 – Předřazení obsahu pole.

Nyní si můžete všimnout, že v příkladech, které jsem zahrnul (upraveno z vašich vlastních, ale se skutečným „maximálním“ datem ), je hodnota „max“ ve skutečnosti poslední hodnotu v poli. K tomu může přirozeně dojít v důsledku toho, že $push ( ve výchozím nastavení ) "připojí" na konec obsahu existujícího pole. Tedy "novější" položky budou mít tendenci být na konci pole.

Toto je samozřejmě výchozí chování, ale existují dobré důvody, proč "můžete" chtít to změnit. Zkrátka nejlepší způsob, jak získat „nejnovější“ položka pole má ve skutečnosti vrátit první prvek z pole.

Vše, co ve skutečnosti musíte udělat, je zajistit „nejnovější“ je ve skutečnosti přidáno nejprve spíše než poslední . Existují dva přístupy:

  1. Použijte $position k "pre-pend" položek pole: Toto je jednoduchý modifikátor $push pomocí 0 pozice abyste vždy přidávali přední :

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [{
              "version": 3,
              "who": "ATL User",
              "when": new Date(),
              "what": "Another change"
            }],
            "$position": 0
          }
       }}
    )
    

    Tím by se dokument změnil na:

    {
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                },
                {
                        "version" : 1,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-19T15:30:39.912Z"),
                        "what" : "Item Creation"
                },
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
    }
    

Všimněte si, že by to vyžadovalo, abyste skutečně šli a "obrátili" všechny prvky pole předem, takže "nejnovější" už byl vpředu, takže pořadí bylo zachováno. Naštěstí je to trochu pokryto druhým přístupem...

  1. Použít $sort upravit dokumenty v pořadí na každém $push : A to je další modifikátor, který ve skutečnosti atomicky "přetřídí" každý nový prvek. Normální použití je v zásadě stejné u všech nových položek do $each jako výše, nebo dokonce jen "prázdné" pole za účelem použití $sort pouze na existující data:

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [],
            "$sort": { "when": -1 } 
          }
       }}
    )
    

    Výsledky v:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : [
                    {
                            "version" : 3,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-20T02:40:30.024Z"),
                            "what" : "Another change"
                    },
                    {
                            "version" : 2,
                            "who" : "ATL Other User",
                            "when" : ISODate("2019-04-19T15:31:39.912Z"),
                            "what" : "Name Change"
                    },
                    {
                            "version" : 1,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-19T15:30:39.912Z"),
                            "what" : "Item Creation"
                    }
            ]
    }
    

    Může chvíli trvat, než pochopíte, proč byste $push za účelem $sort pole, jako je toto, ale obecným záměrem je, když mohou být provedeny úpravy pole, které "změní" vlastnost, jako je Datum podle které se třídí hodnota a takový příkaz byste použili k vyjádření těchto změn. Nebo stačí přidat nové položky pomocí $sort a ať to vyjde.

Proč tedy "ukládat" takto uspořádané pole? Jak již bylo zmíněno, chcete první položku jako „nejnovější“ a pak dotaz, který se má vrátit, jednoduše zní:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true }
  },
  { "change": { "$slice": 1 } }
)

Vrátí:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                }
        ]
}

Takže $slice lze pak použít pouze k extrahování položek pole podle známých indexů. Technicky stačí použít -1 tam, abyste vrátili poslední položka pole tak jako tak, ale změna pořadí, kde je nejnovější jako první, umožňuje další věci, jako je potvrzení poslední úpravy, kterou provedl určitý uživatel, a/nebo další podmínky, jako je omezení časového rozsahu. tj.:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true },
    "change.0.who": "ATL User",
    "change.0.when": { "$gt": new Date("2018-04-01") }
  },
  { "change": { "$slice": 1 } }
)

Zde je třeba poznamenat, že něco jako "change.-1.when" je nezákonné prohlášení, což je v podstatě důvod, proč měníme pořadí pole, abyste mohli použít legální 0 pro první místo -1 na poslední .

Závěr

Existuje tedy několik různých věcí, které můžete udělat, buď pomocí agregačního přístupu k filtrování obsahu pole, nebo pomocí standardních formulářů dotazů po provedení určité úpravy toho, jak jsou data skutečně uložena. Který z nich použít, závisí na vašich vlastních okolnostech, ale je třeba poznamenat, že jakýkoli ze standardních formulářů dotazů poběží výrazně rychleji než jakákoli manipulace prostřednictvím agregačního rámce nebo jakýchkoli výpočetních operátorů.




  1. Příkaz MongoDB dropIndexes

  2. Aktualizujte pole s více podmínkami v mongodb

  3. Ekvivalent mongo shellu db.collection.runCommand() v Node.js

  4. Jak vytvořit partnerský vztah mezi MongoDB Atlas a Google App Engine Standard Environment Node App