E41: Experience the MongoDB Shell

The goal of this experience is for you to become comfortable with the basic ideas behind data storage and retrieval using Mongo.

Task 1: Install Meteor-compatible NPM

Task 2: Install Mongosh

To access the Mongo shell within Meteor, you will need to install Mongosh (MongoDB Shell). To do this follow the Procedure Instructions. (Note: you do not need to install Mongo locally, since Meteor will install it for you. You just need to install Mongo Shell (Mongosh).

To test that it is installed, invoke:

% mongosh --version
2.1.5

Task 3: Create experience-mongodb app

In this experience, you will play around with the MongoDB process that runs whenever the Meteor server is running. So, if you haven’t already, install Meteor. Meteor ships with a version of MongoDB (currently Version 4.0).

Now create a temporary directory to hold your work for this experience. Call it “experience-mongodb/”. (Note: Your life will be better if the full path to this directory does not contain any directories whose name contains spaces. So, for example, don’t put any of your code for this class in a subdirectory such as “Documents and Settings”).

Now cd into the experience-mongodb/ directory, and invoke meteor create app --prototype. This will create the default Meteor app in a new directory called ‘app’. (If you’ve just installed Meteor, you may need to start up a new Terminal in order for the meteor command to be found.)

Task 4: Bring up a MongoDB server-side shell

Now you’re going to play with the server-side MongoDB process, and learn how to create a collection, add documents to it, and retrieve them.

Bring up Meteor

To access the MongoDB process, you must first have Meteor running. So, in a shell window, cd into the experience-mongodb/app directory and invoke meteor run to bring up Meteor (and Mongo) for your sample app. Here’s what the shell running Meteor should look like:

% meteor run
[[[[[ ~/Desktop/experience-mongodb/app ]]]]]  

=> Started proxy.                             
=> Started HMR server.                        
=> Started MongoDB.                           
I20240205-12:06:57.213(-10)? ** You've set up some data subscriptions with Meteor.publish(), but
I20240205-12:06:57.223(-10)? ** you still have autopublish turned on. Because autopublish is still
I20240205-12:06:57.223(-10)? ** on, your Meteor.publish() calls won't have much effect. All data
I20240205-12:06:57.223(-10)? ** will still be sent to all clients.
I20240205-12:06:57.223(-10)? **
I20240205-12:06:57.224(-10)? ** Turn off autopublish by removing the autopublish package:
I20240205-12:06:57.224(-10)? **
I20240205-12:06:57.224(-10)? **   $ meteor remove autopublish
I20240205-12:06:57.224(-10)? **
I20240205-12:06:57.224(-10)? ** .. and make sure you have Meteor.publish() and Meteor.subscribe() calls
I20240205-12:06:57.224(-10)? ** for each collection that you want clients to see.
I20240205-12:06:57.224(-10)? 
=> Started your app.

=> App running at: http://localhost:3000/

Bring up Mongo

Now create a second shell window, cd into the experience-mongo/app directory, and invoke meteor mongo to create a shell command line interface to the running MongoDB process. If you’re successful, you should now have two shell windows, one running Meteor, and one that gives you access to the Mongo process started with Meteor. This second shell window should look like this:

% meteor mongo
Current Mongosh Log ID: 65e4f9837294fdf4f712197b
Connecting to:    mongodb://127.0.0.1:3001/meteor?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.5
(node:3606) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Using MongoDB:    7.0.5
Using Mongosh:    2.1.5

For mongosh info see: https://docs.mongodb.com/mongodb-shell/


To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.

meteor [direct: primary] meteor> 

Note that you are now in a command shell that allows you to type commands.

This MongoDB process has a single database called “local” (created by default). You can see this by running the command “show dbs”. Try it. Here’s what should happen:

meteor [direct: primary] meteor> show dbs
admin    80.00 KiB
config  280.00 KiB
local   460.00 KiB
meteor   40.00 KiB

This MongoDB process also has a collection named “links”. This should be visible when you run the command “show collections”:

meteor [direct: primary] meteor> show collections
links

Task 3: Create a collection

For NoSQL databases like MongoDB, “collections” are the relational database equivalent of a “Table”. Collections store records, called “Documents”. A single MongoDB database can have any number of distinct Collections. For this task, let’s create a Collection called “people”. To do this in the MongoDB shell, you can invoke db.createCollection("people");. Try it. Here’s what should happen:

meteor [direct: primary] meteor> db.createCollection("people");
{ ok: 1 }

Now if you invoke “show collections”, you should see the people collection. Try it. Here’s what should happen:

meteor [direct: primary] meteor> show collections
links
people

Task 4: Add a document to the people collection

Now that we have a collection, let’s add some data to it. In Mongo, each element of a collection is called a “document”, and a document is basically (but not exactly) like a JavaScript object. To add a document to a collection, you use the MongoDB insert function. An example invocation in the MongoDB shell is db.people.insertOne({ first: "Philip", last: "Johnson", age: 58, city: "Kailua" });. Try it. Here’s what should happen:

meteor [direct: primary] meteor> db.people.insertOne({ first: "Philip", last: "Johnson", age: 58, city: "Kailua" });
{
  acknowledged: true,
  insertedId: ObjectId('65c174867618b74ce9726abc')
}

Task 5: Add some more documents

Now that you’ve seen how to add a single record, let’s add a few more. You can just copy and paste the following into your shell:

db.people.insertOne({ first: "Joanne", last: "Amberg", age: 58, city: "Kailua" });
db.people.insertOne({ first: "Jenna", last: "Corin", age: 31, city: "Boulder Creek" });
db.people.insertOne({ first: "Katie", last: "Kai", age: 25, city: "Palo Alto" });

Press Return/Enter after you paste this code into your shell.

Task 6: Retrieve documents with find()

The find function is the primary way to retrieve data from Collections. Find takes two arguments, both optional. The first argument, called the “query”, specifies selection criteria. The second argument, called the “projection”, controls how results are returned. For example, the projection can specify which fields to return from the documents.

If you invoke find() with no arguments, then all of the documents are retrieved. For example, try invoking db.people.find();. Here’s what should happen:

meteor [direct: primary] meteor> db.people.find();
[
  {
    _id: ObjectId('65c174867618b74ce9726abc'),
    first: 'Philip',
    last: 'Johnson',
    age: 58,
    city: 'Kailua'
  },
  {
    _id: ObjectId('65c174e67618b74ce9726abd'),
    first: 'Joanne',
    last: 'Amberg',
    age: 58,
    city: 'Kailua'
  },
  {
    _id: ObjectId('65c174e67618b74ce9726abe'),
    first: 'Jenna',
    last: 'Corin',
    age: 31,
    city: 'Boulder Creek'
  },
  {
    _id: ObjectId('65c174e67618b74ce9726abf'),
    first: 'Katie',
    last: 'Kai',
    age: 25,
    city: 'Palo Alto'
  }
]

One thing you can see from this result is that MongoDB implicitly creates a field called “_id” which contains a unique ID for each document.

A simple way to retrieve a subset of documents is to provide a query object that specifies the field and value of interest. For example, to retrieve all of the documents which contain “Kailua” as the city, use db.people.find({ city: "Kailua" });. Try it.

If you provide a string value for a field, as we did above, then that string must match the field’s value exactly. You can also provide an object with the value for a field to specify a constraint that the document should satisfy in order to be returned.

For example, to retrieve all documents with people younger than 35, you can use db.people.find({ age: { $lt: 35 }});. Try it.

Task 7: Update an existing document

Use the update() function in combination with the $set operator to change the value of a field. For example, if Philip has a birthday, you might want to change his age with db.people.update({ first: "Philip", last: "Johnson" }, { $set: { age: 60 }});. The first argument selects the document associated with Philip Johnson, and the second argument specifies the new field values.

Then you can do a db.people.find({ first: "Philip", last: "Johnson" }); to verify that his age is changed. Try these out yourself.

Task 8: Delete a document

Let’s get rid of Philip with db.people.remove({ first: "Philip", last: "Johnson" });. You can then verify that Philip is no longer in the collection with db.people.find();. Try it.

Task 9: Expand your understanding

Now you know the very basics of MongoDB: how to create a collection, and how to insert, find, update, and delete documents.

There are a gazillion different functions and parameters you can invoke on MongoDB collections, which will slice and dice your data in every conceivable way. The MongoDB collection documentation provides a reference guide.

For this final task, implement the following:

  1. Add two more documents to the collection.
  2. Find and return all the documents in the people collection, sorted by last name. (See MongoDB find documentation on sort) for the syntax.)
  3. Try to add the following document to the people collection: { species: "canis lupus", location: "Boulder Creek" }. Does it succeed? Why?

Summary

Hopefully you now have a basic idea of how to manipulate data in MongoDB. You also now know how to access the server-side database that is created while developing a Meteor application. That will come in handy.

However, it is important to note that the syntax used to manipulate your MongoDB database in the shell is slightly different from the JavaScript syntax you will use when developing your Meteor apps (although the basic ideas won’t change). We will learn more about these syntax differences in the next experience for this module.

Submission instructions

By the time and date indicated on the Schedule page, submit this assignment via Laulima. To submit, please cut and paste your entire MongoDB session (tasks 3-9) into the Laulima text field.