Rechercher un document avec un tableau contenant une valeur spécifique

499

Si j'ai ce schéma ...

person = {
    name : String,
    favoriteFoods : Array
}

... où le favoriteFoodstableau est rempli de chaînes. Comment puis-je trouver toutes les personnes qui ont des «sushis» comme nourriture préférée en utilisant des mangoustes?

J'espérais quelque chose dans le sens de:

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

(Je sais qu'il n'y en a pas $containsdans mongodb, juste expliquer ce que je m'attendais à trouver avant de connaître la solution)

Ludwig Magnusson
la source

Réponses:

693

Comme favouriteFoodsc'est un simple tableau de chaînes, vous pouvez simplement interroger ce champ directement:

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

Mais je recommanderais également de rendre le tableau de chaînes explicite dans votre schéma:

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

La documentation pertinente peut être trouvée ici: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
la source
19
Cela fonctionne également si favouriteFoods:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074
12
En tant que nouveau venu à Mongo provenant d'un SGBDR comme MySQL, constater que de telles solutions fonctionnent si simplement sans avoir besoin de JOIN et de tables supplémentaires me fait me demander pourquoi je n'ai pas commencé sur Mongo plus tôt. Mais cela ne veut pas dire que l'un des SGBD est supérieur à l'autre - cela dépend de votre cas d'utilisation.
Irvin Lim
9
Ne vous y trompez pas. Même s'il s'agit d'une liste de dict, vous pouvez toujours l'interroger de cette façon. Échantillon: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini
3
Que se passe-t-il lorsque je souhaite trouver un tableau contenant au moins deux chaînes?
Aero Wang
151

Il n'y a aucun $containsopérateur dans mongodb.

Vous pouvez utiliser la réponse de JohnnyHK pendant que cela fonctionne. L'analogie la plus proche de celle que contient mongo est $in, en utilisant cela, votre requête ressemblerait à:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Alistair Nelson
la source
10
Est-ce correct? Mongodb n'attend-il pas un tableau de valeurs lors de l'utilisation de $ in? comme {name: {$ in: ["Paul", "Dave", "Larry", "Adam"]}}?
Ludwig Magnusson
38
Hein? C'est inutile. $inest utilisé lorsque vous avez plusieurs valeurs de requête et que le document doit correspondre à l'une d'entre elles. Pour l'inverse (sur quoi porte cette question), la réponse de JohnnyHK est correcte. J'allais voter contre, mais je suppose que cette réponse peut être utile à d'autres personnes qui se retrouvent sur cette page.
MalcolmOcean
4
Mais cela m'a aidé à interroger avec plusieurs valeurs: D Merci beaucoup!
Alexandre Bourlier
11
Merci. Voici ce que je cherchais réellement, la façon de rechercher plusieurs valeurs:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli
@MalcolmOcean est correct, en ce que l'opérateur $ in est pour l'inverse, ayant un tableau comme valeur . Le champ étant un tableau est ce que la question pose. Toutefois, si les deux le champ et la valeur sont des tableaux, alors à la fois cette réponse et de JohnnyHK sont pertinents, ce qui signifie que vous avez besoin $ en.
tscizzle
88

Je pense que ce $allserait plus approprié dans cette situation. Si vous cherchez une personne qui aime les sushis, vous faites:

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

Comme vous voudrez peut-être filtrer davantage votre recherche, comme ceci:

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

$inest comme OU et $allcomme ET. Vérifiez ceci: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
la source
Désolé, c'est une réponse incorrecte à ma question. Je ne recherche pas une correspondance exacte, mais uniquement des tableaux contenant au moins la valeur spécifiée.
Ludwig Magnusson
18
C'est une réponse parfaitement valable à votre question! Pour une valeur, il n'y a pas de différence dans l'utilisation de $ all ou $ in. Si vous avez plusieurs valeurs comme "sushi", "bananes", $ all recherche des personnes qui ont "sushi" ET "bananes" dans leur tableau préféré, si vous utilisez $ dans vous obtenez des personnes qui ont "sushi" OU "bananes" "dans leur gamme de plats préférés.
Jodo
oui, il n'y a pas $ contains mais $ all est en quelque sorte
datdinhquoc
3
La meilleure réponse.
Nikolay Tsenkov
65

Dans le cas où le tableau contient des objets, par exemple, if favouriteFoodsest un tableau d'objets parmi les suivants:

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

vous pouvez utiliser la requête suivante:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Kfir Erez
la source
2
C'est facilement la meilleure réponse. Beaucoup plus facile à utiliser lorsque vous êtes pressé.
Uber Schnoz
Ce devrait être la réponse choisie. Si vous devez interroger un tableau de documents imbriqués dans MongoDB, voici comment procéder. Je ne sais pas si c'est le plus efficace, mais si c'est tout ce que vous essayez de faire, c'est tout ce dont vous avez besoin.
Kyle L.
32

Si vous avez besoin de trouver des documents contenant des éléments NULL dans un tableau de sous-documents, j'ai trouvé cette requête qui fonctionne plutôt bien:

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

Cette requête est tirée de ce post: tableau de requête MongoDb avec des valeurs nulles

C'était une excellente découverte et cela fonctionne beaucoup mieux que ma propre version initiale et incorrecte (qui s'est avérée fonctionner correctement uniquement pour les tableaux avec un élément):

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

Bien que d'accord avec find () est le plus efficace dans votre cas d'utilisation. Il existe toujours $ match du cadre d'agrégation, pour faciliter la requête d'un grand nombre d'entrées et générer un faible nombre de résultats qui vous tiennent particulièrement à cœur pour le regroupement et la création de nouveaux fichiers.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
la source
ne fonctionne pas avec mongodb 4.2 .. veuillez répondre
vimmi
Quelle erreur obtenez-vous, veuillez fournir en détail?
Amitesh
3

L'incase de lookup_food_array est un tableau.

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

L'incase de lookup_food_array est une chaîne.

match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
gautam
la source
1

Pour Loopback3, tous les exemples donnés n'ont pas fonctionné pour moi, ni aussi rapidement que l'utilisation de l'API REST. Mais cela m'a aidé à trouver la réponse exacte dont j'avais besoin.

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

Mark Ryan Orosa
la source
1
Vous êtes un épargnant de vie, merci! Où est-ce documenté et je l'ai raté? Pouvez-vous poster le lien s'il vous plaît? Merci.
user2078023
-3

Si vous souhaitez utiliser quelque chose comme un opérateur "contient" via javascript, vous pouvez toujours utiliser une expression régulière pour cela ...

par exemple. Supposons que vous souhaitiez récupérer un client ayant "Bartolomew" comme nom

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
la source
-26

Je sais que ce sujet est ancien, mais pour les futures personnes qui pourraient se poser la même question, une autre solution incroyablement inefficace pourrait être de faire:

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

Cela évite toutes les optimisations par MongoDB donc ne pas utiliser dans le code de production.

user3027146
la source
Par curiosité, y a-t-il un avantage à le faire de cette façon?
Ludwig Magnusson
5
C'est incroyablement inefficace par rapport à la réponse acceptée; il contourne toute l'optimisation que Mongo met dans les coulisses pour une recherche directe comme dans l'acceptation.
involontaire
1
C'est exactement la réponse dont j'avais besoin dans mon cas! Merci "utilisateur" :)
Vasyl Boroviak