Node + MongoDB

(Hey you! Yeah you! You can find all the code referenced in this post at https://github.com/NerdcoreSteve/node_mongo)

I really like MongoDB because it saves data in a format that is pretty much like how I handle data in application memory. Namely, the nesting of arrays and/or hashes.

Key-value pairs and lists. Simple.

Ok, let's get started. You can download MongoDB here:
https://docs.mongodb.com/manual/administration/install-community/

Or if you have a mac with Homebrew installed you can do

brew install mongodb  

Now we need to start the MongoDB service.

I just ran:

brew services start mongodb  

but I should mention that the official MongoDB website's quick start tutorial said you should run:

mongod --dbpath=/data  

When I had mongo on Linux I seem to remember I only had to run

mongod  

But I can't verify that.

Now that we've got it installed, let's start playing around with this thing. Start the MongoDB shell by simply typing:

mongo  

You should see something like the following output:

MongoDB shell version v3.4.4  
connecting to: mongodb://127.0.0.1:27017  
MongoDB server version: 3.4.4  

You're now in the mongo shell!

Once you're at this point you can always type help to get some insight into what commands to use but I prefer cheat sheets like this one: https://docs.mongodb.com/manual/reference/mongo-shell/

Ok, now we need to create a database. A kind of cool thing is that the same command we use to start using an existing database is the same command we use to create one, namely use.

Here, I'll create a database named potato:

> use potato
switched to db potato  

If you ever want to see what databases you've created just use

> show dbs

Here's my output:

chatter                    0.078GB  
cloud_drive                0.078GB  
first-project_development  0.078GB  
going_pronkers             0.078GB  
local                      0.078GB  
mydb                       0.078GB  
spa                        0.078GB  
test                       0.078GB  

As you can see I've got a few databases from a few projects either abandoned or on hold. I'm sure none of you out there over-commit on projects like I do. ;P

Ok, let's create a collection (or list) in our mongo database:

> db.createCollection('chips')
{ "ok" : 1 }

You can see all collections in the current database you're using by typing:

> show collections
chips  
system.indexes  

I assume system.indexes has something to do with... uh.. the system? Anyway I can see chips there, so I'm happy.

Let's add some chips to our list of chips:

> db.chips.insert({type: 'barbeque'})
WriteResult({ "nInserted" : 1 })  
> db.chips.insert({type: 'Sour Cream And Onion'})
WriteResult({ "nInserted" : 1 })  

If we ever want to see what's in chips here's all we need to do:

> db.chips.find()
{ "_id" : ObjectId("592eb1a8dbd0a5d222d719ba"), "type" : "barbeque" }
{ "_id" : ObjectId("592eb1e1dbd0a5d222d719bb"), "type" : "Sour Cream And Onion" }

See? Pretty much like a regular array in JavaScript.

Let's do some more:

> db.createCollection('lists')
{ "ok" : 1 }
> db.lists.insert({numbers: [1, 2, 3, 4]})
WriteResult({ "nInserted" : 1 })  
> db.lists.find()
> db.lists.insert({fruit: ['apple', 'banana', 'tomato', 'bear']})
WriteResult({ "nInserted" : 1 })  
> db.lists.find()
{ "_id" : ObjectId("592fcedfdbd0a5d222d719bc"), "numbers" : [ 1, 2, 3, 4 ] }
{ "_id" : ObjectId("592fcf20dbd0a5d222d719bd"), "fruit" : [ "apple", "banana", "tomato", "bear" ] }

Objects in collections can have any type of data for a given key.

Here's another example:

> db.createCollection('game')
{ "ok" : 1 }
> db.game.insert({items: [{name: 'flask'}, {name: 'love potion'}, {name: 'sword'}]})
WriteResult({ "nInserted" : 1 })  
> db.game.insert({'player': {att: 1, def: 2, agil: 1}})
WriteResult({ "nInserted" : 1 })  
> db.game.find({player: {$exists : true}})
{ "_id" : ObjectId("592fd099dbd0a5d222d719bf"), "player" : { "att" : 1, "def" : 2, "agil" : 1 } }

And, of course, we can delete collections:

db.sandwiches.drop()  

Ok, this is fun and all, but how do we access our database with JavaScript code?

Check it out:

const  
    {MongoClient} = require('mongodb'),
    url = 'mongodb://localhost:27017/potato'

MongoClient.connect(url, (err, db) => {  
    if(err === null) {
        //...Now we're ready to do some database stuff

We use the mongodb module then we define the url for our database.

I know the port number to put after localhost because it's in their documentation.

Next I use MongoClient to connect to that database. We do the usual JavaScript/Node thing of using a callback to do stuff after the connection is made and checking for an error.

If there's no error we're ready to start messing around with the database.

Here's how we'd get a list of collections in our database:

        db.listCollections().toArray((err, items) => {
            if(!err) {
                console.log(
                    '****listing collections****')
                console.log(items)
                console.log(
                    '***************************')
            }
        })

And here's what we get when we run this part of the program:

****listing collections****
[ { name: 'game',
    type: 'collection',
    options: {},
    info: { readOnly: false },
    idIndex: { v: 1, key: [Object], name: '_id_', ns: 'potato.game' } },
  { name: 'chips',
    type: 'collection',
    options: {},
    info: { readOnly: false },
    idIndex: { v: 1, key: [Object], name: '_id_', ns: 'potato.chips' } },
  { name: 'lists',
    type: 'collection',
    options: {},
    info: { readOnly: false },
    idIndex: { v: 1, key: [Object], name: '_id_', ns: 'potato.lists' } },
  { name: 'system.indexes',
    type: 'collection',
    options: {},
    info: { readOnly: false } } ]
***************************

Now I'm going to insert a collection (initialized with an array), print out its contents, remove an item from that collection, and then print out the new contents:

db.collection('sandwiches').insertMany(sandwiches, (err, result) => {  
    if(!err) {
        db.collection('sandwiches').find().toArray((err, sandwiches) => {
            if(!err) {
                console.log('****initial insert****')
                console.log(sandwiches)
                db.collection('sandwiches').deleteOne({type: 'hotdog'}, (err, result) => {
                    if(!err) {
                        db.collection('sandwiches').find().toArray((err, sandwiches) => {
                            if(!err) {
                                console.log('****After deletion****')
                                console.log(sandwiches)
                                console.log('**********************')
                            }
                            db.close()

And here's our output:

****initial insert****
[ { _id: 594f1aa4e7267b52ee2825d3, type: 'hotdog' },
  { _id: 594f1aa4e7267b52ee2825d4, type: 'hamburger' },
  { _id: 594f1aa4e7267b52ee2825d5, type: 'hogie' } ]
****After deletion****
[ { _id: 594f1aa4e7267b52ee2825d4, type: 'hamburger' },
  { _id: 594f1aa4e7267b52ee2825d5, type: 'hogie' } ]
**********************

Of course you might be wondering why we have to engage in callback hell. Well, we need to make sure the previous asynchronous action is completed before we move onto the next one.

Of course, depending on what you're doing, you may not need to worry about making a callback pyramid, but if you do you can always use promises. There's even a ready-made MongoDB promise library, but I've not tried it yet so I can't say if it's any good.

Since I'm all about functional programming I think I might go the monad route and report back. If that doesn't work I'll make a post about using MongoDB with promises.

In the meantime have a look at the MongoDB quickstart, the MongoDB node driver api, and the MongoDB manual.

Happy coding!

Looking for a software developer?