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

Filtrer resultater efter den sidste matrixindtastningsfeltværdi

Kilometertal kan variere på dette, og det kan meget vel vise sig, at "p.t." den proces, du følger, i hvert fald virker som "mest egnet". Men vi kan nok gøre mere effektivt.

Hvad du kunne gøre nu

Forudsat at dine arrays allerede er "sorteret" ved hjælp af <-koden>$sort modifikator med $push , så kan du sikkert gøre dette:

db.somedb.find(
  { 
    "partn.is_partner": true,
    "$where": function() {
      return this.partn.slice(-1)[0].is_partner == true;
    }
  },
  { "partn": { "$slice": -1 } }
)

Så længe partn,is_partner er "indekseret", er dette stadig ret effektivt, da den indledende forespørgselsbetingelse kan opfyldes ved hjælp af et indeks. Den del, der ikke kan, er $where klausul her, der bruger JavaScript-evaluering.

Men hvad den anden del i $where gør, er simpelthen at "skære" det sidste element fra arrayet og teste dets værdi af is_partner ejendom for at se, om det er sandt. Kun hvis denne betingelse også er opfyldt, returneres dokumentet.

Der er også $slice projektionsoperatør. Dette gør det samme ved at returnere det sidste element fra arrayet. Falske matches er allerede filtreret, så dette viser kun det sidste element, hvor sandt.

Kombineret med indekset som nævnt, så burde dette være ret hurtigt, da dokumenterne allerede er valgt og JavaScript-tilstanden bare filtrerer resten. Bemærk, at uden et andet felt med en standardforespørgselsbetingelse, der matcher, en $where klausul kan ikke bruge et indeks. Så prøv altid at bruge "sparsomt" med andre forespørgselsbetingelser på plads.

Hvad du kan gøre i fremtiden

Next Up, selvom det ikke er tilgængeligt i skrivende stund, men helt sikkert i den nærmeste fremtid vil være $slice operatør for aggregeringsrammen. Dette er i øjeblikket i udviklingsgrenen, men her er et kig på, hvordan det fungerer:

db.somedb.aggregate([
  { "$match": { "partn.is_partner": true } },
  { "$redact": {
    "$cond": {
      "if": { 
        "$anyElementTrue": {
          "$map": {
            "input": { "$slice": ["$partn",-1] },
            "as": "el",
            "in": "$$el.is_partner"
          }
        }
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }},
  { "$project": {
      "partn": { "$slice": [ "$partn",-1 ] }
  }}
])

Ved at kombinere det $slice inden for en $redact trin her tillader dokumenterne at blive filtreret med en logisk betingelse, test af dokumentet. I dette tilfælde $slice producerer et enkelt element array, der sendes til $ kort for blot at udtrække den enkelte is_partner værdi (stadig som et array). Da dette i bedste fald stadig er et enkelt element-array, er den anden test >$anyElementTrue hvilket gør dette til et enestående boolesk resultat, velegnet til $cond .

$redact her bestemmer resultatet om det skal $$KEEP eller $$PRUNE dokumentet fra resultaterne. Senere bruger vi $slice igen i projektet for lige at returnere det sidste element i arrayet efter filtreringen.

Det viser sig at være stort set præcis, hvad JavaScript-versionen gør, med den undtagelse, at denne bruger alle indbyggede kodede operatorer, og derfor burde være en smule hurtigere end JavaScript-alternativet.

Begge formularer returnerer dit første dokument som forventet:

{
    "_id" : 0,
    "partn" : [
            {
                    "date" : ISODate("2015-07-28T00:59:14.963Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-07-28T01:00:32.771Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-07-28T01:15:29.916Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-08-05T13:48:07.035Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-08-05T13:50:56.482Z"),
                    "is_partner" : true
            }
    ]
}

Den store fangst her med begge er, at dit array allerede skal være sorteret, så den seneste dato er først. Uden det har du brug for aggregeringsrammen til $sort arrayet, ligesom du gør nu.

Ikke rigtig effektiv, så derfor bør du "forhåndssortere" dit array og vedligeholde rækkefølgen på hver opdatering.

Som et praktisk trick vil dette faktisk omarrangere alle array-elementer i alle samlingsdokumenter i en simpel erklæring:

db.somedb.update(
    {},
    { "$push": { 
        "partn": { "$each": [], "$sort": { "date": 1 } }
    }},
    { "multi": true }
)

Så selvom du ikke "skubber" et nyt element ind i et array og bare opdaterer en egenskab, kan du altid anvende den grundlæggende konstruktion for at holde arrayet i orden, som du vil have det.

Værd at overveje, da det burde gøre tingene meget hurtigere.




  1. mongodb-forespørgsel ved hjælp af _id-objekt i opslag lokalt felt

  2. Node.js - Mongoose - Opdater indlejret array med alle værdier i req.body

  3. Nodejs paginering

  4. Geospatialt indeks for indre struktur