|
|
|
# Lab 4: Introduction to MongoDB and Comparison
|
|
|
|
|
|
|
|
MongoDB is a document-based database management system. It fits into the large group of 'NOSQL' (`Not Only SQL'), non-relational database technologies.
|
|
|
|
|
|
|
|
This lab session shows you how to perform some basic CRUD (Create Read Update Delete) operations from the JS mongo interactive shell on Igor.
|
|
|
|
|
|
|
|
If you cannot access mongo for some reason, there is also a **video demonstration on the VLE**, under week 5. **Please note the video demo has some minor differences compared to what you will encounter on Igor, namely in relation to the login procedure, and there are also some prohibited operations on Igor.**
|
|
|
|
|
|
|
|
## Learning Aims
|
|
|
|
|
|
|
|
In this lab you will learn:
|
|
|
|
|
|
|
|
+ How to connect to the mongodb interactive shell on Igor
|
|
|
|
+ How to create a collection
|
|
|
|
+ How to insert documents in a collection
|
|
|
|
+ How to retrieve documents based on some search criteria
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
You should work in **pairs** for this exercise. You will be attempting to \`port' the Flucks backend from MySQL to MongoDB. In other words, to reimplement the database as a MongoDB database.
|
|
|
|
|
|
|
|
Working together with your partner, you will need to consider:
|
|
|
|
|
|
|
|
+ What collections you will create to store similar objects in the database
|
|
|
|
+ What key-value pairs will be defined within a document in a given collection (i.e. how will the documents be structured?)
|
|
|
|
+ How easy was it to complete task 7, compared with a similar operation you did on the MySQL database last week?
|
|
|
|
+ Given the type of operations you might want to perform on data in the Flucks database, which do you think would be more suitable, and why?
|
|
|
|
|
|
|
|
For the last objective, you can use your creativity to \`imagine' what other functionality the app might eventually have, or what kinds of analysis you might want to do on the Flucks data...when you argue your case on the forum (task 9), you may make reference to these \`imagined' scenarios.
|
|
|
|
|
|
|
|
**Note:** You are not expected to port the entire database! Just designing and inserting documents in one or two collections will be sufficient for you to get a feel for this system in relation to MySQL.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 1: Start the mongo interactive shell
|
|
|
|
|
|
|
|
+ Connect to Igor:
|
|
|
|
|
|
|
|
ssh USERNAME@doc.gold.ac.uk
|
|
|
|
+ Start the mongo shell on Igor:
|
|
|
|
|
|
|
|
mongo -u USERNAME -p USERNAME USERNAME
|
|
|
|
|
|
|
|
This will start an instance of the mongoDB daemon (mongod), and open up the JS interactive mongo shell.
|
|
|
|
|
|
|
|
The first option supplied here says to connect as a specific user (i.e. you); the second option says to prompt for a password (which is also your username); and the last option supplied is the database name. All of you have a database set up whereby the name matches your username.
|
|
|
|
|
|
|
|
If you were to communicate with a mongo database using an API (which you would do if you were programming a web-based application in a server-side scripting language), the commands would be similar to those shown here, but not identical.
|
|
|
|
|
|
|
|
If starting a local instance of mongo (i.e. on a local installation, not on Igor), you can probably just do:
|
|
|
|
|
|
|
|
mongo
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 2: Create a new database (**Local installations only**)
|
|
|
|
|
|
|
|
**Note: This action is PROHIBITED ON IGOR.** You can only do this task if on a local instance of mongo. There is no requirement for you to install mongodb locally, so skip this task if it does not apply to you.
|
|
|
|
|
|
|
|
+ Just like in MySQL, the first thing you have to do when you start mongo, is tell it which databse you want to work on. To see what databases are available, you can type:
|
|
|
|
|
|
|
|
show dbs
|
|
|
|
+ To switch to another database, type:
|
|
|
|
|
|
|
|
use NAMEOFDATABASE
|
|
|
|
If the database you want to use does not exist, it will be created automatically.
|
|
|
|
|
|
|
|
The current database will be available via the `db` object variable.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 3a: Plan your schema
|
|
|
|
In this task you should work with your partner to determine what **objects** should be represented in the database, and in what **collections** these objects will be housed (remember: an instance of an object will be represented by a single document).
|
|
|
|
|
|
|
|
You should then decide what fields (key-value pairs) will be defined in each object (you can just list them at this stage without worrying about the code).
|
|
|
|
|
|
|
|
**Note:** This does not necessarily mean 4 collections with a direct table -> collection mapping...you may decide to remodel the data completely!
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 3b: Define a document structure
|
|
|
|
|
|
|
|
A document-based database such as mongodb is consistent with the object oriented programming paradigm, in a way that a relational database is not. In a relational database, the concept of objects is just that: a concept to help us model the data more easily. Objects don't really exist in a relational database.
|
|
|
|
|
|
|
|
In a document-based database however, each **document** represents a single **object**. The **key-value pairs** defined inside it represent the **properties** of the object.
|
|
|
|
|
|
|
|
In Mongodb, documents are compatible with **JavaScript Object Notation (JSON)**.
|
|
|
|
|
|
|
|
+ Referring to your Task 3a solution, adapt/remodel some of the dummy data so that it fits the JSON format. For example, here is an example definition of an **Account** in JSON format:
|
|
|
|
|
|
|
|
var Account = {
|
|
|
|
_id: ObjectId(542c2b97bac0595474108b48),
|
|
|
|
username: "jgloo011",
|
|
|
|
email: "jgloo@hotmail.com",
|
|
|
|
password: "$2a$12$OEo1/8zIIyt5rwQn4TzizOJYppoUjYFRJptaq43VUJx.Rwn5llSXa",
|
|
|
|
name: {
|
|
|
|
first: "Janet",
|
|
|
|
last: "Gloob"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
You could do this on paper or in a text file first! **Note:** the \_id field uniquely identifies every document in a Mongo database, and it gets created automatically when an object is inserted.
|
|
|
|
|
|
|
|
You need only **do this for one or two of your collections**, as you may not have time to do it for the entire schema.
|
|
|
|
|
|
|
|
Notice from the above that it is possible to nest objects and arrays within object properties.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 4: Create a document collection in mongodb
|
|
|
|
|
|
|
|
In mongodb, related documents can be stored together in a **collection**.
|
|
|
|
|
|
|
|
+ To create the document for real, use the **insert()** collection method. This inserts a new document in a specific collection. **If the collection does not already exist, it is automatically created.**
|
|
|
|
|
|
|
|
In the following example, I create a collection called 'Account', and insert in it a single document:
|
|
|
|
|
|
|
|
db.accounts.insert({firstname: "Janet", lastname: "Gloob", username: "jgloo011", email: "jgloo@hotmail.com", password: "$2a$12$OEo1/8zIIyt5rwQn4TzizOJYppoUjYFRJptaq43VUJx.Rwn5llSXa"})
|
|
|
|
|
|
|
|
The document is automatically given a unique object id. The object id is the value associated with the `_id` key.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 5: Retrieve the document you just inserted!
|
|
|
|
|
|
|
|
+ To check the document was created and to see its `_id`, you can call the **find()** method on the collection:
|
|
|
|
|
|
|
|
db.accounts.find()
|
|
|
|
|
|
|
|
**find()** will return all the documents in a collection. In this case, there was only one document to return.
|
|
|
|
+ To return only documents that meet a certain criteria, you can pass criteria to the find() method like so:
|
|
|
|
|
|
|
|
db.accounts.find({ username: "jgloo011" })
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 5: Add more documents!
|
|
|
|
|
|
|
|
+ Add a few more documents to your collection (**hint:** use 'up' on your keyboard to retrieve your previous insert command, and then modify it slightly)
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 6: Try a more interesting query
|
|
|
|
|
|
|
|
+ Have a look at the [mongo documentation](https://docs.mongodb.com/v3.4/reference/method/db.collection.find/) for some inspiration
|
|
|
|
+ For example, you could query using an **operator**, or you could apply a **cursor modifier** such as **sort()** or **limit()** to alter the execution of the query.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 7: Delete a document
|
|
|
|
|
|
|
|
+ In mongo, documents can be removed using he **remove()** method. Criteria can be passed to remove(), just as for find():
|
|
|
|
|
|
|
|
db.accounts.remove({username: "jgloo011"})
|
|
|
|
The above will remove all documents in the accounts collection with a username of \`jgloo011'.
|
|
|
|
Alternatively, you could remove a specific document by `_id`:
|
|
|
|
|
|
|
|
db.accounts.remove({_id:ObjectId("5847121f0c40b18bec6d4ccd")})
|
|
|
|
|
|
|
|
**If no criteria are specified, remove() will remove all the documents in a collection!**
|
|
|
|
|
|
|
|
It can sometimes be preferable to use the **removeOne()** method, which will remove only the first document that is found which matches the criteria.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 8: Update a document
|
|
|
|
|
|
|
|
Documents can be altered using the **update()** collection method. This method expects a **query** argument (to tell mongo what objects should be affected), an **update** argument (to tell it what changes to make), and an optional **options** argument (alters how the query is executed).
|
|
|
|
|
|
|
|
+ In the following example, the multi option has been set to true, to ensure the changes are applied to **all** documents that match the query criteria (i.e. to all documents with a specific image url).
|
|
|
|
|
|
|
|
db.flucks.update({img_url: "https://pbs.twimg.com/media/DNZ844zVoAA0z_x.jpg"},{$set: {img_url:"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSdolGLviByI1PEjzD8jQfDwYRURYRSxwOWeUgLTHoYWP79ezXW"}},{multi: true})
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Extension: Model relations
|
|
|
|
|
|
|
|
**There are no 'logical relations' between objects in a MongoDB database.** In your relational database, you modelled logical relations by applying foreign key constraints; MongoDB has no concept of foreign keys.
|
|
|
|
|
|
|
|
To create relations between objects in a MongoDB database, you must **explicitly reference documents (objects) within other documents.**
|
|
|
|
|
|
|
|
+ Create a second collection of related documents (for example, if you just created an \`accounts' collection, you might now create a collection to house \`flucks' or similar).
|
|
|
|
+ Try referencing a document from one collection, inside a document from another collection. For example, here I reference a document in the **accounts** collection within a document in the **flucks** collection:
|
|
|
|
|
|
|
|
db.flucks.insert({
|
|
|
|
timestamp: new Timestamp(),
|
|
|
|
img_url: "https://pbs.twimg.com/media/DNZ844zVoAA0z_x.jpg",
|
|
|
|
account: {
|
|
|
|
"$ref" : "accounts",
|
|
|
|
"$id" : ObjectId("5126bc054aed4daf9e2ab772"),
|
|
|
|
"$db" : "USERNAME"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
**Note:** the `$db` field specifies a database name. If on Igor, this will always be your username.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
## Task 9: Create a case for or against porting Flucks to MongoDB
|
|
|
|
|
|
|
|
Under week 5 on the VLE there is a forum setup for you to state and respond to arguments for or against porting Flucks database to MySQL.
|
|
|
|
|
|
|
|
+ Decide where you stand on this issue, and come up with a rational argument to support your position.
|
|
|
|
+ Refer to information you have been given about Relational and Non-Relational databases, or things you have found out about them for yourself! The Lecture 5 slides have been published already to help you+ Don't be afraid to use your imagination to \`imagine' what directions the Flucks application might take, in terms of future functionality and user-base. |