MongoDB Hands-On

Frank Chung
DeepQ Research Engineering Blog
4 min readApr 19, 2019

Recently I decided to migrate Firebase realtime database into mongodb, and here is my experience on studying the mongodb.

Installation

Installing and Running a local mongodb on my macbook (detail):

$ brew tap mongodb/brew$ brew install mongodb-community@4.0$ mongod --config /usr/local/etc/mongod.conf$ mongo

Database Namespace

In a mongo client, we can use Node.js to interact with mongodb server.

List all databases (a database contains multiple collections):

> show dbs;
admin 0.000GB
config 0.000GB
data 0.000GB
local 0.000GB

Use a database:

> use mydb
switched to db mydb

Create and list all collections (a collection contains multiple documents):

> db.createCollection("mycoll")
{ "ok" : 1 }
> show collections;
mycoll

CRUD operations

INSERT <document> {option}

Insert a document with given document id (if no id given, a random id will be generated):

> db.mycoll.insert({ _id: "key1", data: 1 })
{ "acknowledged" : true, "insertedId" : "key1" }

Insert multiple documents:

> docs = [
{_id:"key2", data:2},
{_id:"key3", data:3},
{_id:"key4", data:4},
];
> db.mycoll.insertMany(docs);
{ "acknowledged" : true, "insertedIds" : [ "key2", "key3", "key4" ] }

FIND <query> <projection> {option}

Read all documents in a collection:

> db.mycoll.find();
{ "_id" : "key1", "data" : 1 }
{ "_id" : "key2", "data" : 2 }
{ "_id" : "key3", "data" : 3 }
{ "_id" : "key4", "data" : 4 }

Read a document with given id:

> db.mycoll.find({_id: "key1" });
{ "_id" : "key1", "data" : 1 }

UPDATE <query> <update> {option}

Replace a document with given id:

> db.mycoll.update({ _id: "key1" }, { "data": 0 })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

Update a document to set a new field:

> db.mycoll.update({ _id: "key1" }, { "$set": { "data2": 0 } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.mycoll.find({_id: "key1" });
{ "_id" : "key1", "data" : 1, "data2" : 0 }

DELETE <query> {option}

Delete a document with given id:

> db.mycoll.deleteOne({ _id: "key1" })
{ "acknowledged" : true, "deletedCount" : 1 }

Replica Set

Some useful features are only supported in mongodb replica set:

  1. Change stream
  2. Multiple documents transaction

Here we provide several ways to build a replica set:

RUN-RS

run-rs is a local mongo replica set for testing, we can one-line deploy it in localhost:

$ npm install run-rs -g
$ run-rs

DOCKER

Create a local network for mongodb replica set:

$ docker network create cluster

Run 3 mongodb server instances and expose the port of node1:

$ docker run --name node1 -d --net cluster -p 27017:27017 mongo --replSet "rs0"
$ docker run --name node2 -d --net cluster mongo --replSet "rs0"
$ docker run --name node3 -d --net cluster mongo --replSet "rs0"

Run mongo client and connect to node1:

$ mongo

Set a replica set config to describe the node name and priority:

> config = {
_id: "rs0",
members: [
{
_id: 0,
host: "node1:27017",
priority: 1,
},
{
_id: 1,
host: "node2:27017",
priority: 0,
},
{
_id: 2,
host: "node3:27017",
priority: 0,
},
],
};

Initialize the replica set:

> rs.initiate(config);
{
"ok" : 1,
"operationTime" : Timestamp(1555645824, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1555645824, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

Now you can send requests to node1, which is the primary of the cluster:

rs0:PRIMARY> show dbs;

Change Stream

WATCH <pipeline> {options}

We can monitor the change events in a replica set, connect to primary node and start watching:

rs0:PRIMARY> db.mycoll.watch([], { maxAwaitTimeMS: 1000000 })

Insert a document from another mongo client:

rs0:PRIMARY> db.mycoll.insertOne({ data: 1 })
{
"acknowledged" : true,
"insertedId" : ObjectId("5cb94903b3c041b710742eac")
}

We can receive a change event:

{ "_id" : { "_data" : "825CB94934000000012B022C0100296E5A10042D1651E114154EE7933832B492B11C5C46645F696400645CB9492AB3C041B710742EAD0004" }, "operationType" : "insert", "clusterTime" : Timestamp(1555646772, 1), "fullDocument" : { "_id" : ObjectId("5cb9492ab3c041b710742ead"), "data" : 1 }, "ns" : { "db" : "mydb", "coll" : "mycoll" }, "documentKey" : { "_id" : ObjectId("5cb9492ab3c041b710742ead") } }

Use pipeline projection to only get the data we needed:

rs0:PRIMARY> db.mycoll.watch([{ "$project": {"fullDocument.data": 1, _id: 0 } }], {maxAwaitTimeMS: 100000}){ "fullDocument" : { "data" : 1 } }

Reference

--

--