MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Rücksendebeleg mit max. Unterbeleg

Lassen Sie uns zunächst Ihre Daten so darstellen, dass andere sie verwenden und ein gewünschtes Ergebnis erzielen können:

{ 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' } ],
}

Beachten Sie, dass das "when" Daten sind tatsächlich unterschiedlich, daher wird es einen $max Wert und sie sind nicht einfach gleich. Jetzt können wir die Fälle durchgehen

Fall 1 – Holen Sie den "Singular" $max Wert

Der grundlegende Fall hier ist die Verwendung von $arrayElemAt und $indexOfArray Operatoren, um den passenden $max zurückzugeben Wert:

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

Rückgabe:

{
        "_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"
        }
}

Im Grunde der "$max":"$change.when" gibt den Wert zurück, der das "Maximum" innerhalb dieses Arrays von Werten ist. Sie finden dann den passenden "Index" dieses Arrays von Werten über $indexOfArray was den ersten zurückgibt passenden Index gefunden. Diese "Index"-Position (aus eigentlich nur einem Array von "when" Werte transponiert in der gleichen Reihenfolge ) wird dann mit $arrayElemAt verwendet um das "ganze Objekt" aus dem "change" zu extrahieren Array an der angegebenen Indexposition.

Fall 2 – Geben Sie das „Mehrfache“ $max zurück Einträge

So ziemlich das gleiche mit $max , außer dass wir diesmal $filter um das Vielfache "möglich" zurückzugeben Werte, die mit $max übereinstimmen Wert:

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

Rückgabe:

{
        "_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"
                }
        ]
}

Also der $max ist natürlich gleich, aber diesmal wird der von diesem Operator zurückgegebene Singularwert in einem $eq Vergleich innerhalb von $filter . Dies untersucht jedes Array-Element und betrachtet den aktuellen "wann" Wert ( "$$this.when" ). Wo "gleich" dann wird das Element zurückgegeben.

Im Grunde dasselbe wie der erste Ansatz, aber mit der Ausnahme, dass $filter erlaubt "mehrere" zurückzugebende Elemente. Also alles mit dem gleichen $max Wert.

Fall 3 - Vorsortieren des Array-Inhalts.

Nun werden Sie vielleicht bemerken, dass in den Beispieldaten, die ich eingefügt habe (angepasst an Ihre eigenen, aber mit einem tatsächlichen "max"-Datum), der "max"-Wert tatsächlich der letzte Wert ist Wert im Array. Dies kann ganz natürlich passieren, wenn $push (standardmäßig) "anhängt" an das Ende des vorhandenen Array-Inhalts. Also "neuer" Einträge werden in der Regel am Ende stehen des Arrays.

Dies ist natürlich die Standardeinstellung Verhalten, aber es gibt gute Gründe, warum Sie "können" möchte das ändern. Kurzum das Beste Möglichkeit, die "neuesten" abzurufen Der Array-Eintrag soll tatsächlich das erste Element zurückgeben aus dem Array.

Alles, was Sie tatsächlich tun müssen, ist sicherzustellen, dass es sich um die "neueste" handelt tatsächlich zuerst hinzugefügt wird statt zuletzt . Es gibt zwei Ansätze:

  1. Verwenden Sie $position um Array-Elemente "voranzustellen": Dies ist ein einfacher Modifikator für $push mit 0 Position um immer vorne zu ergänzen :

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

    Dies würde das Dokument ändern in:

    {
        "_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"
                }
        ]
    }
    

Beachten Sie, dass Sie dazu alle Ihre Array-Elemente vorher tatsächlich "umkehren" müssten, damit das "neueste" bereits vorne war, damit die Reihenfolge beibehalten wurde. Zum Glück wird dies im zweiten Ansatz etwas behandelt ...

  1. Verwenden Sie $sort um die Dokumente bei jedem $push : Und dies ist der andere Modifikator, der bei jeder Hinzufügung eines neuen Gegenstands tatsächlich atomar "umsortiert". Die normale Verwendung ist im Grunde die gleiche wie bei allen neuen Elementen für $each wie oben, oder sogar nur ein "leeres" Array, um den anzuwenden $sortieren nur auf vorhandene Daten:

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

    Ergebnisse in:

    {
            "_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"
                    }
            ]
    }
    

    Es kann eine Minute dauern, bis Sie verstanden haben, warum Sie $push um $sort ein Array wie dieses, aber die allgemeine Absicht besteht darin, Änderungen an einem Array vorzunehmen, die eine Eigenschaft wie ein Datum "ändern". Wert, nach dem sortiert wird, und Sie würden eine solche Anweisung verwenden, um diese Änderungen widerzuspiegeln. Oder fügen Sie einfach neue Elemente mit dem $sort und lass es klappen.

Warum also "speichern" das Array so bestellt? Wie bereits erwähnt, möchten Sie das erste Artikel als "neueste" , und dann wird die zurückzugebende Abfrage einfach zu:

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

Rückgabe:

{
        "_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"
                }
        ]
}

Also der $slice kann dann nur zum Extrahieren von Array-Elementen durch bekannte Indizes verwendet werden. Technisch gesehen können Sie einfach -1 verwenden dort, um den letzten zurückzugeben Element des Arrays, aber die Neuordnung, bei der das Neueste zuerst steht, ermöglicht andere Dinge wie die Bestätigung, dass die letzte Änderung von einem bestimmten Benutzer vorgenommen wurde, und/oder andere Bedingungen wie eine Einschränkung des Datumsbereichs. d.h.:

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

Beachten Sie hier, dass so etwas wie "change.-1.when" ist eine illegale Anweisung, weshalb wir das Array im Grunde neu anordnen, damit Sie die legale verwenden können 0 für zuerst statt -1 für zuletzt .

Schlussfolgerung

Sie können also verschiedene Dinge tun, indem Sie entweder den Aggregationsansatz zum Filtern des Array-Inhalts verwenden oder über Standardabfrageformulare, nachdem Sie einige Änderungen an der tatsächlichen Speicherung der Daten vorgenommen haben. Welche Sie verwenden, hängt von Ihren eigenen Umständen ab, aber es sollte beachtet werden, dass alle Standard-Abfrageformulare verwendet werden können wird deutlich schneller laufen als jede Manipulation über das Aggregations-Framework oder irgendwelche berechneten Operatoren.