Archive for the ‘MongoDB Developer’ tag
MongoDB on macOS
It’s important to document things before I ask my students to do them. This walks through a native installation of MongoDB on the macOS. At least, it does when you’ve already installed Xcode. If you installed Xcode sometime ago, you should update it otherwise you may get an installation error like the following:
==> Installing mongodb-community from mongodb/brew Error: Your Xcode (11.1) is too outdated. Please update to Xcode 13.1 (or delete it). Xcode can be updated from the App Store. |
The core MongoDB installation document is in the MongoDB 5.0 installation manual. The following is a step-by-step set of instructions for those new to installing something like MongoDB without using the macOS standard package management software.
- The first step requires you install Home Brew, as qualified by the Home Brew Installation page. Before you can install Home Brew, you need to install Xcode on your macOS. After installing Xcode, you need to launch a Terminal session on your macOS and run the following command from the macOS command-line interface (CLI):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
After the installation, which may take several minutes, you should see this final message on the console:
==> Next steps: - Run brew help to get started - Further documentation: https://docs.brew.sh
- Now that Home Brew is installed, the next step requires you to update the Homebrew formulat for MongoDB. You use the following command to do that for MongoDB 5:
brew tap mongodb/brew
You should see something like the following on your console when you update the Homebrew formula:
Running `brew update --preinstall`... ==> Homebrew is run entirely by unpaid volunteers. Please consider donating: https://github.com/Homebrew/brew#donations ==> Auto-updated Homebrew! Updated 1 tap (homebrew/core). ==> Updated Formulae Updated 5 formulae. ==> Tapping mongodb/brew Cloning into '/usr/local/Homebrew/Library/Taps/mongodb/homebrew-brew'... remote: Enumerating objects: 825, done. remote: Counting objects: 100% (322/322), done. remote: Compressing objects: 100% (236/236), done. remote: Total 825 (delta 165), reused 140 (delta 83), pack-reused 503 Receiving objects: 100% (825/825), 180.91 KiB | 0 bytes/s, done. Resolving deltas: 100% (403/403), done. Tapped 14 formulae (30 files, 246.8KB).
- Assuming your Xcode is curent, you now install MongoDB with the necessary supporting packages:
brew install mongodb-community@5.0
You should see something like the following on your console after you install MongoDB 5:
==> Summary /usr/local/Cellar/mongodb-community/5.0.5: 11 files, 181.5MB, built in 6 seconds ==> Running `brew cleanup mongodb-community`... Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). ==> Caveats ==> mongodb-community To start mongodb/brew/mongodb-community now and restart at login: brew services start mongodb/brew/mongodb-community Or, if you don't want/need a background service you can just run: mongod --config /usr/local/etc/mongod.conf
- MongoDB really requires a background service, so I suggest you use the following to create the service:
brew services start mongodb/brew/mongodb-community
You should see something like the following on your console after you install MongoDB 5:
==> Successfully started `mongodb-community` (label: homebrew.mxcl.mongodb-community)
The installation gave you three key binaries:
- The MongoDB Server Daemon – mongod
- The MongoDB sharded cluster query router – mongos
- The MongoDB Shell – mongosh
The installation also creates configuration, data, and log files based on physical CPU-Chip hardware:
- Intel Processor
- Configuration File: /usr/local/etc/mongodb.conf
- Log Directory: /usr/local/var/log/mongodb
- Data Directory: /usr/local/var/mongodb
- Apple M1 Processor
- Configuration File: /opt/homebrew/etc/mongodb.conf
- Log Directory: /opt/homebrew/var/log/mongodb
- Data Directory: /opt/homebrew/var/mongodb
- You can now start the MongoDB Shell with the following command in a Terminal window:
mongosh
You should see the following when you connect to the MongoDB Shell
Current Mongosh Log ID: 61e4e2c98f023c2b5fc5a905 Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000 Using MongoDB: 5.0.5 Using Mongosh: 1.1.8 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. ------ The server generated these startup warnings when booting: 2022-01-16T20:26:18.320-07:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted ------ test>
As always, I hope the instructions are helpful and clear. If you find an issue let em know about it.
Fun JavaScript Q?
I got sent a fun JavaScript question from a student. It was fun for two reasons. They were clearly engaged and trying to figure stuff out on their own and they were just beyond basic syntax issues.
The student wanted to know why this code worked:
// Populate an object with one property. var notempty = {test:"me"} // Test for and enumerate object properties. for (var property in notempty) { if (!notempty.hasOwnProperty(property)) { print("No properties") } else { print("[" + property + "]") } } print("done") |
and returned:
[test] done |
but the behavior changed when the initialized object didn’t have any properties. It failed to print the No properties message:
// Populate an object with one property. var empty = {} // Test for and enumerate object properties. for (var property in empty) { if (!empty.hasOwnProperty(property)) { print("No properties") } else { print("[" + property + "]") } } print("done") |
it printed:
done |
The answer was easy, there were no properties to enumerate, which meant the loop never executes. However, that’s only part of the answer. The complete answer is that the empty object and the default Object.prototype
have no properties to enumerate. You should perform a different test before the loop, like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // Populate an object without properties. var empty = {} // Test for and enumerate object properties. if (Object.keys(empty).length !== 0) { for (var property in empty) { if (empty.hasOwnProperty(property)) { print("[" + property + "]") } } } else { print("No properties") } // Print complete message. print("done") |
If you run this from the mongo shell, like this
mongo --nodb --norc < object_properties.js |
It prints:
MongoDB shell version v4.0.19 No properties done bye |
If you run this inside the mongo shell with the load
function, like
load("object_properties.js") |
It prints:
No properties done true |
However, you can generate a parsing error when you rewrite line 11 in the if-else statement, like so
if (Object.keys(empty).length !== 0) { ... } else { ... } |
The reason for the error is that JavaScript appends a semicolon (;) at the end of each line. Therefore, the if-else-statement appears as an if-only-statement. which raises the syntax error.
It prints:
MongoDB shell version v4.0.19 2021-08-02T13:38:34.672-0600 E QUERY [js] SyntaxError: expected expression, got keyword 'else' @(shell):1:0 done bye |
It appears the parsing error is a warning message because the code completes. Personally, I would prefer that they not raise a warning message because it actually implies a style preference.
There’s a next level problem you may run into while trying to implement reflection, or the discovery of existing properties and their value types, of JavaScript objects. Here’s that code, which seems to be missing from many tutorials and articles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Create an object with functions. // Populate an object without properties. var notEmpty = { first_name: "Clark" , last_name: "Kent" , getName: function() { return this.first_name + " " + this.last_name } } // Test for and enumerate object properties. if (Object.keys(notEmpty).length !== 0) { for (var property in notEmpty) { if (notEmpty.hasOwnProperty(property)) { print("[" + property + "] value is a [" + typeof(notEmpty[property]) + "]") } } } else { print("No properties") } print("done") |
This code on line 12 lets you view a property’s value type:
1 | typeof(notEmpty[property]) |
Only the object[property]
subscript notation method works when the property’s value is a variable. The other dot notation syntax to access a property’s value in JavaScript, object.property
, doesn’t work without a literal value. Actually, it would work if you embed it inside an eval() function call, but the eval() function is bad juju (black magic) in JavaScript coding.
It prints the following:
MongoDB shell version v4.0.19 [first_name] value is a [string] [last_name] value is a [string] [getName] value is a [function] done bye |
While this shows the basic functionality, it would be more effective to create a function and extend it while adding some output formatting. The next example wouldn’t be found in a JSON (JavaScript Object Notation) collection element but would be found in a NodeJS application managing JSON structures. You would define the constructor to match the data properties of the JSON structure.
The nice thing about a JavaScript function is that “It can specify a list of parameters that will act as variables initialized by the invocation arguments. The body of the function includes variable definitions and statement. (JavaScript: The Good Parts, 2008)” Newer approaches allow you to pass an object as a parameter with named notation inside the object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // Create an object with functions. function Person(first_name, last_name) { this.first_name = first_name , this.last_name = last_name , this.setFirstName = function(first_name) { this.first_name = first_name } , this.getName = function() { return this.first_name + " " + this.last_name } } // Create an instance of a variable and print results. var myPerson = new Person("Clark","Kent") print("Initial Name: " + myPerson.getName()) // Call the added function and print the new value. myPerson.setFirstName("Lois") print("Reset Name: " + myPerson.getName()) print() // Test for and enumerate object properties. print("Variable Property Names & Values") print("====================================") if (Object.keys(myPerson).length !== 0) { for (var property in myPerson) { if (myPerson.hasOwnProperty(property)) { print("[" + property + "] value is a [" + typeof(myPerson[property]) + "]") } } } else { print("No properties") } print("====================================") print("done") |
It prints the following:
MongoDB shell version v4.0.19 Initial Name: Clark Kent Reset Name: Lois Kent Variable Property Names & Values ==================================== [first_name] value is a [string] [last_name] value is a [string] [setFirstName] value is a [function] [getName] value is a [function] ==================================== done bye |
While the prior example shows object construction, this one introduces one variable inheriting the Person function’s properties and another inheriting the same properties before adding a new one to only the variable. The inherited properties, which are functions, are methods of the variable by delegation.
Below is the modified code. It changes the variable from myPerson to myPerson1 and lines 22 through 28 add the new myPerson2 variable and setLastName function. Lines 47 to 58 enumerate across the new second variable, myPerson2 to show you that it has a new setLastName function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | // Create an object with functions. function Person(first_name, last_name) { this.first_name = first_name , this.last_name = last_name , this.setFirstName = function(first_name) { this.first_name = first_name } , this.getName = function() { return this.first_name + " " + this.last_name } } // Create an instance of a variable and print results. var myPerson1 = new Person("Clark","Kent") print("Initial Name: " + myPerson1.getName()) // Call the added function and print the new value. myPerson1.setFirstName("Lois") print("Reset Name: " + myPerson1.getName()) print() // Create an instance of a variable and print results. var myPerson2 = new Person("Bruce","Wayne") print("Initial Name: " + myPerson2.getName()) // Add a function to the local variable. function setLastName(last_name) { this.last_name = last_name } myPerson2.setLastName = setLastName print() // Test for and enumerate object properties. print("Variable Property Names & Values") print("====================================") if (Object.keys(myPerson1).length !== 0) { for (var property in myPerson1) { if (myPerson1.hasOwnProperty(property)) { print("[" + property + "] value is a [" + typeof(myPerson1[property]) + "]") } } } else { print("No properties") } print("====================================") print() // Test for and enumerate object properties. print("Variable Property Names & Values") print("====================================") if (Object.keys(myPerson2).length !== 0) { for (var property in myPerson2) { if (myPerson2.hasOwnProperty(property)) { print("[" + property + "] value is a [" + typeof(myPerson2[property]) + "]") } } } else { print("No properties") } print("====================================") print("done") |
The program shows you the following output:
MongoDB shell version v4.0.19 Initial Name: Clark Kent Reset Name: Lois Kent Initial Name: Bruce Wayne Variable Property Names & Values ==================================== [first_name] value is a [string] [last_name] value is a [string] [setFirstName] value is a [function] [getName] value is a [function] ==================================== Variable Property Names & Values ==================================== [first_name] value is a [string] [last_name] value is a [string] [setFirstName] value is a [function] [getName] value is a [function] [setLastName] value is a [function] ==================================== done bye |
As always, I hope this helps those working through a similar issue.
MongoDB Duplicate Key
How do you avoid encountering a “duplicate key on update” in MongoDB? Well, that’s an interesting question if you’re coming at it from a relational perspective. The answer is understanding that while there may be a natural key, the update needs to use the unique identifier (or, _id
). The unique identifier is essentially a primary key based on a system generated surrogate key, assigned by the insertOne()
or insertMany()
methods. As you’ll notice in the example, it’s not just a numeric value.
The following builds you a test case and is designed to run from the interactive mongo shell. It assumes you understand enough JavaScript to read the code dealing with the JSON (JavaScript Object Notation) structure.
- Insert three rows in a
people
collection with theinsertMany()
method:> db.people.insertMany([ ... {"name" : "joe", "age" : 65} ... ,{"name" : "joe", "age" : 20} ... ,{"name" : "joe", "age" : 49}])
it returns the following unique identifiers:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("60a4a67f8d03d82365e994ab"), ObjectId("60a4a67f8d03d82365e994ac"), ObjectId("60a4a67f8d03d82365e994ad") ] }
- Assign the return value from a
findOne()
method against thepeople
collection to ajoe
variable:> joe = db.people.findOne({"name" : "joe", "age" : 20})
Typically, you can print local variables in the
mongo
shell with theprint
command, like:print(joe)
it returns the following:
[object BSON]
The object is a Binary JSON object, which requires you use the following command:
printjson(joe)
It prints the following JSON element:
{ "_id" : ObjectId("60a4a67f8d03d82365e994ac"), "name" : "joe", "age" : 20 }
- Increment the age value from 20 to 21:
> joe.age++
You can show the change in the age member’s value by printing the JSON element with the
printjson()
function:{ "_id" : ObjectId("60a4a67f8d03d82365e994ac"), "name" : "joe", "age" : 21 }
- If you attempt to replace the JSON structure in the MongoDB collection with anything other than the following, you’ll raise a duplicate key error. You must reference the
_id
value to update the correct element of the collection. The following syntax works:db.people.replaceOne({"_id" : joe._id}, joe)
it should return the following:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
You can verify that the change only affected the originally queried row with the following command:
db.people.find()
it should return the following:
{ "_id" : ObjectId("60a4a67f8d03d82365e994ab"), "name" : "joe", "age" : 65 } { "_id" : ObjectId("60a4a67f8d03d82365e994ac"), "name" : "joe", "age" : 21 } { "_id" : ObjectId("60a4a67f8d03d82365e994ad"), "name" : "joe", "age" : 49 }
I thought this example may help others because the O’Reily’s MongoDB: The Definitive Guide example (on pages 35-37) uses a manually typed string, which doesn’t lend itself to a programmatic solutions. It also failed to clarify that value returned from the findOne()
method and assigned to the local joe
variable would be a BSON (Binary JSON) object; and it didn’t cover any means to easily convert the BSON to a JSON text. The reality is there’s no necessity to cast the BSON into a JSON object, also left out of the text. All that said, it’s still an excellent, if not essential, book for your technical library if you’re working with the MongoDB database.
MongoDB Two Step
Sometimes a bit of humor helps with a new topic. Creating a database in MongoDB is a two-step process, like the Texas Two-Step (a nick name for a country/western two-step danced in common time). A Texas Two Step is a one-two, one-two shuffle which is like the two-step process for how you create a MongoDB database.
While databases in MongoDB are a multiuser sandbox like a relational database, you can’t simply create them and grant them privileges. You must first USE the database and then put a collection (a.k.a., equivalent to a table in MySQL) in it. This blog post shows you how to create a MongoDB play database with an actor collection. It shows you the commands to create the a database with one collection in it and how to verify its existence.
You connect through the mongo shell, like:
mongo |
The first-step requires you to connect to the play
database even though it doesn’t exist. You do that by typing:
> use play |
You can verify you’re in the play
database by typing the db
command, which the mongo
shell treats as an expression. It returns play
, as the name of the current database.
However, when you call the shell show dbs
helper:
> show dbs |
It displays:
admin 0.000GB config 0.000GB local 0.000GB |
You also should note that the play
database still doesn’t exist when you run the JavaScript equivalent to the shell show dbs
helper:
> db.getMongo().getDBs() |
It returns the following list of databases, which excludes the as yet not created play
database:
{ "databases" : [ { "name" : "admin", "sizeOnDisk" : 32768, "empty" : false }, { "name" : "config", "sizeOnDisk" : 61440, "empty" : false }, { "name" : "local", "sizeOnDisk" : 81920, "empty" : false } ], "totalSize" : 176128, "ok" : 1 } |
You create an actor
collection (a.k.a. the equivalent of a relational table) with the following syntax, where db
maps to the play
database that you’re using. Note that you must create or insert one document to begin a document collection. While you can use the insert
method, you should use either the newer insertOne()
or insertMany()
methods.
> db.actors.insertOne({"actor" : {"first_name" : "Chris", "last_name" : "Pine"}, "age" : 40 }) |
A new call to the shell show dbs
helper:
> show dbs |
displays your new play
database:
admin 0.000GB config 0.000GB local 0.000GB play 0.000GB |
You can add two more documents (a.k.a. for rows in a relational database) with the insertMany()
method:
> db.actors.insertMany([{"actor" : {"first_name" : "Chris", "last_name" : "Evans"}, "age" : 39 } ,{"actor" : {"first_name" : "Chris", "last_name" : "Pratt"}, "age" : 41 }]) |
A quick word to the JavaScript novices out there. Don’t forget the square brackets ([{},{},...]
) in the insertMany()
method call or you’ll get an error like this:
2021-04-12T16:20:13.237-0600 E QUERY [js] TypeError: documents.map is not a function : DBCollection.prototype.insertMany@src/mongo/shell/crud_api.js:295:1 @(shell):1:1 |
You call the shell show collections
helper or db.getMongo().getCollectionNames()
JavaScript function to display the collections in the play database. The show collections
displays a list of collections, and the db.getMongo().getCollectionNames()
displays an JavaScript array of collections.
If you’re like me, Mongo’s db convention is a bit risky that I could do something in the wrong database. So, I put the following function into my .mongorc.js (a.k.a., MongoDB Resource file):
prompt = function() { var dbName = db; return dbName + "> " } |
It ensures you will see the current database name to the left of the prompt (“>”), like:
play> |
You can query the documents from the actors
collection with the following:
play> db.actors.find().pretty() |
It returns:
{ "_id" : ObjectId("6074c692813c5a85db9cc9df"), "actor" : { "first_name" : "Chris", "last_name" : "Pine" }, "age" : 40 } { "_id" : ObjectId("6074c7ea813c5a85db9cc9e0"), "actor" : { "first_name" : "Chris", "last_name" : "Evans" }, "age" : 39 } { "_id" : ObjectId("6074c7ea813c5a85db9cc9e1"), "actor" : { "first_name" : "Chris", "last_name" : "Pratt" }, "age" : 41 } |
As always, I hope this helps those looking for a solution.
MongoDB Script Test
There are many ways to test and edit files. A lot of developers only use their favorite Integrated Developer Environment (IDE) but I find testing script files within the scope of a pipelined set of scripts much faster.
The ability to edit a JavaScript file from within the mongo
Shell would be nice but unfortunately, it doesn’t exist. You are able to edit a complex variable with a mechanism quite like the Oracle Database. Rather than leave you hanging on how to edit a complex variable in the mongo
shell, here are the steps before launching into how to test your JavaScript files:
- You can enter this manually during any connection to the
mongo
shell or put it in your.mongorc.js
configuration file, like this in Fedora with the fully qualified filename:EDITOR="/usr/bin/vim"
- Let’s say you have a
stooges
array variable that contains “Moe”, “Curly”, and “Larry” and you want to add “Shemp” to the variable. You can edit thestooges
array variable withvi
and add “Shemp” to it by typing edit and the stooges variable name.edit stooges
- Then, you can test the
stooges
array variable’s new values:stooges
- It returns the following:
[ "Moe", "Curly", "Shemp", "Larry" ]
Unfortunately, these changes to the demo array variable will be lost after you break the connection. While it does afford a quick test case, you should make the changes in the external JavaScript file. Then, the change is there the the next time you access the resource file.
Here’s my quick edit and test script technique for MongoDB from your present working directory:
- Assume you create a
compliment.js
test file, like:/* Declare an array variable and random index value. */ var compliment = ["Bashful","Doc","Dopey","Grumpy","Happy","Sleepy","Sneezy"] var index = Math.floor(Math.random()*7) /* Print a welcome message. */ print("Hello, " + compliment[index] + ".")
- You can edit the
compliment.js
file withvi
and test the script interactively from the present working directory.- You can edit the file with the following syntax:
vi compliment.js
-
then, you can test the
task.sql
file:mongo --nodb --norc < compliment.js
- The
--nodb
option instructs MongoDB to not connect to a database and the--norc
option instructs MongoDB to not load your.mongorc.js
file. Effectively, disabling the database connection and loading of the MongoDB resource file (.mongorc.js) lets you test your code in MongoDB’s Javascript shell unencumbered by any overhead from the MongoDB server.The foregoing script returns the following:
MongoDB shell version v4.0.19 Hello, Sneezy. bye
- If you have the desired outcome, you’re done. However, if you need further change you repeat the process.
- You can edit the file with the following syntax:
As always, I hope this helps those looking for a quick way to accomplish a task.
MongoDB on Fedora 30
I found that MongoDB instructions changed significantly from a year ago. More or less, there was no pre-configured yum
repository. Fortunately, the following web page was very helpful though not specific about Fedora.
Installing MongoDB 4.0 on Fedora 30 requires updating the yum
repository. You need to create the mongodb.repo
file as the root
user in the /etc/yum.repos.d
directory. The mongodb.repo
file should contain the following:
[Mongodb] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc |
After you create the appropriate mongodb.repo
file, you can run the following command to install the MongoDB database.
def install mongodb-org |
It will produce the following log:
MongoDB Repository 31 kB/s | 21 kB 00:00 Dependencies resolved. ============================================================================= Package Arch Version Repository Size ============================================================================= Installing: mongodb-org x86_64 4.0.12-1.amzn1 Mongodb 5.8 k Installing dependencies: mongodb-org-mongos x86_64 4.0.12-1.amzn1 Mongodb 11 M mongodb-org-server x86_64 4.0.12-1.amzn1 Mongodb 20 M mongodb-org-shell x86_64 4.0.12-1.amzn1 Mongodb 13 M mongodb-org-tools x86_64 4.0.12-1.amzn1 Mongodb 29 M Transaction Summary ============================================================================= Install 5 Packages Total download size: 73 M Installed size: 213 M Is this ok [y/N]: y Downloading Packages: (1/5): mongodb-org-4.0.12-1.amzn1.x86_64.rpm 18 kB/s | 5.8 kB 00:00 (2/5): mongodb-org-shell-4.0.12-1.amzn1.x86_ 4.6 MB/s | 13 MB 00:02 (3/5): mongodb-org-mongos-4.0.12-1.amzn1.x86 1.8 MB/s | 11 MB 00:06 (4/5): mongodb-org-tools-4.0.12-1.amzn1.x86_ 6.6 MB/s | 29 MB 00:04 (5/5): mongodb-org-server-4.0.12-1.amzn1.x86 2.5 MB/s | 20 MB 00:08 ----------------------------------------------------------------------------- Total 9.0 MB/s | 73 MB 00:08 warning: /var/cache/dnf/Mongodb-f722cd88d61a4e38/packages/mongodb-org-4.0.12-1.amzn1.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID e52529d4: NOKEY MongoDB Repository 3.0 kB/s | 1.7 kB 00:00 Importing GPG key 0xE52529D4: Userid : "MongoDB 4.0 Release Signing Key <packaging@mongodb.com>" Fingerprint: 9DA3 1620 334B D75D 9DCB 49F3 6881 8C72 E525 29D4 From : https://www.mongodb.org/static/pgp/server-4.0.asc Is this ok [y/N]: y Key imported successfully Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : mongodb-org-tools-4.0.12-1.amzn1.x86_64 1/5 Installing : mongodb-org-shell-4.0.12-1.amzn1.x86_64 2/5 Running scriptlet: mongodb-org-server-4.0.12-1.amzn1.x86_64 3/5 Installing : mongodb-org-server-4.0.12-1.amzn1.x86_64 3/5 Running scriptlet: mongodb-org-server-4.0.12-1.amzn1.x86_64 3/5 Installing : mongodb-org-mongos-4.0.12-1.amzn1.x86_64 4/5 Installing : mongodb-org-4.0.12-1.amzn1.x86_64 5/5 Running scriptlet: mongodb-org-4.0.12-1.amzn1.x86_64 5/5 Verifying : mongodb-org-4.0.12-1.amzn1.x86_64 1/5 Verifying : mongodb-org-mongos-4.0.12-1.amzn1.x86_64 2/5 Verifying : mongodb-org-server-4.0.12-1.amzn1.x86_64 3/5 Verifying : mongodb-org-shell-4.0.12-1.amzn1.x86_64 4/5 Verifying : mongodb-org-tools-4.0.12-1.amzn1.x86_64 5/5 Installed: mongodb-org-4.0.12-1.amzn1.x86_64 mongodb-org-mongos-4.0.12-1.amzn1.x86_64 mongodb-org-server-4.0.12-1.amzn1.x86_64 mongodb-org-shell-4.0.12-1.amzn1.x86_64 mongodb-org-tools-4.0.12-1.amzn1.x86_64 Complete! |
You create a MongoDB service with the following syntax as a privileged user in the sudoer
list:
sudo systemctl enable mongodb.service |
You can then start the mongod
service with the following command as a privileged user in the sudoer
list:
sudo systemctl start mongod.service |
You confirm that it started with the following command as the same privileged user:
sudo service mongod status |
It should produce a log file like this:
● mongod.service - SYSV: Mongo is a scalable, document-oriented database. Loaded: loaded (/etc/rc.d/init.d/mongod; generated) Active: active (running) since Fri 2019-08-16 14:57:22 MDT; 2min 57s ago Docs: man:systemd-sysv-generator(8) Process: 128115 ExecStart=/etc/rc.d/init.d/mongod start (code=exited, stat> Tasks: 27 (limit: 4661) Memory: 47.0M CGroup: /system.slice/mongod.service └─128131 /usr/bin/mongod -f /etc/mongod.conf Aug 16 14:57:21 localhost.localdomain systemd[1]: Starting SYSV: Mongo is a > Aug 16 14:57:21 localhost.localdomain runuser[128127]: pam_unix(runuser:sess> Aug 16 14:57:22 localhost.localdomain runuser[128127]: pam_unix(runuser:sess> Aug 16 14:57:22 localhost.localdomain mongod[128115]: [30B blob data] Aug 16 14:57:22 localhost.localdomain systemd[1]: Started SYSV: Mongo is a s> lines 1-15/15 (END) |
You close the service log with a “q
“. You can determine your version with the following command:
mongod --version |
It should show you something like this:
db version v4.0.12 git version: 5776e3cbf9e7afe86e6b29e22520ffb6766e95d4 OpenSSL version: OpenSSL 1.0.0-fips 29 Mar 2010 allocator: tcmalloc modules: none build environment: distmod: amazon distarch: x86_64 target_arch: x86_64 |
You can connect to the MongoDB shell with the following command:
mongo |
Inside the MongoDB shell, you can run standard MongoDB commands, like:
> use mydb; switched to db mydb > db.version() 4.0.12 > db.stats() { "db" : "mydb", "collections" : 0, "views" : 0, "objects" : 0, "avgObjSize" : 0, "dataSize" : 0, "storageSize" : 0, "numExtents" : 0, "indexes" : 0, "indexSize" : 0, "fileSize" : 0, "fsUsedSize" : 0, "fsTotalSize" : 0, "ok" : 1 } > quit() |
As always, I hope this helps those looking for the missing steps.
Ruby GEM Mongo
While trying to use the Ruby gem
utility to install the MongoDB gem, I encountered an error on a new Fedora 27 instance. This is the error message:
Fetching: bson-4.3.0.gem (100%) Building native extensions. This could take a while... ERROR: Error installing mongo: ERROR: Failed to build gem native extension. current directory: /usr/local/share/gems/gems/bson-4.3.0/ext/bson /usr/bin/ruby -r ./siteconf20180517-49401-1htl7zc.rb extconf.rb mkmf.rb can't find header files for ruby at /usr/share/include/ruby.h extconf failed, exit code 1 Gem files will remain installed in /usr/local/share/gems/gems/bson-4.3.0 for inspection. Results logged to /usr/local/lib64/gems/ruby/bson-4.3.0/gem_make.out [student@localhost ~]$ ruby --version ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux] |
There wasn’t much on the error but I checked the Ruby installation and ruby-devel
package wasn’t installed by default. That’s odd since I choose to install the development components on the workstation.
Not a problem, I simply ran the yum utility as root
through a sudoer user to install the ruby-devel package:
yum install -y ruby-devel |
You should see a successful installation log like:
Display ruby-devel
Package Log File →
Last metadata expiration check: 2:44:39 ago on Thu 17 May 2018 11:01:10 AM MDT. Dependencies resolved. ================================================================================================ Package Arch Version Repository Size ================================================================================================ Installing: ruby-devel x86_64 2.4.3-87.fc27 updates 119 k Transaction Summary ================================================================================================ Install 1 Package Total download size: 119 k Installed size: 283 k Downloading Packages: ruby-devel-2.4.3-87.fc27.x86_64.rpm 269 kB/s | 119 kB 00:00 ------------------------------------------------------------------------------------------------ Total 118 kB/s | 119 kB 00:01 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : ruby-devel-2.4.3-87.fc27.x86_64 1/1 Verifying : ruby-devel-2.4.3-87.fc27.x86_64 1/1 Installed: ruby-devel.x86_64 2.4.3-87.fc27 Complete! |
As I suspected, it fixed the problem immediately when I ran gem
utility to install the mongo
gem. The syntax to install the mongo
gem is:
gem install mongo |
The console output should be:
Building native extensions. This could take a while... Successfully installed bson-4.3.0 Fetching: mongo-2.5.3.gem (100%) Successfully installed mongo-2.5.3 Parsing documentation for bson-4.3.0 Installing ri documentation for bson-4.3.0 Parsing documentation for mongo-2.5.3 Installing ri documentation for mongo-2.5.3 Done installing documentation for bson, mongo after 3 seconds 2 gems installed |
You can now write and run a Ruby test MongoDB connection program. Here’s a basic MongoDBTest.rb
Ruby file:
#!/usr/bin/ruby # Require libraries. require 'rubygems' require 'mongo' # Create a new connection to MongoDB. $client = Mongo::Client.new(['localhost']) puts 'Connected to MongoDB' puts '====================' puts 'Database Names:' puts '---------------' $client.database_names.each{|name| puts name} |
After you create the MongoDBTest.rb
file, you need to change its permissions with the chmod
utility as follows:
chmod 755 MongoDBTest.rb |
Then, you can run it as follows from the directory where you created the file:
./MongoDBTest.rb |
Unless you’ve added something to the base MongoDB instance, it should print:
Connected to MongoDB ==================== Database Names: --------------- admin local |
As always, I hope this helps somebody looking for a straightforward example and solution.
MongoDB on Fedora 27
I was very pleased to see MongoDB 3.4 in the repo for Fedora 27; however, I was quite frankly severely disappointed by the lack of concrete installation steps in the MongoDB in Action, Second Edition. The installation is straightforward as long as you remember that you don’t simply install a mongodb
package. You need to install the mongodb
and mongodb-server
packages as well as some dependents.
As the root
user, you can use yum
to install them like this:
yum install -y mongo* |
After installing the correct packages, you can start the MongoDB Daemon as a service using the root
account or a sudoer account (if you get warning messages, you need to create a mongod file):
service mongod start |
Finally, you can connect to the MongoDB client, like so:
mongo |
Inside the mongo shell, you can check the MongoDB version with the following command:
db.version() |
It should print:
3.4.11 |
Or, you can check the statistics of a new empty instance:
db.stats() |
A fresh installation of MongoDB 3.4.11 on Fedora 27 should return:
{ "db" : "test", "collections" : 0, "views" : 0, "objects" : 0, "avgObjSize" : 0, "dataSize" : 0, "storageSize" : 0, "numExtents" : 0, "indexes" : 0, "indexSize" : 0, "fileSize" : 0, "ok" : 1 } |
You can insert data into MongoDB by creating a database, which is simply a file. You use an existing database or file by using the use
command. If the database or file doesn’t exist already, the use
command creates the database.
use videodb |
You can create a collection or table with the insert function. The following creates an item
collection and adds two JSON objects to the item
collection.
db.item.insert({item:"Star Wars: A New Hope"}) |
It returns:
WriteResult({ "nInserted" : 1 }) |
You can create or add to an item
collection or table with the same insert function call. You simply enclose the list of collection elements in the square brackets of a JavaScript array or list. The following inserts the second and third Star Wars films:
db.item.insert([{item:"Star Wars: The Empire Strikes Back"},{item:"Star Wars: Return of the Jedi"}]) |
It returns:
BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] |
You can query the collection with the find function, like:
db.item.find() |
It returns:
{ "_id" : ObjectId("5b02732435b861850bc51ef1"), "item" : "Star Wars: A New Hope" } { "_id" : ObjectId("5b02734635b861850bc51ef2"), "item" : "Star Wars: The Empire Strikes Back" } { "_id" : ObjectId("5b02734635b861850bc51ef3"), "item" : "Star Wars: Return of the Jedi" } |
You can write large JavaScript files for bulk inserts. For example, the following users.js file:
db.users.insert( [ { contact_account: "CA_20170321_0001" , first_name: "Jonathan" , middle_name: "Eoin" , last_name: "MacGregor" , addresses: { street_address: ["1111 Broadway","Suite 101"] , city: "Oakland" , state: "CA" , zip: "94607" } } , { contact_account: "CA_20170328_0001" , first_name: "Remington" , middle_name: "Alain" , last_name: "Dennison" , addresses: { street_address: ["2222 Broadway","Suite 121"] , city: "Oakland" , state: "CA" , zip: "94607" } } ]) |
You can call the file with the load
command from the interactive MongoDB command line:
load("/home/student/Code/mongodb/users.js") |
You can query the results after you load the collection elements in the users
collection with the following command:
db.users.find() |
It returns:
{ "_id" : ObjectId("5b0270d535b861850bc51eef"), "contact_account" : "CA_20170321_0001", "first_name" : "Jonathan", "middle_name" : "Eoin", "last_name" : "MacGregor", "addresses" : { "street_address" : [ "1111 Broadway", "Suite 101" ], "city" : "Oakland", "state" : "CA", "zip" : "94607" } } { "_id" : ObjectId("5b0270d535b861850bc51ef0"), "contact_account" : "CA_20170328_0001", "first_name" : "Remington", "middle_name" : "Alain", "last_name" : "Dennison", "addresses" : { "street_address" : [ "2222 Broadway", "Suite 121" ], "city" : "Oakland", "state" : "CA", "zip" : "94607" } } |
In both cases, the item
and user
collections add a unique identifier. The unique identifier is much like a surrogate key in a relational database. That’s why you should always define a unique natural key in the definition of your collection.
As always, I hope this helps.
How to install MongoDB
This post shows the yum
command to install the MongoDB packages on Linux. More on setup and use will follow.
You install the MongoDB package with the following yum
command:
yum install -y mongodb |
You should see the following log file, which may also show a missing mirrored site:
Loaded plugins: langpacks, refresh-packagekit cassandra/signature | 819 B 00:00 cassandra/signature | 2.9 kB 00:00 !!! mysql-connectors-community | 2.5 kB 00:00 mysql-tools-community | 2.5 kB 00:00 mysql56-community | 2.5 kB 00:00 updates/20/x86_64/metalink | 2.6 kB 00:00 Resolving Dependencies --> Running transaction check ---> Package mongodb.x86_64 0:2.4.6-1.fc20 will be installed --> Processing Dependency: v8 for package: mongodb-2.4.6-1.fc20.x86_64 --> Processing Dependency: libv8.so.3()(64bit) for package: mongodb-2.4.6-1.fc20.x86_64 --> Processing Dependency: libtcmalloc.so.4()(64bit) for package: mongodb-2.4.6-1.fc20.x86_64 --> Processing Dependency: libboost_program_options.so.1.54.0()(64bit) for package: mongodb-2.4.6-1.fc20.x86_64 --> Processing Dependency: libboost_iostreams.so.1.54.0()(64bit) for package: mongodb-2.4.6-1.fc20.x86_64 --> Processing Dependency: libboost_filesystem.so.1.54.0()(64bit) for package: mongodb-2.4.6-1.fc20.x86_64 --> Running transaction check ---> Package boost-filesystem.x86_64 0:1.54.0-12.fc20 will be installed ---> Package boost-iostreams.x86_64 0:1.54.0-12.fc20 will be installed ---> Package boost-program-options.x86_64 0:1.54.0-12.fc20 will be installed ---> Package gperftools-libs.x86_64 0:2.1-4.fc20 will be installed ---> Package v8.x86_64 1:3.14.5.10-18.fc20 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: mongodb x86_64 2.4.6-1.fc20 fedora 30 M Installing for dependencies: boost-filesystem x86_64 1.54.0-12.fc20 updates 66 k boost-iostreams x86_64 1.54.0-12.fc20 updates 59 k boost-program-options x86_64 1.54.0-12.fc20 updates 147 k gperftools-libs x86_64 2.1-4.fc20 updates 266 k v8 x86_64 1:3.14.5.10-18.fc20 updates 3.0 M Transaction Summary ================================================================================ Install 1 Package (+5 Dependent packages) Total download size: 34 M Installed size: 101 M Downloading packages: (1/6): boost-iostreams-1.54.0-12.fc20.x86_64.rpm | 59 kB 00:00 (2/6): boost-filesystem-1.54.0-12.fc20.x86_64.rpm | 66 kB 00:00 (3/6): boost-program-options-1.54.0-12.fc20.x86_64.rpm | 147 kB 00:00 (4/6): gperftools-libs-2.1-4.fc20.x86_64.rpm | 266 kB 00:00 (5/6): v8-3.14.5.10-18.fc20.x86_64.rpm | 3.0 MB 00:00 (6/6): mongodb-2.4.6-1.fc20.x86_64.rpm | 30 MB 00:04 -------------------------------------------------------------------------------- Total 7.6 MB/s | 34 MB 00:04 Running transaction check Running transaction test Transaction test succeeded Running transaction (shutdown inhibited) Installing : 1:v8-3.14.5.10-18.fc20.x86_64 1/6 Installing : boost-program-options-1.54.0-12.fc20.x86_64 2/6 Installing : gperftools-libs-2.1-4.fc20.x86_64 3/6 Installing : boost-filesystem-1.54.0-12.fc20.x86_64 4/6 Installing : boost-iostreams-1.54.0-12.fc20.x86_64 5/6 Installing : mongodb-2.4.6-1.fc20.x86_64 6/6 Verifying : boost-iostreams-1.54.0-12.fc20.x86_64 1/6 Verifying : boost-filesystem-1.54.0-12.fc20.x86_64 2/6 Verifying : gperftools-libs-2.1-4.fc20.x86_64 3/6 Verifying : mongodb-2.4.6-1.fc20.x86_64 4/6 Verifying : boost-program-options-1.54.0-12.fc20.x86_64 5/6 Verifying : 1:v8-3.14.5.10-18.fc20.x86_64 6/6 Installed: mongodb.x86_64 0:2.4.6-1.fc20 Dependency Installed: boost-filesystem.x86_64 0:1.54.0-12.fc20 boost-iostreams.x86_64 0:1.54.0-12.fc20 boost-program-options.x86_64 0:1.54.0-12.fc20 gperftools-libs.x86_64 0:2.1-4.fc20 v8.x86_64 1:3.14.5.10-18.fc20 Complete! |
Hope this helps those looking for the command.