it-swarm.com.de

MongoDB-Aggregat nach Feld existiert

Es fällt mir schwer zu glauben, dass diese Frage nicht schon irgendwo gestellt und beantwortet wurde, aber ich kann keine Spur davon finden.

Ich habe eine MongoDB-Aggregationsabfrage, die nach einem Boolean gruppiert werden muss: die Existenz eines anderen Feldes.

Beginnen wir zum Beispiel mit dieser Sammlung:

> db.test.find()
{ "_id" : ObjectId("53fbede62827b89e4f86c12e"),
  "field" : ObjectId("53fbede62827b89e4f86c12d"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee002827b89e4f86c12f"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee092827b89e4f86c131"),
  "field" : ObjectId("53fbee092827b89e4f86c130"), "name" : "John" }
{ "_id" : ObjectId("53fbee122827b89e4f86c132"), "name" : "Ben" }

2 Dokumente haben "Feld" und 2 nicht . Beachten Sie, dass jeder Wert von "Feld" unterschiedlich sein kann. Wir wollen nur nach seiner Existenz gruppieren (oder auch Nicht-Nullheit funktioniert für mich, ich habe keine Nullwerte gespeichert).

Ich habe versucht, $ project zu verwenden, aber $ exists existiert dort nicht und $ cond und $ ifNull haben mir nicht geholfen. Das Feld scheint immer zu existieren, auch wenn es nicht so ist:

> db.test.aggregate(
  {$project:{fieldExists:{$cond:[{$eq:["$field", null]}, false, true]}}},
  {$group:{_id:"$fieldExists", count:{$sum:1}}}
)
{ "_id" : true, "count" : 4 }

Ich würde erwarten, dass das folgende, viel einfachere Aggregat funktioniert, aber aus irgendeinem Grund wird $ exists nicht auf diese Weise unterstützt:

> db.test.aggregate({$group:{_id:{$exists:"$field"}, count:{$sum:1}}})
assert: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
    at Error (<anonymous>)
    at doassert (src/mongo/Shell/assert.js:11:14)
    at Function.assert.commandWorked (src/mongo/Shell/assert.js:244:5)
    at DBCollection.aggregate (src/mongo/Shell/collection.js:1149:12)
    at (Shell):1:9
2014-08-25T19:19:42.344-0700 Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed at src/mongo/Shell/assert.js:13

Weiß jemand, wie man aus einer solchen Sammlung das gewünschte Ergebnis erhält?

Erwartetes Ergebnis:

{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }
31
Erik

Ich habe das gleiche Problem erst letzte Nacht auf diese Weise gelöst:

> db.test.aggregate({$group:{_id:{$gt:["$field", null]}, count:{$sum:1}}})
{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }

Unter http://docs.mongodb.org/manual/reference/bson-types/#bson-types-comparison-order finden Sie eine vollständige Erklärung, wie dies funktioniert.

58
kdkeck

Der Operator $exists ist ein "Abfrage" -Operator. Er wird also hauptsächlich zum "Filtern" von Ergebnissen verwendet, anstatt eine logische Bedingung zu identifizieren.

Als "logischer" Operator unterstützt das Aggregationsframework den Operator $ifNull . Dies gibt den Feldwert zurück, in dem er existiert, oder den alternativ angegebenen Wert, bei dem er nicht oder anderweitig zu null ausgewertet wird

db.test.aggregate([
    { "$group": {
        "_id": { "$ifNull": [ "$field", false ] },
        "count": { "$sum": 1 }
    }}
])

Aber selbst wenn dies kein "wahr/falsch" -Vergleich ist, ist es wahrscheinlich besser, wenn Sie den tatsächlichen Wert des Felds zurückgeben, in dem es vorhanden ist. Dann ist es wahrscheinlich besser mit $cond Aussage ähnlich wie Sie haben:

db.test.aggregate([
    { "$group": {
        "_id": { "$cond": [{ "$eq": [ "$field", null ] }, true, false ] },
        "count": { "$sum": 1 }
    }}
])

Wo $ifNull sehr nützlich sein kann, ist das Ersetzen nicht vorhandener Arrayfelder, die andernfalls einen Fehler verursachen würden, wenn $unwind verwendet wird. Sie können dann beispielsweise ein einzelnes Element oder ein leeres Array zurückgeben, sodass die restliche Pipelineverarbeitung nicht zu Problemen führt.

6
Neil Lunn

Ich habe es mit der Prüfung auf undefined gelöst

$ne : [$var_to_check, undefined]

oder

$ne:  [ { $type : "$var_to_check"}, 'missing'] }

Dies gibt true zurück, wenn die Variable definiert ist

5
Delcon

Meine Antwort lautet:

{'$project': {
    'field_exists': {'$or': [
        {'$eq': ['$field', null]}, 
        {'$gt': ['$field', null]},
    ]},
}}

Hier sind die Details. $ exists bedeutet, dass das Feld existiert, auch wenn es null oder ein anderer leerer Wert ist. Deshalb sind alle Antworten auf dieser Seite falsch.

Lass uns ein bisschen testen. Überprüfen Sie dies:

// Let's take any collection that have docs
db.getCollection('collection').aggregate([
  // Get arbitrary doc, no matter which, we won't use it
  {"$limit": 1},
  // Project our own fields (just create them with $literal)
  {'$project': {
    '_id': 0,
    'null_field': {'$literal': null},
    'not_null_field': {'$literal': {}},
  }},
])

Wir werden das bekommen:

{
    "null_field" : null,
    "not_null_field" : {}
}

Dann klären wir, welche Felder in diesem Dokument existieren:

  1. null_field - existiert
  2. not_null_field - existiert
  3. non_existent_field - nicht.

Okay, es ist Zeit, die oben erwähnte Projektphase zu testen. Fügen wir es für jeden Bereich hinzu, an dem wir interessiert sind:

{'$project': {
    'null_field_exists': {'$or': [
        {'$eq': ['$null_field', null]}, 
        {'$gt': ['$null_field', null]},
    ]},
    'not_null_field_exists': {'$or': [
        {'$eq': ['$not_null_field', null]}, 
        {'$gt': ['$not_null_field', null]},
    ]},
    'non_existent_field_exists': {'$or': [
        {'$eq': ['$non_existent_field', null]}, 
        {'$gt': ['$non_existent_field', null]},
    ]},
}},

Was wir bekommen ist:

{
    "null_field_exists" : true,
    "not_null_field_exists" : true,
    "non_existent_field_exists" : false
}

Richtig!

Und eine kleine Anmerkung: Wir verwenden null zum Vergleichen, weil es der kleinste Wert ist, der zumindest wertvoll ist (kleiner ist nur die Nichtexistenz).

1
egvo

Weiß nicht, wie es war, aber jetzt im Jahr 2019 gibt es eine saubere Lösung. Tun Sie dies in der Aggregationspipeline

$match: {"my_field": {$ne: null}}

Schöne Sache ist in meiner lang 'ne' heißt nicht :)

0
djuleAyo