Getting started with MongoDB and Eiffel
In this article, we’ll have a look at integrating MongoDB, a very popular NoSQL open source database with the MongoDB Eiffel Driver, at the moment under development.
MongoDB is written in C++ and has quite a number of solid features such as map-reduce, auto-sharding, replication, high availability etc, and it's the most popular NoSQL databases and it's one of the most used solutions including Relational Databases. DB engine ranking.
What is MongoDB?
MongoDB is a NoSQL database where we can store data in BSON format where a key represents the property and the value represents the property value stored against a key. Below we describe a few key points about MongoDB itself:
- stores data in BSON (JSON-like) documents that can have various structures.
- uses dynamic schemas, which means that we can create records without predefining anything.
- the structure of a record can be changed simply by adding new fields or deleting existing ones.
The above-mentioned data model gives us the ability to represent hierarchical relationships, to store arrays and other more complex structures easily.
Documents
In Mongo, the document represents a data structure that can hold any number of key and value pairs.
Collections
In Mongo, often, documents with the same structure are put into a bucket, called a collection. You can think of a collection as a table in a relational database, where every row represents a document.
No Schema
This is one of the key differences between a SQL and NoSQL database, it means it has no predefined schema — it can hold anything in BSON format.
Mapping Relational Databases Concepts to MongoDB
Understanding concepts in MongoDB become easier if we can compare them to relational database structures.
Let’s see the analogies between Mongo and a traditional MySQL system:
- A table in MySQL becomes a Collection in Mongo
- Row becomes a Document
- Column becomes a Field
- Joins are defined as linking and embedded documents
This is a simplistic way to look at the MongoDB core concepts of course, but nevertheless useful, to learn more check the following article
To learn more:
- MongoDB Tutorials List of available MongoDB Tutorials part of MongoDB Manual.
Now, let’s dive into implementation to understand this powerful database.
Get the MongoDB Eiffel Driver
$ git clone https://github.com/jvelilla/mongo-eiffel-driver
$ cd mongo-eiffel-driver
Using MongoDB
Now, let’s start implementing Mongo queries with Eiffel. We will follow with the basic CRUD operations as they are the best to start with.
Make a Connection
l_client: MONGODB_CLIENT
...
-- Initialize and create a new mongobd client instance.
create l_client.make ("mongodb://127.0.0.1:27017")
Connecting to a Database
Now, let’s connect to our database. It is interesting to note that Databases are automatically created on the MongoDB server upon insertion of the first document into a collection. There is no need to create a database manually.
When Mongo sees that database doesn’t exist, it will create it for us
.
class
APPLICATION
create
make
feature {NONE} -- Initialization
make
-- Run application.
local
l_client: MONGODB_CLIENT
l_database: MONGODB_DATABASE
do
-- Initialize and create a new mongobd client instance.
create l_client.make ("mongodb://127.0.0.1:27017")
print ("Connected to the database successfully%N")
-- Accessing a database
l_database := l_client.get_database ("newDB")
-- Exists collection "newCollection"?
if l_database.has_collection ("newCollection") then
print ("Collection newCollection exists%N")
else
print ("Collection newCollection does not exists%N")
end
end
end
Query Existing Databases
Show the existing list of known databases.
l_client: MONGODB_CLIENT
l_database_names: LIST [STRING]
do
-- Initialize and create a new mongobd client instance.
create l_client.make ("mongodb://127.0.0.1:27017")
l_database_names := l_client.get_database_names (Void)
across l_database_names as ic loop print (ic.item + "%N") end
end
Create a Collection
Now, we will show you how to create a Collection (table equivalent to MongoDB) for our database.
Firs,t we need to connect to our database as follow
-- Initialize and create a new mongobd client instance.
create l_client.make ("mongodb://127.0.0.1:27017")
-- Accessing a database
l_database := l_client.get_database ("db_name")
Now we can display the current collections in our database db_name
.
across l_database.get_collection_names (VOID) as ic loop print (ic.item + "%N") end
Once we have connected to our database, we can create our collection with the following code.
-- create a new collection using MONGODB_DATABASE
l_collection := l_database.create_collection ("my_new_collection", Void)
across l_database.get_collection_names (VOID) as ic loop print (ic.item + "%N") end
There is another option to create collections in MongoDB. In the previous example we create the collection manually, but we can create collections automatically upon insertion of the first document.
Basic CRUD Operations
This section demonstrates the basics of using the Eiffel Driver to interact with MongoDB.
Create a Document
To insert/create documents into a collection, first we obtain an object instance of MONGODB_COLLECTION
via a MONGODB_CLIENT
. Then, use the feature insert_one
to add BSON documents to the collection.
create_document
local
l_client: MONGODB_CLIENT
l_collection: MONGODB_COLLECTION
l_doc: BSON
l_oid: BSON_OID
l_error: BSON_ERROR
do
-- Create a new client instance.
create l_client.make ("mongodb://localhost:27017/?appname=insert-example")
-- Get a colletion using MONGODB_CLIENT.get_collection feature
-- with database name and the collection name.
l_collection := l_client.get_collection ("mydb", "mycoll")
create l_doc.make
create l_oid.make (Void)
l_doc.bson_append_oid ("_id", l_oid)
l_doc.bson_append_utf8 ("hello", "eiffel")
create l_error
l_collection.insert_one (l_doc, Void, Void, l_error)
end
The document will be inserted into a database named mydb
under collection mycoll
{"_id":"5ac6d0689c990617c8007ef2","hello":"eiffel"}
Read a Document
To read a document we need to query a MongoDB collection, using the feature find_with_opts
. This returns a cursor MONGODB_CURSOR
to the matching documents.
We will use the following document as an example
{"hello":"eiffel"}
The following example iterates through the result cursors and prints the matches to stdout as JSON strings.
read_document
local
l_client: MONGODB_CLIENT
l_collection: MONGODB_COLLECTION
l_query: BSON
l_cursor: MONGODB_CURSOR
l_after: BOOLEAN
do
create l_client.make ("mongodb://localhost:27017/?appname=find-example")
l_collection := l_client.get_collection ("mydb", "mycoll")
create l_query.make
l_query.bson_append_utf8 ("hello", "eiffel")
l_cursor := l_collection.find_with_opts (l_query, Void, Void)
from
until
l_after
loop
if attached l_cursor.next as l_bson then
print (l_bson.bson_as_canonical_extended_json)
print ("%N")
else
l_after := True
end
end
end
Update a Document
We will use the database mydb
and we will insert a new document into the mycoll
collection.
Then using the BSON_ID we use in the previous insert will update it with a different value and a new field, calling the feature update_one
update_document
local
l_client: MONGODB_CLIENT
l_collection: MONGODB_COLLECTION
l_doc: BSON
l_update: BSON
l_query: BSON
l_oid: BSON_OID
l_error: BSON_ERROR
l_subdoc: BSON
do
create l_client.make ("mongodb://localhost:27017/?appname=update-example")
l_collection := l_client.get_collection ("mydb", "mycoll")
-- First we create a document
create l_oid.make (Void)
create l_doc.make
l_doc.bson_append_oid ("_id", l_oid)
l_doc.bson_append_utf8 ("key", "Hello Eiffel")
create l_error.default_create
l_collection.insert_one (l_doc,Void, Void, l_error)
- - Update the document we just create
create l_query.make
l_query.bson_append_oid ("_id", l_oid)
create l_subdoc.make
l_subdoc.bson_append_utf8 ("key", "Hello MongoDB from Eiffel")
l_subdoc.bson_append_boolean ("updated", True)
create l_update.make
l_update.bson_append_document ("$set", l_subdoc)
create l_error.default_create
l_collection.update_one (l_query, l_update, Void, Void, l_error)
end
Delete a Document
Here we will show you how to use the feature delete_one
to delete a document.
The following code inserts a sample document into the database mydb
and collection mycoll
. Then, it deletes all documents matching
{"hello" : "world"}
.
delete_document
local
l_client: MONGODB_CLIENT
l_collection: MONGODB_COLLECTION
l_doc: BSON
l_oid: BSON_OID
l_error: BSON_ERROR
do
create l_client.make ("mongodb://localhost:27017/?appname=delete-example")
l_collection := l_client.get_collection ("test", "test")
create l_oid.make (Void)
create l_doc.make
l_doc.bson_append_oid ("_id", l_oid)
l_doc.bson_append_utf8 ("hello", "world")
-- insert a document
create l_error
l_collection.insert_one (l_doc, Void, Void, l_error)
-- delete the document.
create l_doc.make
l_doc.bson_append_oid ("_id", l_oid)
create l_error
l_collection.delete_one (l_doc, Void, Void, l_error)
end
Count documents
Counting the number of documents in a MongoDB collection is similar to performing a find operation. This example counts the number of documents matching {"hello" : "world"}
in the database mydb
and collection mycoll
.
count_documents
local
l_client: MONGODB_CLIENT
l_doc: BSON
l_collection: MONGODB_COLLECTION
l_error: BSON_ERROR
l_count: INTEGER_64
do
create l_client.make ("mongodb://localhost:27017/?appname=delete-example")
l_collection := l_client.get_collection ("mydb", "mycoll")
create l_doc.make
l_doc.bson_append_utf8 ("hello", "world")
create l_error
l_count := l_collection.count ((create {MONGODB_QUERY_FLAG}).mongoc_query_none, l_doc, 0, 0, Void, l_error)
if l_count < 0 then
print ("Error message: " + l_error.message)
else
print ("Number of documents:" + l_count.out)
end
end
Conclusion
This blog is a quick introduction to Eiffel MongoDB driver tutorial based in the MongoDB-C driver.
To get the code of all these examples got the GitHub repository.