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

Returner dokument med Max Sub Document

Først og fremmest, lad os vise dine data på en måde, som folk kan bruge dem og producere et ønsket resultat:

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

Bemærk, at "when" datoerne er faktisk forskellige, så der vil være en $max værdi, og de er ikke bare ens. Nu kan vi gennemgå sagerne

Case 1 - Hent "singular" $max værdi

Den grundlæggende sag her er at bruge $arrayElemAt og $indexOfArray operatører for at returnere den matchende $max værdi:

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

Returnerer:

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

Grundlæggende er "$max":"$change.when" returnerer den værdi, der er "maksimum" fra denne række af værdier. Du finder derefter det matchende "indeks" for det array af værdier via $indexOfArray som returnerer den første matchende indeks fundet. Denne "indeks"-position (fra faktisk kun en matrix af "når" værdier transponeret i samme rækkefølge ) bruges derefter med $arrayElemAt for at udtrække "hele objektet" fra "ændring" array på den angivne indeksposition.

Case 2 - Returner "multiple" $max poster

Stort set det samme med $max , bortset fra at vi denne gang $filter for at returnere de mange "mulige" værdier, der matcher den $max værdi:

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

Returnerer:

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

$max er selvfølgelig den samme, men denne gang bruges den entalsværdi, der returneres af den pågældende operator i en $eq sammenligning inden for $filter . Dette inspicerer hvert array-element og ser på strømmen "når" værdi ( "$$this.when" ). Hvor "lige" derefter returneres elementet.

Grundlæggende det samme som den første tilgang, men med den undtagelse at $filter tillader "flere" elementer, der skal returneres. Derfor alt med det samme $max værdi.

Case 3 - Forsortér matrixindholdet.

Nu kan du måske bemærke, at i de eksempeldata, jeg inkluderede (tilpasset fra din egen, men med en faktisk "max"-dato) er "max"-værdien faktisk den sidste værdi i arrayet. Dette kan helt naturligt ske som et resultat af, at $push (som standard) "tilføjer" til slutningen af ​​det eksisterende matrixindhold. Så "nyere" poster vil normalt være i slutningen af arrayet.

Dette er selvfølgelig standard adfærd, men der er gode grunde til, hvorfor du "må" ønsker at ændre på det. Kort sagt det bedste måde at få den "seneste" på array-indtastning er faktisk at returnere det første element fra arrayet.

Alt du faktisk skal gøre er at sikre dig den "nyeste" er faktisk tilføjet først i stedet for sidste . Der er to tilgange:

  1. Brug $position for at "afvente" matrixelementer: Dette er en simpel modifikation til $push ved hjælp af 0 position for altid at tilføje til fronten :

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

    Dette ville ændre dokumentet til:

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

Bemærk, at dette ville kræve, at du faktisk går og "vender" alle dine array-elementer på forhånd, så det "nyeste" allerede var foran, så rækkefølgen blev opretholdt. Heldigvis er dette noget dækket i den anden tilgang...

  1. Brug $sort at ændre dokumenterne i rækkefølge på hver $push : Og dette er den anden modifikator, som faktisk "omsorterer" atomisk på hver ny tilføjelse. Normal brug er stort set det samme med alle nye varer til $each som ovenfor, eller endda bare et "tomt" array for at anvende $sort kun til eksisterende data:

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

    Resultater i:

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

    Det kan tage et minut at forstå, hvorfor du ville $push for at $sort et array som dette, men den generelle hensigt er, når der kan foretages ændringer af et array, som "ændrer" en egenskab som en Dato værdi bliver sorteret efter, og du ville bruge en sådan erklæring til at afspejle disse ændringer. Eller faktisk bare tilføje nye elementer med $sort og lad det løse sig.

Så hvorfor "butik" arrayet bestilt sådan? Som tidligere nævnt vil du have den første element som det "seneste" , og derefter forespørgslen, der skal returneres, der blot bliver:

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

Returnerer:

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

$slice kan derefter kun bruges til at udtrække array-elementer ved kendte indekser. Teknisk kan du bare bruge -1 der for at returnere den sidste element i arrayet alligevel, men omarrangeringen, hvor den seneste er først, giver mulighed for andre ting som at bekræfte, at den sidste ændring blev foretaget af en bestemt bruger og/eller andre forhold som en datointerval begrænsning. dvs.:

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

Bemærker her, at noget som "change.-1.when" er en ulovlig erklæring, hvilket grundlæggende er grunden til, at vi omarrangerer arrayet, så du kan bruge det lovlige 0 for først i stedet for -1 til sidste .

Konklusion

Så der er flere forskellige ting, du kan gøre, enten ved at bruge aggregeringstilgangen til at filtrere matrixindholdet eller via standardforespørgselsformularer efter at have foretaget nogle ændringer af, hvordan dataene faktisk lagres. Hvilken man skal bruge afhænger af dine egne forhold, men det skal bemærkes, at enhver af standardforespørgselsformularerne vil køre betydeligt hurtigere end nogen form for manipulation via aggregeringsrammen eller nogen beregnede operatører.




  1. mongodb multi tenacy spil med @Document

  2. Groupby i MongoTemplate returnerer tomme felter

  3. Understøtter azur ting som mongodb og redis?

  4. Hent flere forespurgte elementer i et objektarray i MongoDB-samlingen