Suchen Sie ein Dokument mit einem Array, das einen bestimmten Wert enthält

499

Wenn ich dieses Schema habe ...

person = {
    name : String,
    favoriteFoods : Array
}

... wo das favoriteFoodsArray mit Strings gefüllt ist. Wie kann ich mit Mungo alle Personen finden, die "Sushi" als Lieblingsessen haben?

Ich hoffte auf etwas in der Art von:

PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});

(Ich weiß, dass es $containsin Mongodb kein gibt, nur zu erklären, was ich erwartet hatte, bevor ich die Lösung kannte )

Ludwig Magnusson
quelle

Antworten:

693

Da favouriteFoodses sich um ein einfaches Array von Zeichenfolgen handelt, können Sie dieses Feld einfach direkt abfragen:

PersonModel.find({ favouriteFoods: "sushi" }, ...);

Ich würde aber auch empfehlen, das String-Array in Ihrem Schema explizit zu machen:

person = {
    name : String,
    favouriteFoods : [String]
}

Die entsprechende Dokumentation finden Sie hier: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
quelle
19
Dies funktioniert auch, wenn favouriteFoods:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074
12
Als jemand, der neu in Mongo ist und von einem RDBMS wie MySQL kommt, frage ich mich, warum ich nicht früher mit Mongo angefangen habe, wenn ich feststelle, dass solche Lösungen so einfach funktionieren, ohne dass JOINs und zusätzliche Tabellen erforderlich sind. Das heißt aber nicht, dass eines der DBMS dem anderen überlegen ist - es hängt von Ihrem Anwendungsfall ab.
Irvin Lim
9
Verwechsle es nicht. Auch wenn es sich um eine Diktatliste handelt, können Sie sie auf diese Weise abfragen. Beispiel: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini
3
Was passiert, wenn ich ein Array suchen möchte, das mindestens zwei Zeichenfolgen enthält?
Aero Wang
151

Es gibt keinen $containsOperator in Mongodb.

Sie können die Antwort von JohnnyHK verwenden, da dies funktioniert. Die nächste Analogie zu den Inhalten von Mongo lautet $in: Wenn Sie diese verwenden, würde Ihre Abfrage folgendermaßen aussehen:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Alistair Nelson
quelle
10
Ist das richtig? Erwartet Mongodb nicht ein Array von Werten, wenn $ in verwendet wird? wie {name: {$ in: ["Paul", "Dave", "Larry", "Adam"]}}?
Ludwig Magnusson
37
Huh? Dies ist nicht erforderlich. $inwird verwendet, wenn Sie mehrere Abfragewerte haben und das Dokument mit einem von ihnen übereinstimmen muss. Umgekehrt (worum es in dieser Frage geht) ist die Antwort von JohnnyHK richtig. Ich wollte abstimmen, aber ich denke, diese Antwort kann für andere Leute hilfreich sein, die auf dieser Seite landen.
MalcolmOcean
4
Dies hat mir jedoch geholfen, tatsächlich mehrere Werte abzufragen: D Vielen Dank!
Alexandre Bourlier
10
Vielen Dank. Dies ist, wonach ich eigentlich gesucht habe, um nach mehreren Werten zu suchen:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli
@MalcolmOcean ist insofern korrekt, als der Operator $ in für die Umkehrung steht und ein Array als Wert hat . Das Feld , das ein Array ist, ist das, worum es in der Frage geht. Wenn jedoch sowohl das Feld als auch der Wert Arrays sind, sind sowohl diese Antwort als auch die von JohnnyHK relevant, was bedeutet, dass Sie $ in benötigen.
Tscizzle
87

Ich denke, $allwäre in dieser Situation angemessener. Wenn Sie nach einer Person suchen, die Sushi mag, tun Sie Folgendes:

PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})

Da Sie Ihre Suche möglicherweise stärker filtern möchten, gehen Sie wie folgt vor:

PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})

$inist wie ODER und $allwie UND. Überprüfen Sie dies: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
quelle
Entschuldigung, dies ist eine falsche Antwort auf meine Frage. Ich suche nicht nach einer genauen Übereinstimmung, sondern nur nach Arrays, die mindestens den angegebenen Wert enthalten.
Ludwig Magnusson
17
Dies ist eine absolut gültige Antwort auf Ihre Frage! Für einen Wert gibt es keinen Unterschied bei der Verwendung von $ all oder $ in. Wenn Sie mehrere Werte wie "Sushi", "Bananen" haben, sucht $ all nach Personen, die "Sushi" UND "Bananen" in ihrem Favoriten-Lebensmittel-Array haben. Wenn Sie $ in verwenden, erhalten Sie Personen, die "Sushi" ODER "Bananen" haben "in ihrem Lieblingsessen.
Jodo
Ja, es gibt kein $ enthält, aber $ alles ist irgendwie
datdinhquoc
2
Die beste Antwort.
Nikolay Tsenkov
65

Für den Fall, dass das Array Objekte enthält, z. B. wenn favouriteFoodses sich um ein Array von Objekten der folgenden Art handelt:

{
  name: 'Sushi',
  type: 'Japanese'
}

Sie können die folgende Abfrage verwenden:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Kfir Erez
quelle
2
Dies ist leicht die beste Antwort. Viel einfacher zu bedienen, wenn Sie es eilig haben.
Uber Schnoz
Dies sollte die ausgewählte Antwort sein. Wenn Sie ein Array verschachtelter Dokumente in MongoDB abfragen, gehen Sie wie folgt vor. Ich bin mir nicht sicher, ob es das effizienteste ist, aber wenn das alles ist, was Sie versuchen, ist dies alles, was Sie brauchen.
Kyle L.
32

Für den Fall, dass Sie Dokumente finden müssen, die NULL-Elemente in einem Array von Unterdokumenten enthalten, habe ich diese Abfrage gefunden, die ziemlich gut funktioniert:

db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})

Diese Abfrage stammt aus diesem Beitrag: MongoDb-Abfragearray mit Nullwerten

Es war eine großartige Entdeckung und funktioniert viel besser als meine eigene ursprüngliche und falsche Version (die sich nur für Arrays mit einem Element als gut herausstellte):

.find({
    'MyArrayOfSubDocuments': { $not: { $size: 0 } },
    'MyArrayOfSubDocuments._id': { $exists: false }
})
Jesus Campon
quelle
3

Obwohl Sie mit find () einverstanden sind, ist dies in Ihrem Anwendungsfall am effektivsten. Trotzdem gibt es ein $ match of Aggregation Framework, um die Abfrage einer großen Anzahl von Einträgen zu vereinfachen und eine geringe Anzahl von Ergebnissen zu generieren, die für Sie besonders für das Gruppieren und Erstellen neuer Dateien von Wert sind.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
quelle
funktioniert nicht mit mongodb 4.2 .. bitte antworte
vimmi
Welchen Fehler erhalten Sie bitte im Detail?
Amitesh
3

Der Fall von lookup_food_array ist ein Array.

match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}

Der Fall von lookup_food_array ist eine Zeichenfolge.

match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Gautam
quelle
1

Für Loopback3 funktionierten alle angegebenen Beispiele nicht oder so schnell wie die Verwendung der REST-API. Aber es half mir, die genaue Antwort herauszufinden, die ich brauchte.

{"where":{"arrayAttribute":{ "all" :[String]}}}

Mark Ryan Orosa
quelle
1
Du bist ein Lebensretter, danke! Wo ist das dokumentiert und ich habe es verpasst? Kannst du den Link bitte posten? Vielen Dank.
user2078023
-3

Wenn Sie so etwas wie einen "enthält" -Operator über Javascript verwenden möchten, können Sie dafür immer einen regulären Ausdruck verwenden ...

z.B. Angenommen, Sie möchten einen Kunden mit dem Namen "Bartolomew" abrufen

async function getBartolomew() {
    const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
    const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
    const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol

    console.log(custStartWith_Bart);
    console.log(custEndWith_lomew);
    console.log(custContains_rtol);
}
Alingenomen
quelle
-26

Ich weiß, dass dieses Thema alt ist, aber für zukünftige Leute, die sich die gleiche Frage stellen könnten, könnte eine andere unglaublich ineffiziente Lösung sein:

PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});

Dies vermeidet alle Optimierungen durch MongoDB und wird daher nicht im Produktionscode verwendet.

user3027146
quelle
Gibt es aus Neugier einen Vorteil, wenn man es so macht?
Ludwig Magnusson
5
Dies ist im Vergleich zur akzeptierten Antwort unglaublich ineffizient. es umgeht alle Optimierungen, die Mongo hinter die Kulissen setzt, um einen geraden Fund wie im akzeptierten zu finden.
unwissentlich
1
Dies ist genau die Antwort, die ich in meinem Fall brauchte! Vielen Dank "Benutzer" :)
Vasyl Boroviak