sql >> Databasteknik >  >> NoSQL >> MongoDB

Filtrera resultat efter fältvärdet för sista matrisinmatning

Körsträckan kan variera på detta och det kan mycket väl visa sig att "för närvarande" den process du följer verkar vara "mest lämpad" åtminstone. Men vi kan nog göra mer effektivt.

Vad du kan göra nu

Förutsatt att dina arrayer redan är "sorterade" med hjälp av <-koden>$sort modifierare med $push , då kan du förmodligen göra så här:

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

Så länge som partn,is_partner är "indexerad" är detta fortfarande ganska effektivt eftersom det initiala frågevillkoret kan uppfyllas med ett index. Den del som inte kan är $where klausul här som använder JavaScript-utvärdering.

Men vad den andra delen i $where gör är att helt enkelt "skiva" det sista elementet från arrayen och testa dess värde för is_partner egendom för att se om det är sant. Endast om det villkoret också är uppfyllt returneras dokumentet.

Det finns också $slice projektionsoperatör. Detta gör samma sak för att returnera det sista elementet från arrayen. Falska matchningar är redan filtrerade, så detta visar bara det sista elementet där sant.

I kombination med indexet som nämnts så borde detta gå ganska snabbt med tanke på att dokumenten redan har valts och JavaScript-villkoret bara filtrerar resten. Observera att utan ett annat fält med ett standardfrågevillkor att matcha, en $where klausul kan inte använda ett index. Så försök alltid att använda "sparsamt" med andra frågevillkor på plats.

Vad du kan göra i framtiden

Next Up, även om det inte är tillgängligt i skrivande stund, men kommer säkerligen inom en snar framtid att vara $slice operatör för aggregeringsramverket. Detta är för närvarande i utvecklingsgrenen, men här är en titt på hur det fungerar:

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

Att kombinera den $slice inom en $redact steg här tillåter att dokumenten filtreras med ett logiskt villkor och testar dokumentet. I det här fallet $slice producerar en enstaka elementarray som skickas till $ karta för att bara extrahera singeln is_partner värde (fortfarande som en array). Eftersom detta i bästa fall fortfarande är en enstaka elementarray är det andra testet strong>$anyElementTrue vilket gör detta till ett unikt booleskt resultat, lämpligt för $cond .

$redact här bestämmer resultatet om det ska $$KEEP eller $$PRUNE dokumentet från resultaten. Senare använder vi $slice igen i projektet för att bara returnera det sista elementet i arrayen efter filtreringen.

Det verkar vara i stort sett exakt vad JavaScript-versionen gör, med undantaget att den här använder alla inbyggda kodade operatorer och därför borde vara lite snabbare än JavaScript-alternativet.

Båda formulären returnerar ditt första dokument som förväntat:

{
    "_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 stora haken här med båda är att din array redan måste vara sorterad så det senaste datumet är först. Utan det behöver du aggregeringsramverket för att $sortera arrayen, precis som du gör nu.

Inte riktigt effektivt, så det är därför du bör "försortera" din array och behålla ordningen på varje uppdatering.

Som ett praktiskt knep kommer detta faktiskt att ordna om alla arrayelement i alla samlingsdokument i ett enkelt uttalande:

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

Så även om du inte "skjuter in" ett nytt element i en array och bara uppdaterar en egenskap, kan du alltid tillämpa den grundläggande konstruktionen för att hålla arrayen ordnad som du vill ha den.

Värt att överväga eftersom det borde göra saker mycket snabbare.




  1. Använda en rumslig databas för att hitta polygoner som innehåller en punkt

  2. Konvertera MongoDB-fråga till Java

  3. MongoDB - Skapa en databas

  4. Mongoose Model Custom-felmeddelande för Enums