home shape

Modelling data in ArangoDB vs MongoDB

This is a short tutorial showing you the comparison between performing queries in ArangoDB and MongoDB.

Both, ArangoDB and MongoDB, are “NoSQL databases” and might look pretty similar at first glance. When it comes to data modelling and data querying, they are somewhat different. The main difference being ArangoDB’s flexibility and functionality when working with graph data.  In this 10 min tutorial we will show the differences in syntax between the two databases. We will have a look at some simple examples, but also touch base with a few more complex ones.

Note: Before we start make sure that you have both, ArangoDB 3.0 and MongoDB 3.2, databases installed.
background img reverse min

Let's get started

For this tutorial we are going to use two collections of data that are bundled in ArangoDB: worldVertices and worldEdges.

echo 'require("@arangodb/graph-examples/example-graph.js").loadGraph("worldCountry")' | arangosh --server.password ""
To have the same datasets in both databases, we need to first dump the data from ArangoDB:
arangodump /tmp/arangoExport --server.password ""

Using arangodump will export the raw data including some metadata which you could feed into another ArangoDB instance right away. However, to load the data into MongoDB we need to have the raw data without ArangoDB specific metadata. To extract the raw data we are using the jq tool. The raw data is contained within the _data key in the dump file.

Note: Make sure that you have jq command available on your machine before the import.

and import it into MongoDB:

cat /tmp/arangoExport/worldVertices_*.data.json | jq -c .data | mongoimport --collection worldVertices
cat /tmp/arangoExport/worldEdges_*.data.json | jq -c .data | mongoimport --collection worldEdges

Now that you are all set, we can start with some query examples.

Find a document

The following query finds the first document matching a filter criteria. In the example below we are searching for the first document where the type is “country”. We don’t specify any sorting neither in MongoDB nor in ArangoDB, therefore the results are more or less random in both cases.

MongoDB

mongo > db.worldVertices.findOne(
  {type: 'country'}
)

ArangoDB

mongo > db.worldVertices.findOne(
  {type: 'country'}
)

The results will approximately look like this:

    {
  "_id" : "worldVertices/country-algeria",
  "_key" : "country-algeria",
  "_rev" : "233",
  "code" : "DZA",
  "name" : "Algeria",
  "type" : "country"
}
{ 
  "_key" : "country-brazil", 
  "_id" : "worldVertices/country-brazil", 
  "_rev" : "281", 
  "code" : "BRA", 
  "name" : "Brazil", 
  "type" : "country" 
}
background img

Find a list of documents

With the following query you will find all documents matching a set-up criteria. In this case all documents where type is “continent” will be displayed.

MongoDB

mongo > db.worldVertices.find( 
  {'type': 'continent'}
)

ArangoDB

arangosh > db.worldVertices.byExample(
  {'type': 'continent'}
).toArray()

The results should look like something like this:

{ 
  "_id" : "worldVertices/continent-asia", 
  "_key" : "continent-asia", "_rev" : "212", 
  "name" : "Asia", 
  "type" : "continent" 
}
{ 
  "_id" : "worldVertices/continent-africa", 
  "_key" : "continent-africa", 
  "_rev" : "209", 
  "name" : "Africa", 
  "type" : "continent" 
}
 
...more
[ 
  { 
    "_key" : "continent-africa", 
    "_id" : "worldVertices/continent-africa", 
    "_rev" : "209", 
    "name" : "Africa", 
    "type" : "continent" 
  }, 
  { 
    "_key" : "continent-australia", 
    "_id" : "worldVertices/continent-australia", 
    "_rev" : "215", 
    "name" : "Australia", 
    "type" : "continent" 
  },
 
...more
]

Update documents

Here we are going to update all documents that match a filter “type: country” with a “population: 10″.

MongoDB

mongo > db.worldVertices.update({type: 'country'},
{ $set: {population: 10}},
{ multi: true}
)

ArangoDB

arangosh > db.worldVertices.updateByExample(
  {'type': 'country'}, 
  {'population': 10}
)

And the results that you will see:

WriteResult(
  { 
    "nMatched" : 40, 
    "nUpserted" : 0, 
    "nModified" : 40 
  }
)
40
background img reverse min

Now that we have executed the query, let’s check if the update took place. For simplicity we are going to list one document that got updated.

 

Find a document

MongoDB

mongo > db.worldVertices.find( 
  {'name': 'Australia', 'type': 'country'} 
)

ArangoDB

arangosh > db.worldVertices.firstExample(
  {'name': 'Australia', 'type': 'country'}
)

The results that you will see will look like this:

{
"_id" : ObjectId("57aaf4a42cf4ab073b244ff7"),
"theType" : 2300,
"id" : "worldVertices/country-australia",
"key" : "country-australia",
"rev" : "Tk84gRS--",
"code" : "AUS",
"name" : "Australia",
"type" : "country",
"population" : "10"
}
{ 
  "_key" : "country-australia", 
  "_id" : "worldVertices/country-australia", 
  "_rev" : "2014", 
  "code" : "AUS", 
  "name" : "Australia", 
  "type" : "country", 
  "population" : 10 
}

Note: Now that we have covered some simple steps, we are going to move on to a few more complex examples. Here we are going to use ArangoDB’s powerful & versatile SQL-like query language – AQL – for the first time.

Aggregate

Below is an example of how to use the aggregate function in both databases. Here we are going to group all documents in the worldVertices collection by their type and display what amount of population each type has.

MongoDB

mongo > db.worldVertices.aggregate(
  {
    $group: 
      {
        _id: "$type",
        population: {$sum: "$population"}
      }
   }
)

ArangoDB

arangosh > db._query(`
  FOR item IN worldVertices
    COLLECT types = item.type INTO group
  RETURN {
           _id: types,
           population: SUM(group[*].item.population)
	 }
`)

The results will look like something like this:

{ 
  "_id" : "capital", 
  "population" : 0 
}
{ 
  "_id" : "country", 
  "population" : 400 
}
{ 
  "_id" : "continent", 
  "population" : 0 
}
{ 
  "_id" : "root", 
  "population" : 0 
}
[
{ 
  "_id" : "capital", 
  "population" : 0 
}
{ 
  "_id" : "country", 
  "population" : 400 
}
{ 
  "_id" : "continent", 
  "population" : 0 
}
{ 
  "_id" : "root", 
  "population" : 0 
}
]
background img

Aggregate with a filter

The below example shows how to use the aggregate function with a filter. Here we will group all documents in the worldVertices collection by type, list the amount of population each type has, but by using the filter we will only see the result for type: “country”.

MongoDB

mongo > db.worldVertices.aggregate([
  {
    $match: { type: "country"}
  },
  {
    $group: {
              _id: "$type",
              population: {$sum: "$population"}
            }
  }
])

ArangoDB

arangosh > db._query(`
  FOR item IN worldVertices
    FILTER item.type == 'country'
      COLLECT types = item.type INTO group
  RETURN {
      _id: types,
      population: SUM(group[*].item.population)
    }
`)

You will see the following results:

{ 
  "_id" : "country", 
  "population" : 400 
}
[
  { 
    "_id" : "country", 
    "population" : 400 
  }
]

Range select

With the range select function we are going to filter all documents in the worldVertices collection where the name range is between “Bul” and “Cb”.

MongoDB

mongo > db.worldVertices.find({ 
$and: 
  [     
    { name: {$gt: 'Bul'}},     
    { name: {$lt: 'Cb'}} 
  ] 
})
 

ArangoDB

arangosh > db._query(`
  FOR item IN worldVertices
    FILTER item.type == 'country'
      COLLECT types = item.type INTO group
  RETURN {
      _id: types,
      population: SUM(group[*].item.population)
    }
`)

Your results will be like this:

{ 
  "_id" : "worldVertices/country-bulgaria", 
  "_key" : "country-bulgaria", 
  "_rev" : "287", 
  "code" : "BGR", 
  "name" : "Bulgaria", 
  "type" : "country", 
  "population" : 10 
}
{ 
  "_id" : "worldVertices/country-burkina-faso", 
  "_key" : "country-burkina-faso", 
  "_rev" : "290", 
  "code" : "BFA", 
  "name" : "Burkina Faso", 
  "type" : "country", 
  "population" : 10 
}
{ 
  "_id" : "worldVertices/country-burundi", 
  "_key" : "country-burundi", 
  "_rev" : "293", 
  "code" : "BDI", 
  "name" : "Burundi", 
  "type" : "country", 
  "population" : 10 
}
 
.... more
[
  { 
    "_id" : "worldVertices/country-bulgaria", 
    "_key" : "country-bulgaria", 
    "_rev" : "287", 
    "code" : "BGR", 
    "name" : "Bulgaria", 
    "type" : "country", 
    "population" : 10 
  }
  { 
    "_id" : "worldVertices/country-burkina-faso", 
    "_key" : "country-burkina-faso", 
    "_rev" : "290", 
    "code" : "BFA", 
    "name" : "Burkina Faso",
    "type" : "country", 
    "population" : 10 
  }
  { 
    "_id" : "worldVertices/country-burundi", 
    "_key" : "country-burundi", 
    "_rev" : "293", 
    "code" : "BDI", 
    "name" : "Burundi", 
    "type" : "country", 
    "population" : 10 
  }
 
.... more
]
background img reverse min

JOINs

With the following example we are going to do a JOIN between two documents in two collections (worldVertices and worldEdges). We will find out what the capital of Australia is.

MongoDB

mongo > db.worldVertices.aggregate( 
  [
    {         
      $match: {
                name: 'Australia',
                type: 'country'
              },     
    },     
    {     
      $lookup: {
                 from: 'worldEdges', 
                 localField: '_id', 
                 foreignField: '_to', 
                 as: 'toCities'
               }     
    }, 
    {
      $unwind: "$toCities"
    }, 
    {     
      $lookup: {
                 from: 'worldVertices', 
                 localField: 'toCities._from', 
                 foreignField: '_id', 
                 as: 'capital'
               }     
    }, 
    {
      $unwind: "$capital"
    }, 
    {
      $match: {
                "capital.type":"capital"
              }
    }, 
    {
      $project: {
                  _id: 0, 
                  name: 1, 
                  "capital.name": 1
                }
    }
  ] 
).pretty()

ArangoDB

arangosh > db._query(`
  FOR country in worldVertices 
    FILTER country.name == 'Australia' AND 
           country.type == 'country' 
  FOR edge in worldEdges 
    FILTER edge._to == country._id 
  FOR capital IN worldVertices 
    FILTER capital._id == edge._from 
  RETURN {
           name: country.name, 
           capital: {name: capital.name}
         }
`)

The results will look like this:

{ 
  "name" : "Australia", 
  "capital" : { "name" : "Canberra" } 
}
[
  { 
    "name" : "Australia", 
    "capital" : { "name" : "Canberra" } 
  }
]

Graph Queries:
What you can do with ArangoDB and cannot do with MongoDB

While querying the immediate connection in the graph (country to capital) is still doable via JOINs (previous examples above) it can rather quickly become more difficult once you dig deeper into the graph and perform more complex queries.

As the nature of our example data is a graph and ArangoDB is also a graph database, we can show you with the example below how this procedure can be done in an easier and neater way. In this case, ArangoDB offers more flexibility in comparison to MongoDB.

MongoDB

ArangoDB

arangosh > db._query("
  FOR country in worldVertices 
    FILTER country.name == 'Australia' AND 
           country.type == 'country' 
      FOR capital IN INBOUND country worldEdges 
  RETURN {
           name: country.name, 
           capital: {name: capital.name}
         }
")

Delete a set of documents

As the final step of this quick tutorial, we are going to delete a set of documents based on a filter – document type is “capital

MongoDB

First, count the documents in a collection:

mongo > db.worldVertices.count()

ArangoDB

arangosh > db.worldVertices.count()

The results should look like this:

87

.

87

Delete documents:

mongo > db.worldVertices.remove(
  {type: 'capital'}
)
arangosh > db.worldVertices.removeByExample(
  {type: 'capital'}
)

The results will be the following:

WriteResult(
  { "nRemoved" : 40 }
)
40

Count the documents again to check:

mongo >  db.worldVertices.count()
arangosh > db.worldVertices.count()

And the results should be:

47
47