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.
Let's get started
For this tutorial we are going to use two collections of data that are bundled in ArangoDB: worldVertices and worldEdges.
1 |
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:
1 |
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.
jq
command available on your machine before the import.and import it into MongoDB:
1 2 |
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
1 2 3 |
mongo > db.worldVertices.findOne( {type: 'country'} ) |
ArangoDB
1 2 3 |
arangosh > db.worldVertices.firstExample( {'type': 'country'} ) |
The results will approximately look like this:
1 2 3 4 5 6 7 8 |
{ "_id" : "worldVertices/country-algeria", "_key" : "country-algeria", "_rev" : "233", "code" : "DZA", "name" : "Algeria", "type" : "country" } |
1 2 3 4 5 6 7 8 |
{ "_key" : "country-brazil", "_id" : "worldVertices/country-brazil", "_rev" : "281", "code" : "BRA", "name" : "Brazil", "type" : "country" } |
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
1 2 3 |
mongo > db.worldVertices.find( {'type': 'continent'} ) |
ArangoDB
1 2 3 |
arangosh > db.worldVertices.byExample( {'type': 'continent'} ).toArray() |
The results should look like something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "_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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[ { "_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
1 2 3 4 |
mongo > db.worldVertices.update({type: 'country'}, { $set: {population: 10}}, { multi: true} ) |
ArangoDB
1 2 3 4 |
arangosh > db.worldVertices.updateByExample( {'type': 'country'}, {'population': 10} ) |
And the results that you will see:
1 2 3 4 5 6 7 |
WriteResult( { "nMatched" : 40, "nUpserted" : 0, "nModified" : 40 } ) |
1 |
40 |
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:
1 2 3 |
mongo > db.worldVertices.find( {'name': 'Australia', 'type': 'country'} ) |
1 2 3 |
arangosh > db.worldVertices.firstExample( {'name': 'Australia', 'type': 'country'} ) |
The results that you will see will look like this:
1 2 3 4 5 6 7 8 9 10 11 |
{ "_id" : ObjectId("57aaf4a42cf4ab073b244ff7"), "theType" : 2300, "id" : "worldVertices/country-australia", "key" : "country-australia", "rev" : "Tk84gRS--", "code" : "AUS", "name" : "Australia", "type" : "country", "population" : "10" } |
1 2 3 4 5 6 7 8 9 |
{ "_key" : "country-australia", "_id" : "worldVertices/country-australia", "_rev" : "2014", "code" : "AUS", "name" : "Australia", "type" : "country", "population" : 10 } |
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
1 2 3 4 5 6 7 8 9 10 |
mongo > db.worldVertices.aggregate( { $group: { _id: "$type", population: {$sum: "$population"} } } ) |
ArangoDB
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "_id" : "capital", "population" : 0 } { "_id" : "country", "population" : 400 } { "_id" : "continent", "population" : 0 } { "_id" : "root", "population" : 0 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[ { "_id" : "capital", "population" : 0 } { "_id" : "country", "population" : 400 } { "_id" : "continent", "population" : 0 } { "_id" : "root", "population" : 0 } ] |
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
1 2 3 4 5 6 7 8 9 10 11 |
mongo > db.worldVertices.aggregate([ { $match: { type: "country"} }, { $group: { _id: "$type", population: {$sum: "$population"} } } ]) |
ArangoDB
1 2 3 4 5 6 7 8 9 |
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:
1 2 3 4 |
{ "_id" : "country", "population" : 400 } |
1 2 3 4 5 6 |
[ { "_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
1 2 3 4 5 6 7 8 |
mongo > db.worldVertices.find({ $and: [ { name: {$gt: 'Bul'}}, { name: {$lt: 'Cb'}} ] }) |
ArangoDB
1 2 3 4 5 6 |
arangosh > db._query(` FOR vertex IN worldVertices FILTER vertex.name > "Bul" AND vertex.name < "Cb" RETURN vertex `) |
Your results will be like this:
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 |
{ "_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 |
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 |
[ { "_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 ] |
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
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 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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:
1 2 3 4 |
{ "name" : "Australia", "capital" : { "name" : "Canberra" } } |
1 2 3 4 5 6 |
[ { "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
1 2 3 4 5 6 7 8 9 10 |
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:
1 |
mongo > db.worldVertices.count() |
ArangoDB
1 |
arangosh > db.worldVertices.count() |
The results should look like this:
1 |
87 |
1 |
87 |
Delete documents:
1 2 3 |
mongo > db.worldVertices.remove( {type: 'capital'} ) |
1 2 3 |
arangosh > db.worldVertices.removeByExample( {type: 'capital'} ) |
The results will be the following:
1 2 3 |
WriteResult( { "nRemoved" : 40 } ) |
1 |
40 |
Count the documents again to check:
1 |
mongo > db.worldVertices.count() |
1 |
arangosh > db.worldVertices.count() |
And the results should be:
1 |
47 |
1 |
47 |
Learn more
By now you should have a sound understanding of how to run some operations in ArangoDB in comparison to MongoDB. But there is much more to know and so much more you can do:
- Check out a feature comparison between MongoDB and ArangoDB
- Have a look at the performance benchmark of ArangoDB
- Explore the ArangoDB Cookbook for practical solutions to common problems
- Confused by the jargon? There’s a glossary that can help you
- Made a cool thing? Tell us more about it!
- Want to get involved? ArangoDB is Open Source with many ways to contribute
- Still wondering? Get in touch via our Slack Community Channel or on Twitter @arangodb