Găsi MongoDB înregistrările unde matrice domeniu nu este gol

Toate înregistrările au un câmp numit "imagini". Acest domeniu este o matrice de siruri de caractere.

Vreau acum cele mai noi de 10 de înregistrări în cazul în care această matrice NU ESTE gol.

Am'am căutat pe google, dar destul de ciudat m-am't a găsit de mult pe asta. Am'am citit în $unde opțiune, dar mă întrebam cum de lent, care este nativ funcții, și dacă există o soluție mai bună.

Și chiar și atunci, că nu funcționează:

ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind()

Se întoarce nimic. Lăsând asta.imagini fără lungimea pic de lucru, dar apoi se întoarce, de asemenea, înregistrări goale, desigur.

Soluția

Dacă aveți, de asemenea, documente care don't au o cheie, puteți folosi:

ME.find({ pictures: { $exists: true, $not: {$size: 0} } })

MongoDB don't de a folosi indicii dacă $este implicat dimensiune, deci, aici este o soluție mai bună:

ME.find({ pictures: { $exists: true, $ne: [] } })

Deoarece MongoDB 2.6 eliberare, puteți compara cu operatorul $gt dar ar putea duce la rezultate neașteptate (puteți găsi un explicarea detaliata în acest răspuns):

ME.find({ pictures: { $gt: [] } })
Comentarii (8)

După ce mai multe, mai ales în mongodb documente, și încurcat bits împreună, asta a fost raspunsul:

ME.find({pictures: {$exists: true, $not: {$size: 0}}})
Comentarii (7)

Acest lucru ar putea lucra, de asemenea, pentru tine:

ME.find({'pictures.0': {$exists: true}});
Comentarii (6)

Ai grijă de două lucruri atunci când interogarea precizie și performanță. Cu asta în minte, am testat câteva abordări diferite în MongoDB v3.0.14.

TL;DR db.doc.find({ nums: { $gt: -Infinity }}) este cel mai rapid și cele mai fiabile (cel puțin în MongoDB versiune am testat).

EDIT: nu mai funcționează în MongoDB v3.6! A se vedea comentariile de sub acest post pentru o potențială soluție.

Setup

Am introdus 1k docs w/o o listă de teren, 1k docs cu o listă goală, și 5 documente cu un non-listă goală.

for (var i = 0; i < 1000; i++) { db.doc.insert({}); }
for (var i = 0; i < 1000; i++) { db.doc.insert({ nums: [] }); }
for (var i = 0; i < 5; i++) { db.doc.insert({ nums: [1, 2, 3] }); }
db.doc.createIndex({ nums: 1 });

Recunosc e't suficient de o scară pentru a avea o performanță la fel de serios ca eu sunt in testele de mai jos, dar's suficient de a prezenta corectitudinea diferite întrebări și comportamentul ales planurile de interogare.

Teste

db.doc.find({&#39;nums&#39;: {&#39;$există&#39;: true}}) returnează rezultate greșite (pentru ceea ce ne're încercarea de a realiza).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': {'$exists': true}}).count()
1005

--

db.doc.find({&#39;nums.0&#39;: {&#39;$există&#39;: true}}) returnează rezultate corecte, dar's, de asemenea, lent, folosind o colecție completă de scanare (observa `COLLSCAN stadiul în explicația).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).explain()
{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "test.doc",
    "indexFilterSet": false,
    "parsedQuery": {
      "nums.0": {
        "$exists": true
      }
    },
    "winningPlan": {
      "stage": "COLLSCAN",
      "filter": {
        "nums.0": {
          "$exists": true
        }
      },
      "direction": "forward"
    },
    "rejectedPlans": [ ]
  },
  "serverInfo": {
    "host": "MacBook-Pro",
    "port": 27017,
    "version": "3.0.14",
    "gitVersion": "08352afcca24bfc145240a0fac9d28b978ab77f3"
  },
  "ok": 1
}

--

db.doc.find({&#39;num&#39;: { $există: adevărat, $gt: { &#39; dimensiune$&#39;: 0 }}}) returnează rezultate greșite. Ca's din cauza unui invalid index scan avansează niciun document. Acesta va fi probabil corecte, dar lent, fără index.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 0,
  "executionTimeMillisEstimate": 0,
  "works": 2,
  "advanced": 0,
  "needTime": 0,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "nums": {
            "$gt": {
              "$size": 0
            }
          }
        },
        {
          "nums": {
            "$exists": true
          }
        }
      ]
    },
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 1,
    "advanced": 0,
    "needTime": 0,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 0,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 0,
      "executionTimeMillisEstimate": 0,
      "works": 1,
      "advanced": 0,
      "needTime": 0,
      "needFetch": 0,
      "saveState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "({ $size: 0.0 }, [])"
        ]
      },
      "keysExamined": 0,
      "dupsTested": 0,
      "dupsDropped": 0,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}

--

db.doc.find({&#39;num&#39;: { $există: adevărat, $nu: { &#39; dimensiune$&#39;: 0 }}}) returnează rezultate corecte, dar performanta este rău. Din punct de vedere tehnic nu un index scan, dar apoi încă avansuri toate documentele și apoi trebuie să se filtrează printr-ele).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 2016,
  "advanced": 5,
  "needTime": 2010,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "nums": {
            "$exists": true
          }
        },
        {
          "$not": {
            "nums": {
              "$size": 0
            }
          }
        }
      ]
    },
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 2016,
    "advanced": 5,
    "needTime": 2010,
    "needFetch": 0,
    "saveState": 15,
    "restoreState": 15,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 2005,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 2005,
      "executionTimeMillisEstimate": 0,
      "works": 2015,
      "advanced": 2005,
      "needTime": 10,
      "needFetch": 0,
      "saveState": 15,
      "restoreState": 15,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "[MinKey, MaxKey]"
        ]
      },
      "keysExamined": 2015,
      "dupsTested": 2015,
      "dupsDropped": 10,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}

--

db.doc.find({&#39;num&#39;: { $există: adevărat, $ne: [] }}) returnează rezultate corecte și este puțin mai repede, dar performanța nu este încă ideală. Folosește IXSCAN care doar avansuri docs cu o listă existentă domeniu, dar apoi trebuie să filtreze gol listele unul câte unul.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 1018,
  "advanced": 5,
  "needTime": 1011,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "$not": {
            "nums": {
              "$eq": [ ]
            }
          }
        },
        {
          "nums": {
            "$exists": true
          }
        }
      ]
    },
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 1017,
    "advanced": 5,
    "needTime": 1011,
    "needFetch": 0,
    "saveState": 15,
    "restoreState": 15,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 1005,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 1005,
      "executionTimeMillisEstimate": 0,
      "works": 1016,
      "advanced": 1005,
      "needTime": 11,
      "needFetch": 0,
      "saveState": 15,
      "restoreState": 15,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "[MinKey, undefined)",
          "(undefined, [])",
          "([], MaxKey]"
        ]
      },
      "keysExamined": 1016,
      "dupsTested": 1015,
      "dupsDropped": 10,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}

--

db.doc.find({&#39;num&#39;: { $gt: [] }}) ESTE PERICULOS, DEOARECE ÎN FUNCȚIE de INDICELE FOLOSIT S-ar PUTEA DA REZULTATE NEAȘTEPTATE. Ca's din cauza unui invalid index scan care avansează niciun document.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
5

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 0,
  "executionTimeMillisEstimate": 0,
  "works": 1,
  "advanced": 0,
  "needTime": 0,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "nums": {
        "$gt": [ ]
      }
    },
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 1,
    "advanced": 0,
    "needTime": 0,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 0,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 0,
      "executionTimeMillisEstimate": 0,
      "works": 1,
      "advanced": 0,
      "needTime": 0,
      "needFetch": 0,
      "saveState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "([], BinData(0, ))"
        ]
      },
      "keysExamined": 0,
      "dupsTested": 0,
      "dupsDropped": 0,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}

--

db.doc.find({&#39;nums.0': { $gt: -Infinity }}) returnează rezultate corecte, dar are rău de performanță (folosește o colecție completă de scanare).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages
{
  "stage": "COLLSCAN",
  "filter": {
    "nums.0": {
      "$gt": -Infinity
    }
  },
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 2007,
  "advanced": 5,
  "needTime": 2001,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "direction": "forward",
  "docsExamined": 2005
}

--

db.doc.find({&#39;num&#39;: { $gt: -Infinity }}) în mod surprinzător, acest lucru funcționează foarte bine! Acesta oferă dreptul de rezultatele și de a-l's rapid, avansarea 5 documente de scanare index fază.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages
{
  "stage": "FETCH",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 16,
  "advanced": 5,
  "needTime": 10,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "docsExamined": 5,
  "alreadyHasObj": 0,
  "inputStage": {
    "stage": "IXSCAN",
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 15,
    "advanced": 5,
    "needTime": 10,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "keyPattern": {
      "nums": 1
    },
    "indexName": "nums_1",
    "isMultiKey": true,
    "direction": "forward",
    "indexBounds": {
      "nums": [
        "(-inf.0, inf.0]"
      ]
    },
    "keysExamined": 15,
    "dupsTested": 15,
    "dupsDropped": 10,
    "seenInvalidated": 0,
    "matchTested": 0
  }
}
Comentarii (4)

Începând cu 2.6 presă, un alt mod de a face acest lucru este de a compara câmp la un array gol:

ME.find({pictures: {$gt: []}})

Testarea în coajă:

> db.ME.insert([
{pictures: [1,2,3]},
{pictures: []},
{pictures: ['']},
{pictures: [0]},
{pictures: 1},
{foobar: 1}
])

> db.ME.find({pictures: {$gt: []}})
{ "_id": ObjectId("54d4d9ff96340090b6c1c4a7"), "pictures": [ 1, 2, 3 ] }
{ "_id": ObjectId("54d4d9ff96340090b6c1c4a9"), "pictures": [ "" ] }
{ "_id": ObjectId("54d4d9ff96340090b6c1c4aa"), "pictures": [ 0 ] }

Deci, în mod corespunzător include docs unde "pictures" are cel puțin un element de matrice, și exclude docs unde "pictures" este fie un array gol, nu o matrice, sau lipsește.

Comentarii (2)

Puteți utiliza oricare dintre următoarele pentru a realiza acest lucru. Ambele, de asemenea, grijă de a nu se întoarce un rezultat pentru obiecte care nu't au solicitat cheie în ele:

db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
Comentarii (0)
{ $where: "this.pictures.length > 1" }

folosi $de unde și trece aceasta.nume_câmp.lungimea care returnează dimensiunea de matrice teren și verifică prin compararea cu numărul. dacă orice matrice au nici o valoare decât dimensiunea tabloului trebuie să fie de cel puțin 1. deci toate matrice domeniu au lungime mai mult de una, înseamnă că ai niște date în matrice

Comentarii (0)

de a Prelua toate și numai în cazul în care documentele 'imagini' este o matrice și nu este gol

ME.find({pictures: {$type: 'array', $ne: []}})

Dacă utilizați un MongoDb versiune anterioară 3.2, folosi $tip: 4 "în loc de" $tip: &#39;matrice&#39;. Observați că această soluție nu't de a folosi chiar $size, deci nu's nici o problema cu indici ("Interogări se poate utiliza indicii pentru $dimensiunea porțiune dintr-o interogare")

Alte soluții, inclusiv aceste (răspunsul acceptat):

MINE.find({ imagini: { $există: adevărat, $nu: {$size: 0} } }); MINE.find({ imagini: { $există: adevărat, $ne: [] } })

sunt greșit pentru că se vor întoarce documentele, chiar dacă, de exemplu, 'imagini' se nul, nedefinit, 0, etc.

Comentarii (0)

Folosi $elemMatch operator: în conformitate cu documentația

$elemMatch operator meciuri documente care conțin o serie de teren, cu cel puțin un element care se potrivește toate specificate criteriile de interogare.

$elemMatches face-vă că valoarea este o matrice și că acesta nu este gol. Deci, interogarea ar fi ceva de genul

MĂ.find({ imagini: { $elemMatch: {$există: true }}})

PS O variantă a acestui cod se găsește în MongoDB Universitatea's M121 curs.

Comentarii (0)

De asemenea, puteți utiliza metoda de ajutor Există de-a lungul Mongo operator $există

ME.find()
    .exists('pictures')
    .where('pictures').ne([])
    .sort('-created')
    .limit(10)
    .exec(function(err, results){
        ...
    });
Comentarii (0)
ME.find({pictures: {$exists: true}}) 

Simplu, aceasta a lucrat pentru mine.

Comentarii (0)