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)

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/


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

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/


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

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

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

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}

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


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

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.