DEV Community

Cover image for Learn MongoDB: Query Documents - II
Paras 🧙‍♂️
Paras 🧙‍♂️

Posted on • Updated on

Learn MongoDB: Query Documents - II

Continuing the Query Documents - I post, let's complete the remaining operations. In previous post, I covered simple queries and querying embedded documents with several kinds of operators that are available in mongodb. In this post, operators related to arrays and evaluation of data are discussed.


Table of Content:


New Document Structure in pokemons collection:

Instead of single weakness, now we will keep an array of weaknesses for a pokemon.

{
   name: "pikachu",
   type: "Electric",
   stats: {
     health: 40,
     attack: 50,
     defense: 45
   },
   level: 16,
   weakness: ["ground", "grass", "dragon"], // now it's array
   evolution: "raichu",
   moves: [
      {name: "quick attack", dmg: 40},
      {name: "thunder bolt", dmg: 90},
      {name: "irontail", dmg: 50}
   ]   
}
Enter fullscreen mode Exit fullscreen mode

Evaluation Query Operators

There is nothing much to say about evaluation query operators. They just do evaluation.

  • $regex (allows you to specify regex expression in your query)
  • $text (useful when working with indexes in mongodb, therefore not covered now)
  • $jsonSchema (For validating data against a schema)
  • $expr
  • $mod ( { field: { $mod: [ divisor, remainder ] } })

Usage:

# $regex to find pokemons with "pi" in their name
> db.persons.find({ description: {$regex: /pi/}})

# create a validator when creating collection
# let's create a simple schema for pokemon collection
> db.createCollection( <collection>, { validator: { 
    required: ["name", "type", "level"],
   $jsonSchema: {
      name: { bsonType: "string" },
      type: { bsonType: "string" },
      level: { bsonType: "int", minimum: 1 }
   }
}})

# $mod operators helps you perform mod operation on a field against a number
# get all pokemons whose level are divisible by 5
> db.pokemons.find({level: {$mod: [5, 0]}}) # divisor, remainder
Enter fullscreen mode Exit fullscreen mode

$expr : It is related to aggregation. It helps us to use aggregation expressions within the query.

Note: There are some operations for which we need some knowledge of aggregation. Therefore, we will cover the relevant part for now and rest will be covered later.

  • Comparing fields: You can use $expr operator to compare two fields. E.g. we can compare level and defense field in our pokemons collection (weird comparision, but will teach you about embedded field)
# get pokemons whose level are greater than their defense
> db.pokemons.find({$expr: {$gt: ["$level", "$stats.defense"] }})
Enter fullscreen mode Exit fullscreen mode

As you can see, you need quotes and $ sign when specifying the field in $expr

E.g. "$fieldName", "$fieldName.nestedFieldName"

There is another operator $cond that helps us in creating conditions (if, then, else). We will learn it later in the series.

Querying Arrays

Arrays are here !! Now we will learn how to handle arrays inside documents.

Simple query for an element in array : You can query arrays normally and mongo will take care of the rest. It will check if the value exists in array or not.

# find pokemons with weakness "ground"
> db.pokemons.find({weakness: "ground"})

# if you do something like this, it will check for exact match for array
> db.pokemons.find({weakness: ["ground"]})
Enter fullscreen mode Exit fullscreen mode

Querying an index in array : It is possible to query for a specific index in array. Just specify the index similar to specifying a key in object.

> db.pokemons.find({"weakness.2": "electric" })
Enter fullscreen mode Exit fullscreen mode

What if we have embedded docs inside array?? (Hint: similar to embedded docs)

# find pokemons who have "quick attack" move
> db.pokemons.find({"moves.name": "quick attack"})
Enter fullscreen mode Exit fullscreen mode

This can get tricky when document structure gets more complicated. In our above example, we only looked for a single key. Let's see some more examples that can get us in trouble. We will use a different document structure instead of our pokemons one.

// e.g collection = articles
{
   comments: [
      { name: "Nina", rating: 3 },
      { name: "Colt", rating: 4 },
      { name: "Rico", rating: 2.5}
   ]
}
Enter fullscreen mode Exit fullscreen mode
# find articles where nina's has given rating, greater than 3

# this won't work, it will look for exact match
> db.articles.find({"comments": { name: "Nina", rating: {$gt: 2}}})

# this won't work either
# name can match different comment than the rating
> db.articles.find({"comments.name": "Nina", "comments.rating": {$gt: 2}})
Enter fullscreen mode Exit fullscreen mode

We will solve this problem in later section, where we will learn about operators.

Array Operators

Array operators are few but very useful.

  • Operators: $all, $size, $elemMatch

$all : To understand it, let's form a case. Find all pokemons whose weaknesses are ground and electric.

> db.pokemons.find({weakness: {$all: ["ground", "electric"]}})

# Don't try the following query because 
# it will look for exact match + order 
> db.pokemons.find({weakness: ["ground", "electric"]})
Enter fullscreen mode Exit fullscreen mode

$all makes sure it finds all elements in array. They can be objects, single values or arrays. It also ignores the order in which you specified elements in query.

$size : Simple...let's you query the size of array.

# find pokemons with 4 moves
> db.pokemons.find({moves: {$size: 4}})
Enter fullscreen mode Exit fullscreen mode

$size operator only accepts exact number. Greater than or less than queries are not possible.

$elemMatch : The operator we need is here !!!...Just kidding. Do you remember the unsolved mystery we just had when querying embedded documents in array ? $elemMatch can help us with that !! Let's see how

// e.g collection = articles
{
   comments: [
      { name: "Nina", rating: 3 },
      { name: "Colt", rating: 4 },
      { name: "Rico", rating: 2.5}
   ]
}
Enter fullscreen mode Exit fullscreen mode
# find articles where Nina has given a rating greater than 2
> db.articles.find({
   comments: {
      $elemMatch: {name: "Nina", rating: {$gt: 2}}
   }
})
Enter fullscreen mode Exit fullscreen mode

What just happened in that query ??

$elemMatch helped us in defining how the document should look like, that can match our query. Cool, isn't it ?


So, that was it for querying arrays and embedded docs. If you have any question, feel free to ask in comments. I will try my best to answer them.

Hope you find this useful !

Next Post : Update Documents

Prev Post : Query Documents - Part I

Discussion (0)