Alle meine Aufzeichnungen haben ein Feld namens "Bilder". Dieses Feld ist ein Array von Zeichenfolgen.
Ich möchte jetzt die neuesten 10 Datensätze, bei denen dieses Array NICHT leer ist.
Ich habe herum gegoogelt, aber seltsamerweise habe ich nicht viel darüber gefunden. Ich habe in die Option $ where gelesen, aber ich habe mich gefragt, wie langsam das für native Funktionen ist und ob es eine bessere Lösung gibt.
Und selbst dann funktioniert das nicht:
ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind()
Gibt nichts zurück. Das Verlassen this.pictures
ohne das Längenbit funktioniert, aber dann werden natürlich auch leere Datensätze zurückgegeben.
mongoengine
ME.find({ pictures: { $gt: [] } })
IST GEFÄHRLICH, auch in neueren MongoDB-Versionen. Wenn Sie einen Index in Ihrem Listenfeld haben und dieser Index während der Abfrage verwendet wird, erhalten Sie unerwartete Ergebnisse. Beispiel:db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
Gibt die richtige Nummer zurück, währenddb.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
zurückgegeben wird0
.Nach einigem Hin und Her, insbesondere in den Mongodb-Dokumenten, und rätselhaften Zusammenhängen war dies die Antwort:
quelle
pictures
.Dies könnte auch für Sie funktionieren:
quelle
pictures.2
existiert,pictures.1
aber nicht?$exists
Operator ist ein Boolescher Wert, kein Offset. @tenbatsu solltetrue
anstelle von verwenden1
.Would there ever be a case where pictures.2 exists but pictures.1 does not?
Ja, dieser Fall könnte passieren .pictures
es sich um ein Unterdokument handelt, nicht um ein Array. zBpictures: {'2': 123}
pictures
.Bei der Abfrage sind Ihnen zwei Dinge wichtig - Genauigkeit und Leistung. In diesem Sinne habe ich in MongoDB v3.0.14 einige verschiedene Ansätze getestet.
TL; DR
db.doc.find({ nums: { $gt: -Infinity }})
ist die schnellste und zuverlässigste (zumindest in der von mir getesteten MongoDB-Version).EDIT: Dies funktioniert nicht mehr in MongoDB v3.6! In den Kommentaren unter diesem Beitrag finden Sie eine mögliche Lösung.
Installieren
Ich habe 1k Dokumente ohne Listenfeld, 1k Dokumente mit einer leeren Liste und 5 Dokumente mit einer nicht leeren Liste eingefügt.
Ich erkenne, dass dies nicht ausreicht, um die Leistung so ernst zu nehmen wie in den folgenden Tests, aber es reicht aus, um die Richtigkeit verschiedener Abfragen und das Verhalten ausgewählter Abfragepläne darzustellen.
Tests
db.doc.find({'nums': {'$exists': true}})
gibt falsche Ergebnisse zurück (für das, was wir erreichen wollen).- -
db.doc.find({'nums.0': {'$exists': true}})
Gibt korrekte Ergebnisse zurück, ist aber auch bei Verwendung eines vollständigen Erfassungsscans langsam (HinweisphaseCOLLSCAN
in der Erklärung).- -
db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}})
gibt falsche Ergebnisse zurück. Dies liegt an einem ungültigen Index-Scan, bei dem keine Dokumente weitergeleitet werden. Ohne den Index wird es wahrscheinlich genau, aber langsam sein.- -
db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}})
gibt korrekte Ergebnisse zurück, aber die Leistung ist schlecht. Es führt technisch einen Index-Scan durch, rückt dann aber alle Dokumente vor und muss sie dann filtern.- -
db.doc.find({'nums': { $exists: true, $ne: [] }})
liefert korrekte Ergebnisse und ist etwas schneller, aber die Leistung ist immer noch nicht ideal. Es wird IXSCAN verwendet, das nur Dokumente mit einem vorhandenen Listenfeld erweitert, dann aber die leeren Listen einzeln herausfiltern muss.- -
db.doc.find({'nums': { $gt: [] }})
IST GEFÄHRLICH, WEIL ABHÄNGIG VON DEM VERWENDETEN INDEX UNERWARTETE ERGEBNISSE FÜHREN KÖNNEN. Dies liegt an einem ungültigen Index-Scan, bei dem keine Dokumente weitergeleitet werden.- -
db.doc.find({'nums.0’: { $gt: -Infinity }})
Gibt korrekte Ergebnisse zurück, weist jedoch eine schlechte Leistung auf (verwendet einen vollständigen Sammlungsscan).- -
db.doc.find({'nums': { $gt: -Infinity }})
überraschenderweise funktioniert das sehr gut! Es liefert die richtigen Ergebnisse und ist schnell und rückt 5 Dokumente aus der Index-Scan-Phase vor.quelle
seen_events
String-Array haben, das ebenfalls indiziert ist. Bei der Suche mit{ $gt: -Infinity }
bekomme ich sofort 0 Dokumente. Mit{ $exists: true, $ne: [] }
I erhalten Sie die wahrscheinlicheren 1,2 Millionen Dokumente, wobei in der FETCH-Phase viel Zeit verschwendet wird: gist.github.com/N-Coder/b9e89a925e895c605d84bfeed648d82cdb.test_collection.find({"seen_events.0": {$exists: true}})
ist schlecht, weil es einen Sammlungsscan verwendet. 2.db.test_collection.find({seen_events: {$exists: true, $ne: []}})
ist schlecht, weil sein IXSCAN mit allen Dokumenten übereinstimmt und dann die Filterung in der langsamen FETCH-Phase durchgeführt wird. 3. Gleiches gilt fürdb.test_collection.find({seen_events: {$exists: true, $not: {$size: 0}}})
. 4. Alle anderen Abfragen geben ungültige Ergebnisse zurück.seen_events
Zeichenfolgen enthalten, können Sie Folgendes verwenden :db.test_collection.find({seen_events: {$gt: ''}}).count()
. Überprüfen Sie, ob die Leistung gut istdb.test_collection.find({seen_events: {$gt: ''}}).explain(true).executionStats
. Sie können wahrscheinlich erzwingen, dass gesehene Ereignisse Zeichenfolgen sind, und zwar über die SchemaüberprüfungAb Version 2.6 können Sie das Feld auch mit einem leeren Array vergleichen:
Testen Sie es in der Shell:
Es enthält also ordnungsgemäß die Dokumente, in denen
pictures
mindestens ein Array-Element vorhanden ist, und schließt die Dokumente aus, in denenpictures
entweder ein leeres Array oder kein Array vorhanden ist oder fehlt.quelle
db.ME.createIndex({ pictures: 1 })
und danndb.ME.find({pictures: {$gt: []}})
wird null Ergebnisse zurückgeben, zumindest in MongoDB v3.0.14Sie können eine der folgenden Methoden verwenden, um dies zu erreichen.
Beide sorgen auch dafür, dass für Objekte, die nicht den angeforderten Schlüssel enthalten, kein Ergebnis zurückgegeben wird:
quelle
Rufen Sie alle und nur die Dokumente ab, bei denen 'Bilder' ein Array ist und nicht leer
Wenn Sie eine MongoDb-Version vor 3.2 verwenden , verwenden Sie
$type: 4
anstelle von$type: 'array'
. Beachten Sie, dass diese Lösung nicht einmal $ size verwendet , sodass es kein Problem mit Indizes gibt ("Abfragen können keine Indizes für den $ size-Teil einer Abfrage verwenden").Andere Lösungen, einschließlich dieser (akzeptierte Antwort):
sind falsch , weil sie Dokumente , auch wenn zum Beispiel zurückkehren, ‚Bilder‘ ist
null
,undefined
, 0, usw.quelle
Verwenden Sie den
$elemMatch
Operator: gemäß der Dokumentation$elemMatches
stellt sicher, dass der Wert ein Array ist und nicht leer. Die Abfrage wäre also so etwas wieME.find({ pictures: { $elemMatch: {$exists: true }}})
PS Eine Variante dieses Codes finden Sie im M121-Kurs der MongoDB University.
quelle
Sie können auch die Hilfsmethode Exists verwenden, wenn der Mongo-Operator $ existiert
quelle
Verwenden Sie $ where und übergeben Sie this.field_name.length, das die Größe des Array-Felds zurückgibt, und überprüfen Sie es, indem Sie es mit number vergleichen. Wenn ein Array einen Wert als die Arraygröße hat, muss es mindestens 1 sein. Wenn also alle Arrayfelder eine Länge von mehr als eins haben, bedeutet dies, dass einige Daten in diesem Array enthalten sind
quelle
So einfach war das, das hat bei mir funktioniert.
quelle