Commanding MongoDB

MongoDB comes with an interactive shell, which we have already used briefly in the previous chapter. To refresh your memory, after starting the MongoDB daemon by typing mongod, you access the shell in a separate terminal window by typing mongo.

Primarily, you will be accessing MongoDB using native code in your application. However, understanding the MongoDB shell is invaluable to using it. There will be times when you want to access the shell directly, particularly for debugging. You may also need to manage a MongoDB instance in the cloud.

You should have a good grasp of the MongoDB shell.

Getting information

One of the most important things you can do in the MongoDB shell is to manage your databases. Getting meta information out of MongoDB is most easily accomplished using shell commands. The following are some of the basic commands you can use in the MongoDB shell to get information.

help - This will output a list of basic commands available in the MongoDB shell. For help with methods that operate on a database, you will use the db.help() method. Typing help into the MongoDB shell outputs the following:

  • db.help(): Help on db methods
  • db.mycoll.help(): Help on collection methods
  • sh.help(): Sharding helpers
  • rs.help(): Replica set helpers
  • help admin: Administrative help
  • help connect: Connecting to a db help
  • help keys: Key shortcuts
  • help misc: Misc things to know
  • help mr: Mapreduce
  • show dbs: Show database names
  • show collections: Show collections in current database
  • show users: Show users in current database
  • show profile: Show most recent system.profile entries with time s>= 1 m
  • show logs: Show accessible logger names
  • show log [name]: Prints out last segment of log in memory; global is default
  • use <db_name>: Set current database
  • db.foo.find(): List objects in the foo collection
  • db.foo.find( { a : 1 } ): List objects in foo where a == 1
  • it: Result of the last line evaluated; use to further iterate
  • DBQuery.shellBatchSize = x: Set default number of items to display on shell
  • exit: Quit Mongo shell

Some of the most important commands for gathering info from a database are the commands that begin with show. For example, showdbs will give you a list of the currently accessible database names on the system. showcollections will list the collections in the current database.

One thing that isn't listed here is a method for retrieving the database on which you are currently operating. To do that, simply type db and the shell will output the name of the current database.

Inserting and updating data

In the last chapter, we inserted some records using the insert method. You're going to do that a little differently here so that you can set up and load some data into your giftapp database, one that we created in the last chapter for the SPA you're building.

We're going to use two methods to insert data that you haven't used yet. One will be to execute a JavaScript file in the MongoDB shell which will set up and execute commands. We'll use this to insert some documents. The other method we'll use is a bulk operation that will allow us to set up some data and then execute and bulk insert it.

Running scripts in the MongoDB shell

The MongoDB shell allows you to load and execute JavaScript files. In your giftapp directory, create a new folder called scripts and create a JavaScript file called db-init.js:

db = db.getSiblingDB('giftapp'); 
 
var user1 = {firstName:"Mark", lastName:"Smith",       email:"[email protected]"}; 
 
var user2 = {firstName:"Sally", lastName:"Jones", email:"[email protected]"}; 
 
var users = [user1, user2]; 
 
db.users.insert(users); 

The first line, db=db.getSiblingDB('giftapp'), tells the MongoDB shell which database to work with in case you haven't already selected the giftapp database in some way. We need to use this method because the use command isn't valid JavaScript.

Next, you create two objects, user1 and user2, using JavaScript object literal notations. These objects represent user data for the users' Mark Smith and Sally Jones. You then create an array called users that contains the two user objects.

Next, we invoke the insert method on the users collection and pass it the users array. If there is no users collection in the giftapp database, one will be created when we execute this script.

Note that when an array is passed to the insert method, MongoDB will insert each document separately. This is a powerful feature allowing for easy and efficient multiple document inserts.

There are two ways we can load and execute this script.

From the command-line in a terminal not running the MongoDB shell, navigate to the directory where the script is stored and type the following:

$ mongo localhost:27017/test db-init.js
MongoDB shell version: 3.0.4
connecting to: localhost:27017/test

Unfortunately, there won't be any really useful output to tell you that the inserts were completed. If you start the MongoDB shell, or use a terminal where it's already running, you can verify by doing the following:

> db.users.count()
2
> db.users.find()
{ "_id" : ObjectId("566dcc5b65d385d7fa9652e3"), "firstName" : "Mark", "lastName" : "Smith", "email" : "[email protected]" }
{ "_id" : ObjectId("566dcc5b65d385d7fa9652e4"), "firstName" : "Sally", "lastName" : "Jones", "email" : "[email protected]" }

The count method returns the number of documents in a collection. Here, there are two. We've already explored the find method. Here we invoke find with no arguments, which returns all the documents in the collection. You can see that Mark and Sally are now documents stored separately in the users collection.

If you run this script multiple times, it will create numerous Mark and Sally documents. If you want to clean out the collection and start over, you can use the drop method and verify using the following command:

> db.users.drop()
true
> db.users.count()
0
> db.users.find()
>

I promised you a second method of running scripts, and we'll get to that. Let's make a small modification to the script first:

db = db.getSiblingDB('giftapp'); 
var now = new Date(); 
 
var user1 = {firstName:"Mark", lastName:"Smith", email:"[email protected]", created: now}; 
var user2 = {firstName:"Sally", lastName:"Jones", email:"[email protected]", created: now}; 
 
var users = [user1, user2]; 
 
db.users.insert(users); 

We added a variable called now that contains a new Date object. Creating a Date object in this way sets the date and time in the object to the current date and time. Next, we add a field called created to Mark and Sally, and give it the value of now, our date object.

In a terminal running the MongoDB shell, do the following:

> db.users.drop()
true
> db.users.count()
0
> load('/[path to your directory]/giftapp/scripts/db-init.js')
true
> db.users.count()
2
> db.users.find()
{ "_id" : ObjectId("566dd0cb1c09d090fd36ba83"), "firstName" : "Mark", "lastName" : "Smith", "email" : "[email protected]", "created" : ISODate("2015-12-13T20:10:51.336Z") }
{ "_id" : ObjectId("566dd0cb1c09d090fd36ba84"), "firstName" : "Sally", "lastName" : "Jones", "email" : "[email protected]", "created" : ISODate("2015-12-13T20:10:51.336Z") }

Here, we use the load method to run the script, passing it the path to the script. We see that the two users have been added to the collection, and the find method retrieves their documents.

If you look at the created field on Mark and Sally documents you'll see something new. The Date may look a little different. Internally, MongoDB stores dates as a 64-bit integer representing the number of milliseconds since January 1st, 1970. Negative numbers are used to represent dates before that.

Storing dates and times as integers likes this, instead of strings, allows things such as date calculations and comparisons.

Fortunately, MongoDB outputs dates in a somewhat usable and readable format. We will explore displaying dates in a more human friendly way in a later chapter.

Running bulk operations

Another way to insert multiple documents into a MongoDB collection in a single pass is to use MongoDB's Bulk API. This allows us to set up a list of ordered or unordered operations and then run them all when we choose to execute We can experiment with this using the MongoDB shell commands.

Take a look at the following commands:

> var bulk = db.users.initializeUnorderedBulkOp()
> bulk.insert(
... { firstname: "John",
... lastname: "Smith",
... email: "[email protected]",
... created: new Date()
... }
... );
> bulk.insert(
... { firstname: "Jane",
... lastname: "Smothers",
... email: "[email protected]",
... created: new Date()
... }
... );
> bulk.execute()
BulkWriteResult({
  "writeErrors" : [ ],
  "writeConcernErrors" : [ ],
  "nInserted" : 2,
  "nUpserted" : 0,
  "nMatched" : 0,
  "nModified" : 0,
  "nRemoved" : 0,
  "upserted" : [ ]
})

In the first line, we opened up an unordered bulk operation on users and assigned it to the variable called bulk. We could also have made that an ordered operation, but we don't currently care about the order in which the inserts are executed.

We then add two insert commands to the bulk operation, one for John Smith, and another for Jane Smothers. We can then call execute on the bulk operation. The returned value tells us that there were no errors and that two documents were inserted.

Let's have a look at our collection now:

> db.users.find().pretty()
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba83"),
  "firstName" : "Mark",
  "lastName" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba84"),
  "firstName" : "Sally",
  "lastName" : "Jones",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba85"),
  "firstname" : "John",
  "lastname" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:26:42.165Z")
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "firstname" : "Jane",
  "lastname" : "Smothers",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z")
}

I added the pretty method to the end of the find method in order to tidy up our output and make it a bit more readable. As you can see, John and Jane have been added to our collection.

Finding, modifying, and removing data

Queries are how we search for and return data out of our database. We've been using queries all along every time we have used the find method. We know that find, on its own, will return every single document in a collection. That's not exactly useful.

Specific results

Generally, we want to query a collection and return specific results. We want only those states that export peanuts, or we want a list of customers who live in France.

To specify that we want documents where a specific field matches a specific value, we do this:

> db.users.find({lastname:"Smith"}).pretty()
{
  "_id" : ObjectId("566dff161c09d090fd36ba85"),
  "firstname" : "John",
  "lastname" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:26:42.165Z")
}

Here, I've called the find operation and passed it an object with a single field: lastname. This is called the criteria. The value of that field is Smith. As you can see this returned the record for John Smith. For more than one field, you would separate the fields by commas.

Wait a minute, shouldn't I also see the document for Mark Smith? If you look carefully, the documents for Mark Smith and Sally Jones camelcase firstName and lastName. That is, the N is a capital letter. Therefore, MongoDB doesn't see this as the same field.

This is a good illustration of one of the dangers of schemaless databases, and something to keep in mind. We will fix this in the section on updates.

Let's say that we want to get documents for users with lastName fields matching Smith or Jones. There are a couple of ways you could write this query, but the best way when comparing the same field is to use the $in operator, as shown in the following commands:

> db.users.find({lastName: { $in: ['Jones', 'Smith']}}).pretty()
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba83"),
  "firstName" : "Mark",
  "lastName" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba84"),
  "firstName" : "Sally",
  "lastName" : "Jones",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}

Query operators

MongoDB comes with a number of operators that all begin with the dollar sign. They are used for modifying and comparing within query criteria.

There are a number of types of query operators that include comparison operators such as $eq: equal to, $gt: greater than, and $lte: less than or equal to. Here's an example:

> db.users.find({payrate: {$gt: 45}})

This would return all documents in the users collection that had a payrate field with a value greater than 45.

Logical operators include $or, $and, $not, and $nor. Each of these behaves like you'd expect if you're used to logical operators. Here's an example:

db.find({$and: [{firstName: "Steve"},{lastName: "Smith"}]})

This query returns all documents that have a firstName field equal to Steve and a lastName field equal to Smith.

MongoDB includes two element operators: $exists: to check if a field exists, and $type: to check the type of a specified file. Take a look at the following command:

> db.users.find({car: { $exists: true })

This query returns all documents in the users collection that have a car field.

MongoDB includes a number of other operators. These include things such as regex matching and geospatial comparison. There are also operators comparing arrays.

For a more complete list of operators, see the MongoDB documentation on operators at https://docs.mongodb.org/v3.0/reference/operator/query/.

Projections

We covered projections briefly in the previous chapter but, to refresh your memory, a projection specifies the fields returned in a query. We don't always want all of the fields in the documents that we return, so a projection lets us limit the data to the fields we are interested in.

Projections will be the second argument to the find method, as shown in the following commands:

> db.users.find({},{ email: 1 })
{ "_id" : ObjectId("566dd0cb1c09d090fd36ba83"), "email" : 
      "[email protected]" }
{ "_id" : ObjectId("566dd0cb1c09d090fd36ba84"), "email" : 
      "[email protected]" }
{ "_id" : ObjectId("566dff161c09d090fd36ba85"), "email" : 
      "[email protected]" }
{ "_id" : ObjectId("566dff161c09d090fd36ba86"), "email" : "[email protected]"
    }

We specified that we wanted all documents in the collection by passing an empty object as the first argument to find. Then, we used a projection to tell MongoDB that we wanted to see the email field.

You'll notice that the _id field is returned in the results. This is a default. To suppress that, we give it a value of 0 in the find in the projection as shown in the following command:

> db.users.find({},{ email: 1, _id: 0 })
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }

In this query, email is included, while _id is excluded.

There are also a number of projection operators. You can find the details for those in the MongoDB documentation at https://docs.mongodb.org/v3.0/reference/operator/query/.

Query modifiers

As the name implies, query modifiers are used to modify the data coming back from a query. This includes doing things such as sorting, or returning a maximum number of results.

There are two forms of modifiers in Mongo DB (I prefer the first). Take a look at the following commands:

db.collection.find( { <query> } )._addSpecial( <option> )
db.collection.find( { $query: { <query> }, <option> } )

Let me illustrate with an example:

> db.users.find({},{ email:1, _id:0 }).sort({ email:1 })
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }

Here, I am selecting all documents in the users collection. I am returning only the email field (and suppressing the _id field). I am then sorting by ascending order by email. If we wanted to sort the documents by the email field in descending order, we would make the value in the modifier -1, as shown in the following commands:

> db.users.find({},{ email:1, _id:0 }).sort({ email:-1 })
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }

Modifying data

To modify MongoDB documents, you generally use the update method.

Take a look at following commands:

> db.users.find({lastname:"Smothers"}).pretty()
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "firstname" : "Jane",
  "lastname" : "Smothers",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z")
}
> db.users.update({lastname:"Smothers"},{$set:{ 
     email:"[email protected]"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find({lastname:"Smothers"}).pretty()
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "firstname" : "Jane",
  "lastname" : "Smothers",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z")
}

Here, we do a find just to display the document for Jane Smothers. We want to change the e-mail address of Jane, so we use the update method. The first argument to the update method is the same criteria used in the find method to select a document or set of documents. The second argument is the instruction for the update.

Here, we've used the $set operator to change the e-mail address. If there wasn't an email field in the document, the $set operator would create a new field.

It's important to note that update, by default, will only update a single document. To update multiple documents, you set a multi option as part of a third option to update.

Let's fix our users collection to make the fields for firstname and lastname into camelcase:

> db.users.update({ lastname: { $exists: true }}, {$rename:
      {'lastname':'lastName','firstname':'firstName'}}, { multi: true })
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.users.find().pretty()
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba83"),
  "firstName" : "Mark",
  "lastName" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba84"),
  "firstName" : "Sally",
  "lastName" : "Jones",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba85"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:26:42.165Z"),
  "lastName" : "Smith",
  "firstName" : "John"
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z"),
 "lastName" : "Smothers",
  "firstName" : "Jane"
}

The first argument to the update method uses the $exists operator to select any documents without the camelcase lastname field. The second argument uses the $rename operator to change both firstname and lastname field names to camelcase. The final argument sets the multi option to true, telling MongoDB to update all of the matched documents.

The result shows us that two documents were matched and two documents were updated. Running the find method shows us that all documents now have the same field names.

By default, if the query part of the update method doesn't match any documents, MongoDB doesn't do anything. We can tell MongoDB to create a new document if none are matched using the upsert option:

> db.users.update(
... { email: "[email protected]"},
... {
...   firstName: "Johnny",
...   lastName: "Fiverton",
...   email: "[email protected]",
...   created: new Date()
... },
... { upsert: true })
WriteResult({
  "nMatched" : 0,
  "nUpserted" : 1,
  "nModified" : 0,
  "_id" : ObjectId("566eaec7fa55252158538298")
})
> db.users.find().pretty()
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba83"),
  "firstName" : "Mark",
  "lastName" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba84"),
  "firstName" : "Sally",
  "lastName" : "Jones",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba85"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:26:42.165Z"),
  "lastName" : "Smith",
  "firstName" : "John"
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z"),
  "lastName" : "Smothers",
  "firstName" : "Jane"
}
{
  "_id" : ObjectId("566eaec7fa55252158538298"),
  "firstName" : "Johnny",
  "lastName" : "Fiverton",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-14T11:57:59.196Z")
}

Here, we select documents where an email field matches [email protected]. As we know, there are no documents matching this query. The second argument to update lists the data we want to change. Finally, we set the upsert option to true.

The write result shows us that no documents were matched or modified, but that a single document was upserted.

Invoking find shows us that the record for Johnny Fiverton has been added.

You may have noticed that we did not use the $set operator this time around. If the second argument in update uses no operators, MongoDB will replace the entire document with the data in the second argument. This is something to be careful of; use $set when you don't want to replace the entire documents.

A list of update operators is available in the MongoDB documentation: https://docs.mongodb.org/v3.0/reference/operator/update/.

Removing data

So far, we have covered the: create, read, and update components of CRUD (create, read, update, delete). The remaining part is deleting documents. For deletion, MongoDB has the remove method.

Remove has a somewhat familiar signature.

Take a look at the following commands:

> db.users.remove({ email: "[email protected]" })
WriteResult({ "nRemoved" : 1 })
> db.users.find().pretty()
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba83"),
  "firstName" : "Mark",
  "lastName" : "Smith",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dd0cb1c09d090fd36ba84"),
  "firstName" : "Sally",
  "lastName" : "Jones",
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T20:10:51.336Z")
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba85"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:26:42.165Z"),
  "lastName" : "Smith",
  "firstName" : "John"
}
{
  "_id" : ObjectId("566dff161c09d090fd36ba86"),
  "email" : "[email protected]",
  "created" : ISODate("2015-12-13T23:28:00.383Z"),
  "lastName" : "Smothers",
 "firstName" : "Jane"
}

And it's goodbye Johnny.

You can probably surmise that the first argument to remove is the query. Here, we have selected all documents with an email field matching [email protected]. In this case, there is only one. The write result tells us that the number of documents removed is one.

A word of caution: by default, remove will delete all matched documents. If the query is an empty object, remove will delete everything in the collection. The indexes, however, will stay intact. To ensure that you are only removing a single document, you set the justOne parameter, the second optional argument to remove, to 1, as shown in the following command:

db.users.remove( { lastName: "Smith" }, 1 )

This would remove a single Smith from our users collection.

The cursor

In MongoDB, the result of invoking db.collection.find() is actually a cursor. A cursor is a pointer to the results of a query. In the MongoDB shell, if you do not assign a cursor to a variable, the cursor is automatically iterated and output. This is what we have been doing so far:

> var cursor = db.users.find({},{ email:1, _id: 0 })
> cursor
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
{ "email" : "[email protected]" }
> cursor
>

Here, we create a variable called cursor and assign to it the cursor returned by the find method. We then manually iterate the cursor simply by typing its name and hitting Enter. Typing the cursor name again and hitting Enter does nothing because the cursor has already been iterated.

This, in itself, isn't very useful, but we can do all kinds of things with the cursor. For example, if we wanted to put all of our documents into an array we could do this:

> var cursor = db.users.find({},{ email:1, _id: 0 })
> var myDocs = cursor.toArray()
> myDocs
[
  {
    "email" : "[email protected]"
  },
  {
    "email" : "[email protected]"
  },
  {
    "email" : "[email protected]"
  },
  {
    "email" : "[email protected]"
  }
]

MongoDB offers a ton of built-in cursor methods. Documentation for MongoDB JavaScript cursor methods can be found at: https://docs.mongodb.org/manual/reference/method/#js-query-cursor-methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.31.165