Basic commands
First install Mongodb, there are lots of ways to do this. I used Homebrew but I know some people suggest against it.
To get your mongo server (daemon) running, run:
mongod
To shutdown the service, run:
db.shutdownServer()
To connect to Mongo, open up a terminal and connect to the db by running:
mongo
Running mongo --help
shows you all the different parameters you can use. For example, using a particular db you can run mongo mydbname
. The default port is 27018 so you can change that by mongo --port 7000
.
Now that you are in you may want to know what databases you have, so very similar to a lot of RDMS out there you can run show databases
and use database
:
show databases;
use chefs;
Mongo has collections for data, so you can run show collections
. show tables
also works the same.
Inserting a record
Having something in the database is a good start.
db.collection.insert({ name: "bob" });
or in version 3.2:
db.collection.insertOne({ name: "bob" });
Inserting multiple records
The insertMany method allows us to pass in an array of objects to be inserted. It inserts records linearly.
db.collection.insertMany([
{ name: "bob" },
{ name: "jim" },
{ name: "mary" }
]);
One thing to note about the above is that if any of the objects cause an error, the records after will not be inserted. You can add a second param specifying the order to be false. In this way anything that doesn't cause an error will be inserted.
db.collection.insertMany([
{ name: "bob" },
{ name: "jim" },
{ name: "mary" }
], { ordered: false });
Running Queries
To query collections using find
, you can either specify an empty object which will return all records:
db.chefs.find().pretty();
You can specify attributes to search on (multiple attributes act like AND):
db.chefs.find({"style": "sioux" });
You can also count the number of records by using count
.
db.chefs.find().count(); //returns a number e.g. 8
You can also chain commands such as limit
to restrict the number of results (the pretty
function converts the result into a human readable form):
db.records.find({ published : true }).limit(5).pretty();
Finding records based on some child object attribute is easy too:
db.chefs.find({"skills.cuisine": "French" });
Searching in Arrays is also easy. The below would match a user if their restaurant is only Bob's Kitchen:
db.chefs.find({"restaurants": ["Bob's Kitchen"] });
This would not match:
db.chefs.find({"restaurants": ["Bob's Kitchen", "Kim's Bistro"] });
But this would:
db.chefs.find({"restaurants": "Bob's Kitchen" });
Another caveat is that using brackets for arrays is an exact comparison. If the document had ["Kim's Bistro", "Bob's Kitchen"]
the query below would not return any results:
db.chefs.find({"restaurants": ["Bob's Kitchen", "Kim's Bistro"] });
Just like the dot notation on an object, we can also target elements in the array using their index:
db.chefs.find({"restaurants.0": "Bob's Kitchen" });
Mongo _id
You'll notice from running any of the above that a _id
is created for each record. If you don't specify a _id
Mongo will create one for you.
Mongo stores record primary keys ("_id") in the BSON format. The primary key type is ObjectId, that is either generated by the application driver or by the mongod
service.
In the event the driver fails to provide a _id field with a unique ObjectId, the mongod service will add it automatically using:
- A 4-byte value representing the seconds since the Unix epoch
- A 3-byte machine identifier (MAC Address)
- A 2-byte process ID
- A 3-byte counter, starting with a random value
Cursors
The point of a cursor is not to load in all the contents of a query, as in using the toArray function. The cursor sets up the query, waiting for functions to be run against it, such as the forEach function. The forEach looks very much like the forEach in Functional Reactive programming, and it acts very much the same - it streams results.
Since the Mongo shell is a Javascript interpreter you can use it for things such as cursors. If we don't assign the value returned from find
then you get a cursor which iterate through records up to 20 times, triggered by typing "it".
let c = db.collection.find();
c.hasNext(); //true or false
c.next(); //displays next records
c.objsLeftInBatch(); //displays number of records left
Projection
Projection is just like the real world projection: you can let through only the things that you want. By default, when querying Mongo it returns all fields in results. If you only need certain fields then run the results through a projection, which is specified by the second param of the find
method. In this param you specify fields to explicitly include or exclude by passing the attribute and a boolean 1 or 0 whether to include or not.
db.chefs.find({"restaurants.0": "Bob's Kitchen" }, {name: 1});
Query Operators
There are many query operators: Comparison operators, Element operators, Logical operators, Regex operators, Array operators.
Updating a record
db.collection.update( { name: "bob" }, { $set: { "skills.cuisine": "Spanish" } });
You can also update a document using updateOne
which updates the first record matching the criteria specified.
Upserting
"Upserting" is a term meaning a record is inserted if an update attempt is performed on a record that doesn't exist.
db.collection.update( { name: "bob" }, { $set: { "skills.cuisine": "Spanish" } }, { upsert: true});
Deleting a database
use temp;
db.dropDatabase();
Deleting a collection
db.collection.drop();
Deleting a record
db.collection.remove({ attribute: value }, 1);
Deleting all records
db.collection.remove({})
Deleting multiple records
db.collection.remove( { _id : { $in: [
ObjectId("576dbca0f48a891f0048c3ff"),
ObjectId("576dbca0f48a891f0048c401"),
ObjectId("576dbca0f48a891f0048c402"),
ObjectId("576dbca0f48a891f0048c403"),
ObjectId("576dbca0f48a891f0048c404"),
ObjectId("576dbca0f48a891f0048c405")
] } } );
Renaming fields
db.mycollection.update({}, { $rename: {
"nmae": "name",
"pstcode": "postcode"
} }, false, true);
Indexes
Searching a non-indexed collection is O(n), so very slow when there are lots of documents. Creating an index means you are sorting that key so a search becomes O(log2n) as it uses a binary search (btree).
Creating an index is as easy as:
db.collection.createIndex({ myField: 1 });
The 1
means it is sorted ascending.
To find out what indexes already exist do:
db.collection.getIndexes();
To delete an index we use dropIndex
with the same signature it was created with:
db.collection.dropIndex({ myField: 1 });
The _id
is indexed by default.
Importing JSON files
mongoimport -d mydatabasename -c mycollection --drop --jsonArray --file myJSONFile.json
Exporting Collections
It is easy to export a collection to a JSON file:
mongoexport --host my.mongo.db.host --port 37017 -d mydatabasename -c mycollection --out /wherever/whatever.json
And to export a CSV file, naming their fields:
mongoexport --db mydb --collection chefs --type=csv -f "Name","Age","DOB","Cuisine" --out chefs.csv
Flags are:
-d
: database-c
: collection-f
: field list (to export)--out
:file/directory path and file to export to
Exporting is fine when you are using JSON files or importing csv files. But this is not great for backing up or restoring your databases. Instead you want to take a great big dump.
Mongodump and Mogorestore
These both export and imort BSON respectively, including any meta data and indexes you have set up.
To dump "mydatabase" run:
mongodump -d mydatabase
This creates a dump folder in your current directory containing the BSON files.
To restore you just need to specify the dump folder from the previous step:
mongorestore yourDumpFolder
You can specify any host and port numbers too, along with user name and password if you are restoring a remote server.
Aggregation operations
Check out the Mongo aggregation quick reference for Mongo piplelines.
- match
- project
- sort
- skip
- limit