MongoDB Find, Projection e Filter
FIND
Para que você consiga executar os exemplos eu preparei um repositório com um arquivo docker compose para rodar o MongoDB e também com o backup dos bancos de exemplo. Dê uma olhada no arquivo README. MongoDB Samples
Banco utilizado para executar os exemplos
Para esse artigo eu vou utilizar o banco sample_airbnb do repositório mongo-samples.
Exemplo básico
A maneira mais fácil para obter dados a partir do MongoDB é com o comando find:
db.getCollection('listingsAndReviews').find({})
O argumento está vazio por enquanto, mas com esse argumento nós poderemos filtar os resultados mais tarde.
Essa instrução irá obter todos os documentos com todos os campos da collection lisntingAndRevies.
Mas trazer todos os campos não é uma boa prática, porque nós geralmente só trazemos os campos que iremos precisar.
Podemos fazer isso projetando os campos desejados.
Selecione quais campos deverão ser obtidos
Nós podemos adicionar um segundo parâmetro para definir quais são os campos desejados.
db.getCollection('listingsAndReviews').find({}, { bedrooms: 1, accommodates: 1, bed_type: 1, beds: 1 })
+--------+------------+--------+--------+----+
|_id |accommodates|bed_type|bedrooms|beds|
+--------+------------+--------+--------+----+
|10006546|8 |Real Bed|3 |5 |
|10009999|4 |Real Bed|1 |2 |
|1001265 |2 |Real Bed|1 |1 |
|10021707|1 |Real Bed|1 |1 |
|10030955|2 |Real Bed|1 |1 |
+--------+------------+--------+--------+----+
Como você pode ver o campo _id is retornado automaticamente, se você quiser evitar esse comportamento você precisa definir _id para 0 dessa forma:
db.getCollection('listingsAndReviews').find({}, { _id: 0, bedrooms: 1, accommodates: 1, bed_type: 1, beds: 1 })
+------------+--------+--------+----+
|accommodates|bed_type|bedrooms|beds|
+------------+--------+--------+----+
|8 |Real Bed|3 |5 |
|4 |Real Bed|1 |2 |
|2 |Real Bed|1 |1 |
+------------+--------+--------+----+
Se você quiser filtrar os dados, você pode criar um filtro dessa forma:
db.getCollection('listingsAndReviews').find({ bed_type: 'Real Bed' }, { bedrooms: 1, accommodates: 1, bed_type: 1, beds: 1 })
+--------+------------+--------+--------+----+
|_id |accommodates|bed_type|bedrooms|beds|
+--------+------------+--------+--------+----+
|10006546|8 |Real Bed|3 |5 |
|10009999|4 |Real Bed|1 |2 |
|1001265 |2 |Real Bed|1 |1 |
|10021707|1 |Real Bed|1 |1 |
|10030955|2 |Real Bed|1 |1 |
+--------+------------+--------+--------+----+
Com essa instrução o MongoDB só irá retornar os documentos o campo bed_type seja igual a Real Bed.
Operadores de comparação
Para operações mais elaboradas existem alguns operadores de comparação que podemos utilizar com o comando find, como:
$eq (equal)
$ne (not equal)
$gt (greater than)
$gte (greater than or equal)
$lt (lower than)
$lte (lower than or equal)
$in (in)
$nin (not in)
Adicionando uma condição
Nós podemos adicionar uma condição ao comando find para trazer somente documentos com o campo beds maior que 1.
db.getCollection('listingsAndReviews').find({ beds: { $gt: 1 } }, { beds: 1 })
+--------+----+
|_id |beds|
+--------+----+
|10006546|5 |
|10009999|2 |
|10038496|3 |
|10047964|6 |
|10051164|8 |
|10057447|2 |
+--------+----+
Agora somente documentos onde o campo beds é maior ou igual a 1.
db.getCollection('listingsAndReviews').find({ beds: { $gte: 1 } }, { beds: 1 })
+--------+----+
|_id |beds|
+--------+----+
|10006546|5 |
|10009999|2 |
|1001265 |1 |
|10021707|1 |
|10030955|1 |
|1003530 |1 |
+--------+----+
Operadores Lógicos
Para criar filtros mais complexos você pode utilizar logical query operators.
$and (all criteria must be true)
$or (at least one criteria must be true)
$nor (none of the criteria can be true)
$not (inverts the condition)
Definindo um intervalo para o número de camas:
db.getCollection('listingsAndReviews').find({ $and: [
{ beds: { $gte: 1 } },
{ beds: { $lte: 3 } }
] }, { beds: 1 })
+--------+----+
|_id |beds|
+--------+----+
|10009999|2 |
|1001265 |1 |
|10021707|1 |
|10030955|1 |
|1003530 |1 |
|10038496|3 |
|10057447|2 |
+--------+----+
Ordenando o resultado
Geralmente nós precisamos ordenar o resultado.
Para orderar o resultado você só precisa adicionar a função sort à função find.
Use 1 para ordenar de forma ascendente.
db.getCollection('listingsAndReviews').find({ $and: [
{ beds: { $gte: 1 } },
{ beds: { $lte: 3 } }
] }, { beds: 1 }).sort({ beds: 1 })
+--------+----+
|_id |beds|
+--------+----+
|26488387|1 |
|26361065|1 |
|26365414|1 |
|26366454|1 |
|26380958|1 |
|26391176|1 |
+--------+----+
Use -1 para ordenar de forma descendente
db.getCollection('listingsAndReviews').find({ $and: [
{ beds: { $gte: 1 } },
{ beds: { $lte: 3 } }
] }, { beds: 1 }).sort({ beds: -1 })
+--------+----+
|_id |beds|
+--------+----+
|4593396 |3 |
|1912695 |3 |
|4458669 |3 |
|19080306|3 |
|4515942 |3 |
+--------+----+
Paginando o resultado
Da mesma forma que é feito em bancos relacionais, você pode utilizar as funções skip, limit e sort para paginar o resultado.
db.getCollection('listingsAndReviews').find({ $and: [
{ beds: { $gte: 1 } },
{ beds: { $lte: 3 } }
] }, { beds: 1 }).sort({ beds: -1 }).skip(10).limit(3)
+--------+----+
|_id |beds|
+--------+----+
|10628126|3 |
|102995 |3 |
|10038496|3 |
+--------+----+
Filtrando através de um campo array
Se você precisa filtrar os documentos que tenham pelo menos um valor dentro de um campos do tipo array, faça dessa forma:
db.getCollection('listingsAndReviews').find({ amenities: "Wifi" }, { amenities: 1 })
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|_id |amenities |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|10006546|["TV", "Cable TV", "Wifi", "Kitchen", "Paid parking off premises", "Smoking allowed", "Pets allowed", "Buzzer/wireless intercom", "Heating", "Family/kid friendly", "Washer", "First aid kit", "Fire extinguisher", "Essentials", "Hangers", "Hair dryer", "Iron", "Pack ’n Play/travel crib", "Room-darkening shades", "Hot water", "Bed linens", "Extra pillows and blankets", "Microwave", "Coffee maker", "Refrigerator", "Dishwasher", "Dishes and silverware", "Cooking basics", "Oven", "Stove", "Cleaning before checkout", "Waterfront"] |
|10009999|["Wifi", "Wheelchair accessible", "Kitchen", "Free parking on premises", "Smoking allowed", "Hot tub", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "First aid kit", "Essentials", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace"] |
|1001265 |["TV", "Cable TV", "Wifi", "Air conditioning", "Pool", "Kitchen", "Free parking on premises", "Elevator", "Hot tub", "Washer", "Dryer", "Essentials", "Shampoo", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace", "Self check-in", "Lockbox", "Hot water", "Bed linens", "Extra pillows and blankets", "Ethernet connection", "Microwave", "Coffee maker", "Refrigerator", "Dishes and silverware", "Cooking basics", "Stove", "BBQ grill", "Garden or backyard", "Well-lit path to entrance", "Disabled parking spot", "Step-free access", "Wide clearance to bed", "Step-free access"]|
|10021707|["Internet", "Wifi", "Air conditioning", "Kitchen", "Buzzer/wireless intercom", "Heating", "Smoke detector", "Carbon monoxide detector", "Essentials", "Lock on bedroom door"] |
|10030955|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Pool", "Kitchen", "Free parking on premises", "Doorman", "Gym", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "Essentials", "24-hour check-in"] |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Se você precisa filtar documentos que possuam todos os valores passados como parâmetro dentro de um campo do tipo array, faça dessa forma:
db.getCollection('listingsAndReviews').find({ amenities: { $all: [ "Wifi", "Wheelchair accessible", "Elevator" ] } }, { amenities: 1 })
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|_id |amenities |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|10069642|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Wheelchair accessible", "Kitchen", "Free parking on premises", "Doorman", "Gym", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "Essentials", "Hair dryer", "Iron"] |
|10082307|["TV", "Internet", "Wifi", "Air conditioning", "Wheelchair accessible", "Doorman", "Elevator", "Family/kid friendly", "Shampoo", "Hangers", "Hair dryer", "Iron"] |
|10115921|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Wheelchair accessible", "Kitchen", "Paid parking off premises", "Smoking allowed", "Doorman", "Elevator", "Buzzer/wireless intercom", "Heating", "Family/kid friendly", "Suitable for events", "Dryer", "Smoke detector", "Carbon monoxide detector", "First aid kit", "Safety card", "Fire extinguisher", "Essentials", "Shampoo", "24-hour check-in", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace", "Self check-in", "Building staff", "Crib", "Hot water", "Luggage dropoff allowed", "Long term stays allowed"]|
|10116256|["TV", "Internet", "Wifi", "Air conditioning", "Wheelchair accessible", "Kitchen", "Paid parking off premises", "Smoking allowed", "Doorman", "Elevator", "Buzzer/wireless intercom", "Heating", "Family/kid friendly", "Suitable for events", "Washer", "Dryer", "Smoke detector", "Carbon monoxide detector", "Fire extinguisher", "Essentials", "Shampoo", "24-hour check-in", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace", "Self check-in", "Building staff", "Hot water", "Luggage dropoff allowed", "Long term stays allowed"] |
|10116578|["Cable TV", "Wifi", "Air conditioning", "Wheelchair accessible", "Kitchen", "Free parking on premises", "Doorman", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "24-hour check-in", "Iron"] |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Campos aninhados
Se você precisa filtrar campos aninhados, faça dessa forma:
db.getCollection('listingsAndReviews').find({ "address.street": "Rio de Janeiro, Rio de Janeiro, Brazil" }, { amenities: 1 })
+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|_id |amenities |
+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|10009999|["Wifi", "Wheelchair accessible", "Kitchen", "Free parking on premises", "Smoking allowed", "Hot tub", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "First aid kit", "Essentials", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace"] |
|10030955|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Pool", "Kitchen", "Free parking on premises", "Doorman", "Gym", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "Essentials", "24-hour check-in"] |
|10038496|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Kitchen", "Paid parking off premises", "Smoking allowed", "Doorman", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "Fire extinguisher", "Essentials", "Hangers", "Hair dryer", "Iron", "Laptop friendly workspace", "Hot water", "Bed linens", "Extra pillows and blankets", "Microwave", "Coffee maker", "Refrigerator", "Dishes and silverware", "Oven", "Stove", "Long term stays allowed", "Wide hallway clearance", "Host greets you"]|
|10051164|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Kitchen", "Smoking allowed", "Family/kid friendly", "First aid kit", "Fire extinguisher", "Essentials"] |
|10069642|["TV", "Cable TV", "Internet", "Wifi", "Air conditioning", "Wheelchair accessible", "Kitchen", "Free parking on premises", "Doorman", "Gym", "Elevator", "Buzzer/wireless intercom", "Family/kid friendly", "Washer", "Essentials", "Hair dryer", "Iron"] |
+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Verificando se um campo existe
Como no MongoDB não temos um modelo fixo, alguns documentos podem ter alguns campos, e outros documentos podem ter campos diferentes. Então se você precisar filtrar documentos baseado na condição de ele possuir um campo específico, você pode fazer isso:
db.getCollection('listingsAndReviews').find({ transit: { $exists: true } }, { transit: 1 })
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|_id |transit |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|10006546|Transport: • Metro station and S. Bento railway 5min; • Bus stop a 50 meters; • Lift Guindais (Funicular) 50 meters; • Tuc Tuc-to get around the city; • Buses tourist; • Cycling through the marginal drive; • Cable car in Gaia, overlooking the Port (just cross the bridge).|
|10009999|Easy access to transport (bus, taxi, car) and easy free parking around. Very close to Gávea, Leblon, Ipanema, Copacabana and Botafogo. |
|1001265 |Honolulu does have a very good air conditioned bus system. |
|10021707| |
|10030955| |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+