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)

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/


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"]} }, ...);

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/


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

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

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

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}

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


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

-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.


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.