This content originally appeared on DEV Community and was authored by Renan Marquetti
Introdução
Esse artigo tem por objetivo ensinar realizar consultas em campos de arrays para collections MongoDb
“Sintaxe JS” vs “Sintaxe mongoDb”
Primeiramente vamos estabelecer a base da nossa didática, para isso vamos supor que você tenha uma array de objetos com a seguinte tipagem:
type Inventory = {
item: string,
qty: number,
tags: Array<string>,
dim_cm: Array<number>,
}
como você faria para buscar a lista de objetos com menos de 10 itens em estoque? poderia ser feito um filter no array passando como argumento uma arrow function que atende-se tal critério, ficaria algo, mais ou menos assim:
const queryLessThanTen = obj => obj.qty < 10;
const myResult = myCollectionArray.filter(queryLessThanTen);
Observe a nossa arrow function na primeira linha, nesse contexto ela se comporta como uma “query”, sendo essa a “Sintaxe JS” para buscar dados em um array
o mongo possui a sua própria “sintaxe” de consulta, então se os nossos dados estiverem em uma collection do mongoDb, teriamos que escrever a nossa query da seguinte forma:
const queryLessThanTen = {qty: {$lt: 10}};
const myResult = db.collection('inventory').find(queryLessThanTen);
você consegue perceber a diferença? embora as “sintaxes” sejam diferentes, os objetivos são os mesmos, logo obj => obj.qty < 10
é equivalente a {qty: {$lt: 10}}
, esta sera a base da nossa didática
Casos de uso
Agora vamos começar a colocar a mão na massa, assim vamos expor os principais casos de uso envolvido quando deseja realizar consultas sobre array em mongoDb, mas cuidado, pode parecer simples a primeira vista, e de fato o é, mas finds sobre arrays esconde uma série de segredos e nuances que vamos começar a expor e trabalhar a partir daqui.
Comparação por igualdade/uniformidade
Caso: contem/inclui valor
const js = i => i.tags.includes("red")
const mongo = {tags: "red"}
este é o mais simples deles, deve ser usado para selecionar todos os documentos que contem um determinado valor dentro do array
Caso: valor index N é igual á
const js = i => i.tags[1] === "red"
const mongo = {'tags.1': "red"}
caso similar ao anterior, mas com uma diferença importante, ele contem uma especificação de index para a consulta.
por exemplo nesse exemplo o array ["blank", "red", "blue"]
é valido, porque o valor de index 1 é iqual a “red”, mas o array ["red", "blue"]
não é valido, porque o valor de index 1 (“blue”) é diferente de “red”.
Caso: idêntico/exato
const js = i => i.tags.toString() == ["red", "blank"].toString()
const mongo = {tags: ["red", "blank"]}
este caso é similar ao primeiro caso, mas esconde um segredo, deve ser usado somente quando deseja buscar documentos com correspondência exata de valores, incluindo a ordem estabelecida na consulta.
por exemplo, quando se utiliza a correspondência exata um documento que contem o array ["blank", "red"]
não corresponde exatamente ao array ["red", "blank"]
, isso porque contem todos os valores mas a ordem dos não corresponde.
Caso: contem/inclui um conjunto de valores
const js = i => i.tags.includes("red") && i.tags.includes("blank")
const mongo = {tags: { $all: ["red", "blank"]}}
similar o caso acima, mas com uma diferença crucial, aqui usamos o operador $all
, ele faz com que os valores não precisam estar na ordem pré estabelecida, portanto nessa consulta os arrays ["blank", "red"]
e ["red", "blank"]
são equivalentes.
outro ponto a ser observado, nesse tipo de consulta não é levado em conta outros valores do array, logo então o array ["blank", "blue", "red"]
também corresponde ao array ["red", "blank"]
, isso porque os demais valores são irrelevantes para a consulta.
Caso: Tamanho Array
const js = i => i.tags.length == 3
const mongo = {tags: {$size: 3}}
este caso é bem simples, usamos o operador $size
para simplesmente compara os tamanhos do array, portanto os arrays: [true, true, false]
, [1, 2, 3]
, ["red", "blank", "blue"]
são equivalentes.
Comparação por referencia/relativa
Caso: contem/inclui valor maior que
const js = i => i.dim_cm.findIndex(d => d > 25) > -1
const mongo = {dim_cm: {$gt: 25}}
neste caso busca todos os documentos que contem ao menos um valor numérico maior que 25.
também pode ser utilizado o comparador {$lt: 25}
(less than), para buscar valor menor que 25.
Caso: contem/inclui valor maior que e valor menor que
const js = i => i.dim_cm.findIndex(d => d > 15) > -1 && i.dim_cm.findIndex(d => d < 20) > -1
const mongo = {dim_cm: {$gt: 15, $lt: 20}}
está consulta deve ser usada quando busca um array que contem algum valor maior que 15 e algum valor menor do que 15, nesse caso o array contendo os valores [25, 10]
é valido para a consulta.
este caso contem uma importante nuance, caso o array contenha algum valor que atende ambas as condições, como por exemplo o array [18, 23]
é um array valido, porque o valor 18 atende ambas as condições, sendo assim o valor 23 é irrelevante para a consulta.
Caso: contem/inclui valores maior que e menor que
const js = i => i.dim_cm.findIndex(d => d > 22 && d < 30) > -1
const mongo = {dim_cm: {$elemMatch: {$gt: 22, $lt: 30}}
similar o caso acima, com uma diferença, aqui usamos o operador $elemMatch
, ele faz com que todas as comparações se apliquem em todos os elementos do array.
por exemplo nessa consulta o array [25, 29]
é valido, porque tanto 25 quanto 29 é maior que 22 e menor que 29.
já o array [25, 32]
não é valido, porque 25 atende ambas as comparações, mas 32 atende apenas uma delas, sendo assim a comparação é invalida
Casos compostos
Também é possível combinar diversos casos e operadores para buscar um resultado mais especifico, seria um trabalho hercúleo expor todas as possibilidades, logo vamos exemplificar apenas alguns deles
Caso: tamanho de array e inclui conjunto de valores
const js = i => i.tags.length == 2 && i.tags.includes("red") && i.tags.includes("black")
const mongo = {tags: {$size: 2, $all: ["red", "black"]}}
este exemplo utiliza a junção dos operadores $size e $all, deve ser usado quando deseja buscar documentos cujo array tenha apenas o conjunto de valores passado como paramentos, mas sem se importar com a ordem dos valores.
por exemplo nesse caso os arrays ["red", "black"]
e ["blank", "red"]
são equivalentes, mas o array ["red", "black", "blue"]
agora não é mais equivalente, isso porque a presença do valor “blue” passa a ser relevante para a comparação.
em resumo, utilize esse caso quando deseja buscar arrays por uniformidade, sem exatidão.
Caso: contem/inclui valores maior que e menor que e algum valor igual á
const js = i => i.dim_cm[0] == 26 && i.dim_cm.findIndex(d => d > 22 && d < 30) > -1
const mongo = {dim_cm: {'dim_cm.0': 26, $elemMatch: {$gt: 22, $lt: 30}}}
unindo o operador $elemMatch e a busca por index, é possível filtrar ainda mais os dados, nesse caso sera retornado os arrays em que todos os valores são maior do que 22 e menor do que 30, mas desde que o primeiro valor seja 26.
por exemplo os arrays: [26]
, [26, 29]
, [26, 25, 27]
são equivalentes, mas os arrays [23]
, [27, 29]
, [28, 25, 27]
não são equivalentes.
isso porque todos os valores satisfazem a primeira regra, mas não satisfaz a ultima regra em que exige que o primeiro valor seja igual a 26.
Repositorio
caso tenha interesse faça um fork desse repositório, e execute na sua maquina, ele contem uma série de teste com todos esses exemplos, isto vai agilizar e profundar ainda mais o seu conhecimentos sobre o assunto.
Agradecimentos
muito obrigado por você que leu até aqui, quero agrade-lo pela sua atenção e dedicação, qualquer duvida ou sugestão ou critica por favor entre em contato comigo através do meu linkedin
This content originally appeared on DEV Community and was authored by Renan Marquetti