it-swarm.com.de

Suchen Sie nach MongoDB-Datensätzen, bei denen das Array-Feld nicht leer ist

Alle meine Aufzeichnungen haben ein Feld namens "Bilder". Dieses Feld ist ein Array von Strings.

Ich möchte jetzt die neuesten 10 Datensätze, bei denen dieses Array IS NICHT leer ist.

Ich habe herumgeloggert, aber seltsamerweise habe ich nicht viel darüber herausgefunden ... Ich habe die Option "where" gelesen, aber ich habe mich gefragt, wie langsam native Funktionen sind 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. Wenn Sie this.pictures ohne Längenbits verlassen, werden auch leere Datensätze zurückgegeben.

375
skerit

Wenn Sie auch Dokumente haben, die nicht über den Schlüssel verfügen, können Sie Folgendes verwenden:

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

MongoDB verwendet keine Indizes, wenn $ size beteiligt ist. Hier ist eine bessere Lösung:

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

Seit MongoDB 2.6 können Sie mit dem Operator $gt vergleichen, es kann jedoch zu unerwarteten Ergebnissen kommen (Sie finden eine detaillierte Erklärung in dieser Antwort ):

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

Nach einigem Hinschauen, besonders in den mongodb-Dokumenten, und rätselhaften Teilen zusammen war dies die Antwort:

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

Das könnte auch für Sie funktionieren:

ME.find({'pictures.0': {$exists: true}});
92
tenbatsu

Ab Version 2.6 können Sie dies auch mit einem leeren Array vergleichen:

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

Testen Sie es in der Shell:

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

Es schließt also die Dokumente richtig ein, wobei pictures mindestens ein Array-Element hat, und schließt die Dokumente aus, wobei pictures entweder ein leeres Array, kein Array ist oder fehlt.

28
JohnnyHK

Bei der Abfrage sind zwei Dinge wichtig: Genauigkeit und Leistung. In diesem Sinne habe ich in MongoDB v3.0.14 ein paar 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: Das funktioniert in MongoDB v3.6 nicht mehr! Siehe die Kommentare unter diesem Beitrag für eine mögliche Lösung.

Konfiguration

Ich habe 1k Dokumente ohne ein Listenfeld eingefügt, 1k Dokumente mit einer leeren Liste und 5 Dokumente mit einer nicht leeren Liste.

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

Ich bin mir bewusst, dass dies nicht ausreicht, um die Leistung so ernst zu nehmen, wie ich es in den Tests unten gemacht habe, aber es reicht aus, um die Korrektheit verschiedener Abfragen und das Verhalten der ausgewählten Abfragepläne darzustellen.

Tests

db.doc.find({'nums': {'$exists': true}}) liefert falsche Ergebnisse (für das, was wir zu erreichen versuchen).

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

-

db.doc.find({'nums.0': {'$exists': true}}) gibt korrekte Ergebnisse zurück, aber es ist auch langsam, wenn ein vollständiger Erfassungsscan verwendet wird (beachten Sie den Abschnitt COLLSCAN in der Erläuterung).

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({'nums': { $exists: true, $gt: { '$size': 0 }}}) gibt falsche Ergebnisse zurück. Dies liegt daran, dass ein ungültiger Index-Scan keine Dokumente weiterleitet. Es wird wahrscheinlich genau sein, aber langsam ohne den 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({'nums': { $exists: true, $not: { '$size': 0 }}}) liefert korrekte Ergebnisse, aber die Leistung ist schlecht. Technisch führt es einen Index-Scan durch, aber es werden trotzdem alle Dokumente vorgerückt und müssen dann durchgefiltert werden.

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({'nums': { $exists: true, $ne: [] }}) liefert korrekte Ergebnisse und ist etwas schneller, aber die Leistung ist immer noch nicht ideal. Es verwendet IXSCAN, das Dokumente nur mit einem vorhandenen Listenfeld weitertransportiert, die leeren Listen jedoch nacheinander herausfiltern muss.

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({'nums': { $gt: [] }}) IS GEFÄHRLICH, DASS DER INDEX VERWENDET WERDEN KANN, DASS ER Unerwartete Ergebnisse GIBT. Dies liegt an einem ungültigen Index-Scan, der keine Dokumente weiterleitet.

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({'nums.0’: { $gt: -Infinity }}) gibt korrekte Ergebnisse zurück, hat jedoch eine schlechte Leistung (verwendet einen vollständigen Auflistungsscan).

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({'nums': { $gt: -Infinity }}) überraschenderweise funktioniert das sehr gut! Es liefert die richtigen Ergebnisse und ist schnell, 5 Dokumente werden aus der Index-Scan-Phase weitergeleitet.

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
  }
}
27
wojcikstefan

Um dies zu erreichen, können Sie Folgendes verwenden.
Beide sorgen auch dafür, dass kein Ergebnis für Objekte zurückgegeben wird, die den angeforderten Schlüssel nicht enthalten:

db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
4
Paul Imisi

Sie können auch die Hilfsmethode Exists über den Mongo-Operator $ Existieren verwenden

ME.find()
    .exists('pictures')
    .where('pictures').ne([])
    .sort('-created')
    .limit(10)
    .exec(function(err, results){
        ...
    });
0
Eat at Joes
{ $where: "this.pictures.length > 1" }

verwenden Sie das $ where und übergeben Sie das this.field_name.length, das die Größe des Arrayfelds zurückgibt, und überprüfen Sie es durch Vergleichen mit number. Wenn ein Array einen Wert hat, der größer als die Array-Größe ist, muss es mindestens 1 sein. Wenn also alle Array-Felder mehr als eine Länge haben, bedeutet dies, dass einige Daten in diesem Array enthalten sind

0
Prabhat Yadav

Verwenden Sie den Operator $elemMatch: gemäß der Dokumentation

Der Operator $ elemMatch vergleicht Dokumente, die ein Arrayfeld enthalten, mit mindestens einem Element, das allen angegebenen Abfragekriterien entspricht.

$elemMatches stellt sicher, dass der Wert ein Array ist und nicht leer ist. Also wäre die Abfrage so etwas wie

ME.find({ pictures: { $elemMatch: {$exists: true }}})

PS Eine Variante dieses Codes finden Sie im M121-Kurs der MongoDB University.

0
Andres Moreno

Rufen Sie alle und nur die Dokumente ab, bei denen 'Bilder' ein Array ist und nicht leer ist

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

Wenn Sie eine MongoDb-Version vor 3.2 verwenden, verwenden Sie $type: 4 statt $type: 'array'. Beachten Sie, dass diese Lösung nicht einmal $ size verwendet, daher gibt es kein Problem mit Indizes ("Abfragen können keine Indizes für den $ size-Teil einer Abfrage verwenden").

Andere Lösungen, einschließlich dieser (akzeptierte Antwort):

ME.find ({Bilder: {$ Existiert: Wahr, $ Nicht: {$ Größe: 0}}}); ME.find ({bilder: {$ exists: wahr, $ ne: []}})

sind falsch, weil sie Dokumente zurückgeben, auch wenn "Bilder" beispielsweise null, undefined, 0 usw. ist.

0
SC1000