sql >> Database >  >> NoSQL >> MongoDB

Resultaten filteren op de laatste array-invoerveldwaarde

Kilometerstand kan hierop variëren en het kan best zijn dat "momenteel" het proces dat u volgt op zijn minst "het meest geschikt" blijkt te zijn. Maar we kunnen waarschijnlijk efficiënter.

Wat u nu zou kunnen doen

Op voorwaarde dat uw arrays al zijn "gesorteerd" met behulp van de $sort modifier met $push , dan kunt u waarschijnlijk dit doen:

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

Dus zolang partn,is_partner is "geïndexeerd", dit is nog steeds behoorlijk efficiënt omdat aan die initiële vraagvoorwaarde kan worden voldaan met behulp van een index. Het deel dat dat niet kan is de $where clausule hier die JavaScript-evaluatie gebruikt.

Maar wat dat tweede deel in de $where doet, is simpelweg het laatste element uit de array "snijden" en de waarde van de is_partner testen eigendom om te zien of het waar is. Alleen als ook aan die voorwaarde is voldaan, wordt het document geretourneerd.

Er is ook de $slice projectie-operator. Dit doet hetzelfde bij het retourneren van het laatste element uit de array. Valse overeenkomsten zijn al gefilterd, dus dit toont alleen het laatste element waar waar is.

Gecombineerd met de index zoals vermeld, zou dit vrij snel moeten zijn, aangezien de documenten al zijn geselecteerd en de JavaScript-voorwaarde alleen de rest filtert. Merk op dat zonder een ander veld met een standaard zoekvoorwaarde die overeenkomt, een $where clausule kan geen index gebruiken. Probeer dus altijd "spaarzaam" te gebruiken met andere zoekvoorwaarden.

Wat u in de toekomst kunt doen

Next Up, hoewel niet beschikbaar op het moment van schrijven, maar zeker in de nabije toekomst zal de $slice zijn operator voor het aggregatieraamwerk. Dit is momenteel in de ontwikkelbranche, maar hier is een kijkje in hoe het werkt:

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

Die $slice . combineren binnen een $redact In de fase hier kunnen de documenten worden gefilterd met een logische voorwaarde, waardoor het document wordt getest. In dit geval de $slice produceert een array met één element die wordt verzonden naar $map om alleen de enkele is_partner . te extraheren waarde ( nog steeds als een array ). Aangezien dit op zijn best nog steeds een array met één element is, is de andere test $anyElementTrue waardoor dit een enkelvoudig booleaans resultaat is, geschikt voor $cond .

De $redact hier beslist over dat resultaat of $$KEEP of $$PRUNE het document uit de resultaten. Later gebruiken we $slice opnieuw in het project om alleen het laatste element van de array na het filteren terug te geven.

Dat blijkt vrijwel precies te zijn wat de JavaScript-versie doet, behalve dat deze alle native gecodeerde operators gebruikt, en daarom een ​​beetje sneller zou moeten zijn dan de JavaScript-alternatieven.

Beide formulieren retourneren uw eerste document zoals verwacht:

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

De grote vangst hier met beide is dat je array al moet zijn gesorteerd, dus de laatste datum is eerst. Zonder dat heb je het aggregatieraamwerk nodig om $sort de array, net zoals je nu doet.

Niet echt efficiënt, daarom moet u uw array "voorsorteren" en de volgorde bij elke update behouden.

Als een handige truc, zal dit in feite alle array-elementen in alle collectiedocumenten opnieuw ordenen in één simpele instructie:

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

Dus zelfs als u geen nieuw element in een array "pusht" en alleen een eigenschap bijwerkt, kunt u altijd die basisconstructie toepassen om de array geordend te houden zoals u dat wilt.

Het overwegen waard, omdat het de zaken veel sneller zou moeten maken.




  1. Mongodb recursieve query werkt niet zoals verwacht met $graphLookup

  2. Mongo veld A groter dan veld B

  3. Projecteer verschillende ingebedde structuren met dezelfde naam

  4. Mongoose vind array met $in