Buscar documento con una matriz que contiene un valor específico

499

Si tengo este esquema ...

person = {
    name : String,
    favoriteFoods : Array
}

... donde la favoriteFoodsmatriz se llena con cadenas. ¿Cómo puedo encontrar a todas las personas que tienen "sushi" como su comida favorita usando mangosta?

Esperaba algo en la línea de:

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

(Sé que no hay $containsmongodb, solo explicando lo que esperaba encontrar antes de conocer la solución)

Ludwig Magnusson
fuente

Respuestas:

693

Como favouriteFoods es una simple matriz de cadenas, puede consultar ese campo directamente:

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

Pero también recomendaría hacer explícito el conjunto de cadenas en su esquema:

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

La documentación relevante se puede encontrar aquí: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
fuente
19
Esto también funciona si favouriteFoodses:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074
12
Como alguien nuevo en Mongo que viene de un RDBMS como MySQL, descubrir que tales soluciones funcionan de manera tan simple sin necesidad de UNIRSE y tablas adicionales me hace preguntarme por qué no comencé en Mongo antes. Pero eso no quiere decir que ninguno de los DBMS sea superior al otro, depende de su caso de uso.
Irvin Lim
99
No lo confundas. Incluso si es una lista de dict, aún puede consultarlo de esta manera. Muestra: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini
3
¿Qué sucede cuando quiero encontrar una matriz que contiene al menos dos cadenas?
Aero Wang
151

No hay $containsoperador en mongodb.

Puede usar la respuesta de JohnnyHK ya que funciona. La analogía más cercana a contiene que tiene Mongo es$in , usando esto su consulta se vería así:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Alistair Nelson
fuente
10
¿Es esto correcto? ¿No está mongodb esperando una matriz de valores al usar $ in? como {nombre: {$ en: ["Paul", "Dave", "Larry", "Adam"]}}?
Ludwig Magnusson el
38
¿Eh? Esto es innecesario $inse usa cuando tiene varios valores de consulta y el documento debe coincidir con uno de ellos. Para el reverso (que es de lo que trata esta pregunta), la respuesta de JohnnyHK es correcta. Iba a votar en contra, pero supongo que esta respuesta puede ser útil para otras personas que terminan en esta página.
MalcolmOcean
44
Pero esto me ayudó a consultar con varios valores: D ¡Muchas gracias!
Alexandre Bourlier
11
Gracias. Esto es lo que realmente estaba buscando, la forma de buscar múltiples valores:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli
@MalcolmOcean es correcto, ya que el operador $ in es para el reverso, teniendo una matriz como valor . El campo que es una matriz es sobre lo que se pregunta. Sin embargo, si tanto el campo como el valor son matrices, tanto esta respuesta como JohnnyHK son relevantes, lo que significa que necesita $ in.
tscizzle el
88

Siento $allque sería más apropiado en esta situación. Si buscas a una persona que le guste el sushi, lo haces:

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

Como es posible que desee filtrar más su búsqueda, así:

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

$ines como OR y $allcomo AND. Verifique esto: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
fuente
Lo sentimos, esta es una respuesta incorrecta a mi pregunta. No estoy buscando una coincidencia exacta, sino solo matrices que contengan al menos el valor especificado.
Ludwig Magnusson
18
¡Esta es una respuesta perfectamente válida a su pregunta! Para un valor no hay diferencia en usar $ all o $ in. Si tiene varios valores como "sushi", "plátanos", $ all está buscando personas que tengan "sushi" Y "plátanos" en su matriz de comida favorita, si usa $ in obtendrá personas que tienen "sushi" O "plátanos" "en su variedad de comida favorita.
Jodo
sí, no hay $ contiene pero $ todo es algo así
datdinhquoc
3
La mejor respuesta
Nikolay Tsenkov
65

En caso de que la matriz contenga objetos, por ejemplo, si favouriteFoodses una matriz de objetos de lo siguiente:

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

puedes usar la siguiente consulta:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Kfir Erez
fuente
2
Esta es fácilmente la mejor respuesta. Mucho más fácil de usar cuando tienes prisa.
Uber Schnoz
Esta debería ser la respuesta seleccionada. Si está tratando de consultar una matriz de documentos anidados en MongoDB, así es como lo hace. No estoy seguro de si es el más eficiente, pero si eso es todo lo que está tratando de hacer, esto es todo lo que necesita.
Kyle L.
32

En caso de que necesite encontrar documentos que contengan elementos NULL dentro de una matriz de subdocumentos, he encontrado esta consulta que funciona bastante bien:

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

Esta consulta está tomada de esta publicación: matriz de consulta MongoDb con valores nulos

Fue un gran hallazgo y funciona mucho mejor que mi propia versión inicial e incorrecta (que resultó funcionar bien solo para matrices con un elemento):

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

Aunque estar de acuerdo con find () es más efectivo en su caso de uso. Todavía hay $ match de marco de agregación, para facilitar la consulta de una gran cantidad de entradas y generar una baja cantidad de resultados que tienen valor para usted, especialmente para agrupar y crear nuevos archivos.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
fuente
no funciona con mongodb 4.2 .. por favor responda
vimmi
¿Qué error está recibiendo, por favor proporcione en detalle?
Amitesh
3

En caso de lookup_food_array es matriz.

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

En caso de lookup_food_array es string.

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

Para Loopback3, todos los ejemplos dados no funcionaron para mí, o tan rápido como usar REST API de todos modos. Pero me ayudó a encontrar la respuesta exacta que necesitaba.

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

Mark Ryan Orosa
fuente
1
Eres un salvavidas, gracias! ¿Dónde está documentado y me lo perdí? ¿Puedes publicar el enlace por favor? Gracias.
user2078023
-3

Si desea usar algo como un operador "contiene" a través de javascript, siempre puede usar una expresión regular para eso ...

p.ej. Digamos que desea recuperar un cliente que tenga "Bartolomew" como nombre

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
fuente
-26

Sé que este tema es antiguo, pero para las personas futuras que podrían preguntarse la misma pregunta, otra solución increíblemente ineficiente podría ser:

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

Esto evita todas las optimizaciones de MongoDB, por lo que no debe usarse en el código de producción.

usuario3027146
fuente
Por curiosidad, ¿hay alguna ventaja de hacerlo de esta manera?
Ludwig Magnusson
55
Esto es increíblemente ineficiente en comparación con la respuesta aceptada; evita toda la optimización que Mongo pone detrás de escena para un hallazgo directo como el aceptado.
involuntario
1
¡Sin embargo, esta es exactamente la respuesta que necesitaba en mi caso! Gracias "usuario" :)
Vasyl Boroviak