MongoDB Hands-On
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:
- Change stream
- 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 } }