Localizar documento com matriz que contenha um valor específico

499

Se eu tiver esse esquema ...

person = {
    name : String,
    favoriteFoods : Array
}

... onde a favoriteFoodsmatriz é preenchida com cadeias. Como posso encontrar todas as pessoas que têm "sushi" como comida favorita usando mangusto?

Eu esperava algo do tipo:

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

(Eu sei que não há $containsno mongodb, apenas explicando o que eu esperava encontrar antes de conhecer a solução)

Ludwig Magnusson
fonte

Respostas:

693

Como favouriteFoodsé uma matriz simples de strings, você pode simplesmente consultar esse campo diretamente:

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

Mas eu também recomendo tornar explícita a matriz de strings no seu esquema:

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

A documentação relevante pode ser encontrada aqui: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
fonte
19
Isso funciona também se favouriteFoods:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074
12
Como alguém novo no Mongo vindo de um RDBMS como MySQL, descobrir que essas soluções funcionam tão simplesmente sem precisar de JOINs e tabelas adicionais me faz pensar por que não iniciei o Mongo antes. Mas isso não quer dizer que o DBMS seja superior ao outro - depende do seu caso de uso.
Irvin Lim
9
Não confunda isso. Mesmo que seja uma lista de dict, você ainda pode consultá-lo dessa maneira. Exemplo: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini
3
O que acontece quando eu quero encontrar uma matriz que contenha pelo menos duas seqüências de caracteres?
Aero Wang
151

Não há $containsoperador no mongodb.

Você pode usar a resposta de JohnnyHK como isso funciona. A analogia mais próxima de contém o mongo é $in: usando isso, sua consulta se pareceria com:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Alistair Nelson
fonte
10
Isso está correto? O mongodb não espera uma matriz de valores ao usar $ in? como {name: {$ in: ["Paul", "Dave", "Larry", "Adam"]}}?
precisa
38
Hã? Isso é desnecessário. $iné usado quando você possui vários valores de consulta e o documento precisa corresponder a um deles. Pelo contrário (que é essa a questão), a resposta de JohnnyHK está correta. Eu estava com um voto negativo, mas acho que essa resposta pode ser útil para outras pessoas que acabam nesta página.
MalcolmOcean
4
Mas isso me ajudou a consultar com vários valores: D Muito obrigado!
Alexandre Bourlier 23/05
11
Obrigado. Isto é o que eu estava realmente procurando, o caminho de pesquisa para vários valores:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli
@ MalcolmOcean está correto, pois o operador $ in é para o reverso, tendo uma matriz como valor . O campo que é uma matriz é o que a pergunta está fazendo. No entanto, se tanto o campo e o valor são arrays, então ambos esta resposta e JohnnyHK do são relevantes, ou seja, você precisa de US $ em.
tscizzle
88

Eu sinto que $allseria mais apropriado nessa situação. Se você está procurando uma pessoa interessada em sushi, faça:

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

Como você pode filtrar mais sua pesquisa, faça o seguinte:

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

$iné como OR e $allcomo AND. Verifique isto: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
fonte
Desculpe, esta é uma resposta incorreta à minha pergunta. Não estou procurando uma correspondência exata, mas apenas as matrizes que contenham pelo menos o valor especificado.
Ludwig Magnusson
18
Esta é uma resposta perfeitamente válida para sua pergunta! Para um valor, não há diferença no uso de $ all ou $ in. Se você tiver vários valores como "sushi", "bananas", $ all estará procurando pessoas que tenham "sushi" AND "bananas" em sua matriz favoritaFood, se usar $ em você estiver recebendo pessoas que tenham bananas "sushi" OU " "em sua variedade de comida favorita.
Jõdo
Sim, não há $ contém, mas $ all é uma espécie disso #
datdinhquoc
3
A melhor resposta.
Nikolay Tsenkov
65

Caso a matriz contenha objetos, por exemplo, se favouriteFoodsfor uma matriz de objetos do seguinte:

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

você pode usar a seguinte consulta:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Kfir Erez
fonte
2
Esta é facilmente a melhor resposta. Muito mais fácil de usar quando você está com pressa.
Uber Schnoz
Essa deve ser a resposta selecionada. Se você está lidando com a consulta de uma matriz de documentos aninhados no MongoDB, é assim que você faz. Não tenho certeza se é o mais eficiente, mas se é tudo o que você está tentando fazer, é tudo o que você precisa.
Kyle L.
32

Caso você precise encontrar documentos que contenham elementos NULL dentro de uma matriz de sub-documentos, encontrei esta consulta que funciona muito bem:

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

Esta consulta é retirada desta postagem: matriz de consulta MongoDb com valores nulos

Foi uma ótima descoberta e funciona muito melhor do que minha própria versão inicial e incorreta (que funcionou bem apenas para matrizes com um elemento):

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

Embora concordar com find () seja mais eficaz em seu caso de uso. Ainda há $ match of framework de agregação, para facilitar a consulta de um grande número de entradas e gerar um baixo número de resultados que agregam valor a você, especialmente para agrupar e criar novos arquivos.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
fonte
não está funcionando com o mongodb 4.2 .. por favor responda
vimmi 27/02
Que erro você está recebendo, forneça em detalhes?
Amitesh 28/02
3

O caso de lookup_food_array é array.

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

O caso de lookup_food_array é string.

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

Para o Loopback3, todos os exemplos dados não funcionaram para mim ou tão rápido quanto o uso da API REST. Mas isso me ajudou a descobrir a resposta exata que eu precisava.

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

Mark Ryan Orosa
fonte
1
Você é um salva-vidas, obrigado! Onde isso está documentado e eu perdi? Você pode postar o link, por favor? Obrigado.
User2078023
-3

Se você quiser usar algo como um operador "contém" por meio de javascript, sempre poderá usar uma expressão regular para isso ...

por exemplo. Digamos que você queira recuperar um cliente que tenha "Bartolomew" como nome

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

Sei que esse tópico é antigo, mas para futuras pessoas que poderiam se perguntar a mesma pergunta, outra solução incrivelmente ineficiente seria:

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

Isso evita todas as otimizações do MongoDB, portanto, não use no código de produção.

user3027146
fonte
Por curiosidade, há alguma vantagem de fazer dessa maneira?
Ludwig Magnusson
5
Isso é incrivelmente ineficiente em comparação com a resposta aceita; ele contorna toda a otimização que Mongo coloca nos bastidores para uma descoberta direta, como no aceito.
involuntário
1
Esta é exatamente a resposta que eu precisava no meu caso! Obrigado "usuário" :)
Vasyl Boroviak 18/04/19