What is MongoDB?

MongoDB is a document-oriented database that stores documents in a JSON-like format (this format is known as BSON, for binary JSON). A database is an organized collection of data. Differently from a file, where data is stored in the order of writing, in a database the stored data can be organized in tables (like in relational databases) and retrieved at request (without reading the whole database). Traditional relational databases use the SQL language to store and access the stored information. You can learn about such databases if you take CS 304 Databases with Web Interfaces. MongoDB belongs to the family of NoSQL databases, meaning that it doesn't use the same language and principles found in the relational databases. NoSQL databases are used in real-time web applications and with big data, because they make certain operations faster than the traditional databases. There is a big debate among software developers on this issue. It is good if you learn both technologies (they are both widely used) and then reflect about advantanges and disadvantages of each, so that you can know where they are most useful.

Our reason for learning MongoDB in this class is that it is used by Meteor, the real-time JavaScript web application framework that we will start using soon.

Using MongoDB

Given that the only reason for learning MongoDB is to use it with Meteor, we will not be installing MongoDB separately. When we'll install Meteor, MongoDB will be part of it and Meteor will take care of the details of setting it up for our applications.

MongoDB has two parts: the MongoDB server and the MongoDB client. The MongoDB server is the part of MongoDB where the data are stored and queried, the MongoDB client is an application that allows us to enter data or query the chosen database. Our CS server, Tempest, has the MongoDB installed, where the MongoDB server runs uninterruptibly and we will simply run the MongoDB client from the command line.

To do that, you'll remotely connect to CS server using your credentials. The command you'll need is ssh (stands for secure shell) together with your account name on the CS server, for example ssh eni@cs.wellesley.edu. You'll be prompted for your password. After entering it, you can type mongo and start the work in the client shell.

You start the ssh command on your own local machine, then, once connected to the server, the mongo command is being executed on the server, where the MongoDB is installed.

Mac machines already have ssh installed, which you can start from a Terminal window. If you have a Windows machine, you will need to install an application such as PuTTY and learn how to connect to the CS server, or simply use our Mac machines in SCI 257.

Terminology

We will be using certain terms when talking about MongoDB and the data stored in it. Here is an overview:

  1. A MongoDB server can contain multiple databases. A database is an independent container for data. Each database can contain multiple collections and a collection can contain multiple documents.
  2. A collection is a collection of documents (objects). It corresponds to a table in relational databases.
  3. A document is a single record in a collection. Typically all documents in a collection have the same structure, although it is not strictly necessary. A document correspond to a row of the table in relational databases.
  4. A field is the constituting unit of a document. For a document to exist, it needs at least a field. Fields are like an object's properties. For example, a person's name or its age. The purpose of the document is to store different values for such fields. Fields are similar to columns in relational databases.
// Examples of documents that can be stored in a collection about HP book characters
{"name": "Harry Potter", "age": 17}
{"name": "Ron Weasley", "age": 17, "house": "Gryffindor"}
{"name": "Severus Snape", "age": 38, "profession": "Teacher", "alive": false}       

The example above shows different documents that can be stored in the same collection. However, each document can have more fields, depending on the need.

Inserting Documents

Note: the following instructions can be entered in the mongo shell you opened in the CS server.

The server will have several databases, therefore, our first step will be to choose the database we want to use. It doesn't matter whether a database exists or not, once we enter the instruction: use database_name_here, we will be working with that database. Once we have specified the database name, we can refer to it by simply prefixing all other instructions with db. For example, in the screenshot below the following is happening:

Working with a database on the mongo shell.
  1. On step 1, we specify the name of the database.
  2. On step 2, we refer to a collection named "sisters" (this is not created yet, but we don't get an error).
  3. On step 3, we insert a new document to this collection. At this time, the database, the collection and the document will be created. This is why this operation is a bit slow. If we keep adding new documents to this collection now, the response is much faster.

I added one more document: db.sisters.insert({'name': 'Kate'}). Now, we can try to see what is stored in the collection, by using the method .find(). The result is shown in the screenshot below. You can notice that two documents were found, and both of them have an additional field, "_id", which was automatically added when the documents were entered in the database. Think of this ID as a unique way to identify each document. In some cases, we might want to use values that we know as the "_id" field (for example the Banner number of a student), but most of the time, we will not care about its value.

Finding documents in a collection.

If in certain occasions we are only interested to see one document in a collection, we can use the method .findOne() instead of .find(). Try it out.

Test your knowledge

At this point make sure that you can do the following:

  1. Choose a new database (e.g. yournameTest).
  2. Insert a few documents to a collection of this database (your documents can have a different number of fields, if you want).
  3. Find all documents in a collection.
  4. Find one document in the collection.

Querying with Selectors

Just like in CSS & Javascript where we use "selectors" to find certain DOM elements, mongodb has the same concept known as "query selectors". A query selector will be a JSON object, including the empty object {}.

In order to test query selectors in MongoDB, I have inserted the following 12 documents to the collection unicorns in the cs249test database.

{name: 'Horny', dob: new Date(1992,2,13,7,47), loves: ['carrot','papaya'], weight: 600, gender: 'm', vampires: 63}  
{name: 'Aurora', dob: new Date(1991,0,24,13,0), loves: ['carrot','grape'], weight: 450, gender: 'f', vampires: 43}
{name: 'Unicrom', dob: new Date(1973,1,9,22,10), loves: ['energon','redbull'], weight: 984, gender: 'm', vampires: 182}
{name: 'Roooooodles', dob: new Date(1979,7,18,18,44), loves: ['apple'], weight: 575, gender: 'm', vampires: 99}
{name: 'Solnara', dob: new Date(1985,6,4,2,1), loves: ['apple', 'carrot','chocolate'], weight: 550, gender: 'f', vampires: 80}
{name: 'Aynaa', dob: new Date(1998,2,7,8,30), loves: ['strawberry','lemon'], weight: 733, gender: 'f', vampires: 40}
{name: 'Kenny', dob: new Date(1997,6,1,10,42), loves: ['grape', 'lemon'], weight: 690, gender: 'm', vampires: 39}
{name: 'Raleigh', dob: new Date(2005,4,3,0,57), loves: ['apple','sugar'], weight: 421, gender: 'm', vampires: 2}
{name: 'Leia', dob: new Date(2001,9,8,14,53), loves: ['apple','watermelon'], weight: 601, gender: 'f', vampires: 33}
{name: 'Pilot', dob: new Date(1997,2,1,5,3), loves: ['apple','watermelon'], weight: 650, gender: 'm', vampires: 54}
{name: 'Nimue', dob: new Date(1999,11,20,16,15), loves: ['grape','carrot'], weight: 540, gender: 'f'}
{name: 'Dunx', dob: new Date(1976,6,18,18,18), loves: ['grape','watermelon'], weight: 704, gender: 'm', vampires: 165}

To see them in the database, do the following:

use cs249test // change the database
db.unicorns.find() // show all 12 documents of the collection

Here are some things that we will try on the console: (solutions are at the end of the document)

Find all female unicorns. The query selector will be {gender: 'f'} and we can use that in the .find() method as an argument. You should be able to see five documents as a result, if you type db.unicorns.find({gender: 'f'}) in the mongo shell.

Your Turn: Find a unicorn named Aurora.

Relational Operators

The query selector that we just saw, {gender: 'f'} is trying to find all documents where a field has the desired value. Very often we want to find documents where the values is greater than or less than a value, or even not equal. We can use the five following relational operators to check for these cases:

  • $gt for "greater than"
  • $gte for "greater than or equal"
  • $lt for "less than"
  • $lte for "less than or equal"
  • $ne for "not equal"

The syntax for using these operators is shown in the example below:

db.unicorns.find({vampires: {$gt: 60}})

This query will return five unicorns who have fought more than 60 vampires.

Your turn:

  • write a query to find all male unicorns that weigh more than 550 lbs
  • write a query to find all unicorns that have fought at most 40 vampires
  • write a query to find all female unicorns that were born since 1999
  • So far, by using multiple fields in the query selector, we have been generating AND queries (all the fields need to be true). But sometimes, we will be interested in documents that fulfill at least one of the query parameters. For this, we can use the $or operator. This operator requires its value to be an array of fields separated by comma. Here is an example:

    db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {weight: {$lt : 500}}]})       
    

    This query selector says: find all female unicors who either love apples or weigh less than 500 lbs.

    Notice that for values such as 'apple' that are part of an array, we can still use them as separate values in the query selector. If we want to have more than one value, we have to list them separately:

    db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'grape'}, {weight: {$lt : 500}}]})       
    

    Another useful operator is $exists which can take the values true / false and will return documents that contain or not a certain field. For example, let's find documents that don't contain the field 'vampire'.

    db.unicorns.find({vampires: {$exists: false}})
    

    CRUD Functions

    The basic functions in a database are: create, read, update, and delete. In MongoDB, creating documents is performed through .insert(), reading (or retrieving) is done through .find(), updating is done with .update() and deleting is done with .drop(). We already saw .insert() and .find() and we will see examples of .update() and .drop() when we'll work with Meteor. For the moment, let's see some other operations that have to do mostly with how the results are shown to us, such as filtering fields, sorting, limiting or skipping, or counting.

    Projections

    You might have noticed so far that when we search for some documents, the entire document (all its fields and values) is displayed. Sometimes, we might be interested in seeing only one or a few fields. This is called a projection, and can be realized by supplying a second argument to the method .find().

    db.unicorns.find({gender: 'f'}, {name: 1, gender: 1})

    This will do what we want (showing only name and gender), but the "_id" field is shown as well, so, we can request to not show it:

    db.unicorns.find({gender: 'f'}, {name: 1, gender: 1, _id: 0})

    Thus, using fields with the value 1 will include them in the projection, and using a field with the value 0 will exclude them in the projection. Sometimes, you only want to exclude less fields than you want to include, thus, it makes sense to know about how to do that.

    Sorting

    We can sort elements by creating an object that has the field for sorting and a value of 1 or -1 for the order of sorting (ascending or descending). For example, let's sort the male unicorns by vampires count (decreasing):

    db.unicorns.find({gender: 'm'}, {name: 1, vampires: 1, gender: 1, _id: 0}).sort({vampires: -1})

    The sorting works for fields of different kinds, for example Date() objects too:

    db.unicorns.find({gender: 'f'}, {name: 1,  gender: 1, dob: 1, _id: 0}).sort({dob: 1})

    Limit and Skip

    We can limit the number of results we want to see by using the method .limit(). For example, we can only show the three oldest male unicorns:

    db.unicorns.find({gender: 'm'}, {gender: 1, dob: 1, name: 1, _id: 0}).sort({dob: 1}).limit(3)
     

    What is happening is that instead of the 7 male unicorns, only 3 are shown. In addition to limiting results, we can skip some of them, by using the method .skip(). For example, we can decide to skip the first three oldest males and show the rest of them:

    db.unicorns.find({gender: 'm'}, {gender: 1, dob: 1, name: 1, _id: 0}).sort({dob: 1}).skip(3

    Combining .limit() and .skip() will allow us to show documents grouped together (the first ten, the second ten, etc.)

    Counting

    Often, we are only interested in how many docoments fulfill some criteria. For example, if we want to know the total number of documents in the collection, we an write:

    db.unicorns.count()

    If we are interested in the number of documents found by a query selector:

    db.unicorns.find({gender: 'f'}).count()

    It is possible to use .count() instead of .find(), by using the query selector as its arguments:

    db.unicorns.count({gender: 'm', weight: {$gt: 600}})

    Solutions

    Unicorn named Aurora: db.unicorns.find({name: 'Aurora'})

    Male unicorns that weigh more than 550 lbs: db.unicorns.find({gender: 'm', weight: {$gt : 550}}).

    All unicorns that have fought at most 40 vampires: db.unicorns.find({vampires: {$lte: 40}})

    Female unicorns that were born since 1999: db.unicorns.find({gender: 'f', dob: {new Date(1999,0,1)}})

    Learn More

    These notes were based on the short book "The Little MongoDB book" by Karl Seguin.

    You can see the whole list of query selector operators in the MongodDB documentation.

    If you want to go in more depth with MongoDB, you should read the MongoDB Manual.