Getting started with RavenDB

Milan Groeneweg
12 min readSep 16, 2022

--

*update* I have since had the amazing opportunity to interview the CEO of RavenDB. Check it out here: https://youtu.be/kGfbsi5gKgc

RavenDB is an open — source document-oriented NoSQL database designed for .NET and built with C#. But what does any of this mean and what are the benefits of a NoSQL database? How do I setup a database in RavenDB, add data, query and make sure its the right fit for my organization? This blog hopes to answer all those questions for you.

In this blog we will:

Compare RavenDB to other databases

Set RavenDB up for Windows

Create a database in the RavenDB studio

Build a simple CRUD

Test query’s in the RavenDB studio

Setup multiple nodes

Set RavenDB up for Docker

As well as dive a little deeper in some of the fundamental properties of RavenDB.

Background

Originally named "Rhino DivanDB", RavenDB began in 2008 as a project by Oren Eini and is developed by Hibernating Rhinos Ltd. The company claims it was the first document database to run natively in the .NET Framework. It was an early document database to offer ACID guarantees.

Comparison

Setup (Windows)

Download

First things first we will download RavenDB here: https://ravendb.net/download.

Configure folders

Now we will setup the folders where we will extract this package, make a folder named “RavenDB” inside we will have a folder named “Cluster parent” inside of Cluster parent we will be storing our nodes. For now we will have just one node, node A. So create a folder called Node A. Your path should be as followed: C:\RavenDB\Cluster Parent\Node A. Now extract the downloaded zip in Node A.

We can now start the setup wizard by running run.ps1 in PowerShell (right-click, Run with PowerShell).

This should open your browser. Accept the agreements and now it should redirect you to this:

Choose “New Cluster”.

Certificate

Next you will be asked to choose a certificate, choose the one which best suits you. If you want to know more about which certificate is right for your organization I would recommend you read the official docs first: https://ravendb.net/docs/article-page/5.4/csharp/server/security/overview.

For the sake of this article I will go with unsecure.

Configure server addresses

Next we will be asked to configure our server addresses.

Using the command netstat -a you can see which ports are taken, if you are setting RavenDB up for multiple devices make sure the port you choose is available on all devices.

Once this is done we can now run the server on url: http://127.0.0.1:8080.

If you did choose to get a certificate you would’ve been asked to create your own domain so this url may differ based on your settings.

Now we will be greeted by the dashboard of RavenDB:

Creating a database in RavenDB

To create a database in the dashboard go to databases and click on “New database”. Create a database called “Northwind” and we can leave the settings on default.

Importing a dataset

To import a dataset in your database go to “Tasks” and click “Create sample data” then click “Create”.

Data in RavenDB

If we now go back to our database we can see it contains around 915 MB’s worth of data, 1,059 documents in 9 collections.

Collections are the basic building blocks inside RavenDB. Every document belongs to a collection and a collection typically holds similar documents (though it doesn’t have to). Collections are comparable to tables in relational databases, but unlike tables collections are free of any structure or schema. Collections are very important for organizing our data.

We can now open a document in our database like an order, go to the “Orders collection” in your database and you will see a row of orders:

Lets inspect the top one, open up Orders/830-A.

Here we have the JSON of Orders/830-A.

There are a few things to note here: This one document holds 202 lines of code that includes nested objects and arrays, this might surprise you if you are used to relational databases. In a document in RavenDB we are able to store arbitrarily complex data as a single unit, in Order/830-A we aren’t just storing a few columns ( as we’d have to in relational databases) instead we are storing tons of information with complex types like arrays in 1 document. This makes it way easier to organize, optimize and access our data.

Building a simple CRUD.

Let’s start off by making a new database for our CRUD (Create, Read, Update, Delete). Let’s say we are making a videogame so lets name the database “Game”. For our game we are gonna make a CRUD for the videogame characters.

We will write our code in a simple console application using the RavenDB API. We will be writing the code in C#.

Installing the ravenDB Client

The ravenDB client can be installed here: https://www.nuget.org/packages/RavenDB.Client

The code

Once the client has been added to your project we can start writing code. First thing we need to do is set up access to the RavenDB cluster that we want to talk to. We do this by instantiating DocumentStore and configuring it as shown:

var store = new DocumentStore{Urls = new[] {"https://localhost:8080"},Database = "Game"};store.Initialize();

Now we have the DocumentStore set up. We use the DocumentStore to communicate with the RavenDB cluster, it is the starting point for any RavenDB application. In the code we let it know we are talking to our node A (https://localhost:8080) and we define the database we are using (Game). Typically you will have one instance of a DocumentStore per application (singleton pattern) and use that same instance for the lifetime of the application.

The DocumentStore is the starting location of any RavenDB app but the session is what we will use to talk to our server and hold our entities.

As mentioned we will be creating Characters for our video game so let’s make a class that we can fill with data. For this we are gonna create a class named Characters and store all the properties of our characters inside like so:

public class Characters{public string Id { get; set; }public string Name { get; set; }public string Faction { get; set; }public string Power { get; set; }public int Level { get; set; }public bool Pet { get; set; }}

Now lets create a new character we do this by assigning some values to our Character class.

Create

using (var session = store.OpenSession()){var character = new Characters{Name = "Milan",Faction = "Fire",Power = "FireBall",Level = 18,Pet = true};session.Store(character);session.SaveChanges();}

So we open a new session (OpenSession) from store which remember: refers to DocumentStore from the RavenDB client, within our open session we create a new character (var character = new Characters) based on our Characters class. Finally we store the character in the session with session.Store which is essentially our Create and call saveChanges to save all the changes in the session to the server.

Run the program and it should now create our new character:

Read

Let’s say we want to print our character’s name, level and faction in our console we do so by opening a session and loading our character by its id. Then we can Console.WriteLine the properties we want to show.

using (var session = store.OpenSession()){Characters character = session.Load<Characters>("characters/1-A");Console.WriteLine($"Welcome {character.Name}, " +$"you are level {character.Level} " +$"and are part of the {character.Faction} faction");}

Run the program and the output should be: “Welcome (character name), you are level (character level) and are part of the (character faction) faction”

Update

To modify our data we will open a session and load our character by its id then we can update the properties that we want to update. Let’s say our character has leveled up to level 19 and he no longer has a pet.

using (var session = store.OpenSession()){var character = session.Load<Characters>("characters/1-A");character.Level = 19;character.Pet = false;session.SaveChanges();}

Run the program and the data has now been updated.

Notice how we no longer have to call Store like in Create this is because the character was loaded via the session and any changes made to it would be sent back to the server when SaveChanges was called.

Delete

We can delete our character by simply loading it in and calling session.Delete.

using (var session = store.OpenSession()){var character = session.Load<Characters>("characters/1-A");session.Delete(character);session.SaveChanges();}

It’s good to note that nothing will be removed until SaveChanges() is called.

The character has now been deleted out of the database.

Querying in RavenDB

For querying with RavenDB we use RQL (RavenDB Query Language) an SQL-like language. Working with RavenDB we know two types of queries: a dynamic query and an index query. A dynamic query does not specify an index giving the query optimizer full freedom with regards to which index that query will use. Like the example below where we don’t specify an index.

from Orders where ...

And the example below where we do specify an index(index query):

from index "Orders/ByCompany" where ...

This instructs RavenDB to use the Orders/ByCompany index. If it fails to find an index RavenDB will actually create an index for this query, a different approach then most databases who usually fall back on doing a full scan. The problem with full scans is that they are not scalable, as the data in your database grows you will start to experience ever-increasing query times. In contrast, RavenDB queries always use an index and can return results with the same speed regardless of the size of the data.

So let’s navigate to query in our RavenDB studio for our Northwind database so we can write some queries.

We can write a simple query as followed to request some data from our database

from Employees

You will get a list of employees in the Studio, which should not be a surprise if you are familiar with SQL.

Let’s be more specific and ask the database to return us one employee by it’s id.

from Employees where id() = 'employees/1-A'

This will give us the employee with id ‘employees/1-A’.

Now for neither of these queries did Raven have to make a new index because they were dynamic, but now let’s run the following code:

from Employees
where FirstName = 'Anne'

So before we ran the query there was no index to retrieve this data so the query optimizer of RavenDB auto generated the index ‘Employees/ByFirstNameAndLastName’ to get us the data, very quickly.

Querying in RavenDB always requires an index this means we get very fast results because we don’t have to go trough full scans but on the contrary RavenDB does not allow computation during execution because this requires a full scan but such queries usually have a work around that is permitted.

Setting up multiple nodes

To set up another node, add another node folder named Node B in your Cluster Parent folder. In here extract the RavenDB zip like we did with Node A. In the Server folder open settings.json and change the “License.Eula.Accepted” to true and “Setup.Mode” to “None” like so:

"License.Eula.Accepted": true,"Setup.Mode": "None",

This way we have to do less steps for our setup.

Now open up your command prompt and navigate to your Node B folder and run the following command:

Raven.Server.exe
--ServerUrl=http://127.0.0.2:8080
--Logs.Path=Logs/B
--DataDir=Data/B

This time, we run the RavenDB instance bound to 127.0.0.2 (not 0.0.1) and on the same 8080 port. If we copy the url in our browser we should see this:

We can see the node is marked with a question mark this is because it is not yet part of a cluster. To add it to our cluster go to your Node A studio, “Manage Server” and then to “Cluster”. That should look like this:

Click on “Add Node to cluster” and enter the url of node B: “http://127.0.0.2:8080/” and the tag: “B”.

Once added it should now look like this:

We now have our leader node (A) and our member node (B). Node B is now no longer unknown but rather node B.

Understanding RavenDB clusters and nodes

So now we have created these nodes and added them to our cluster but what even are nodes and clusters?

"A node in computer science is a device or a structure, which can be considered as an independent unit. Nodes function within an automated system and communicate with each other." source: Wikipedia

Joined nodes together make a network, also known as a cluster.

A RavenDB cluster is three or more machines (nodes) joined together (A cluster with just two nodes rarely makes sense). The cluster will distribute work among the nodes, handle failures and recovery. This is also known as a distributed network. RavenDB operates on two distinct layers in its distributed system. The first, the cluster level, is composed of nodes working together to achieve the same goal. This is done by using the Raft consensus protocol and having the cluster members vote to select a strong leader among themselves. The leader basically ensures that, as long as the majority of the nodes are functioning and can talk to one another, we’ll remain in operation. And second the database layer where all nodes are equals(no leader), working together, cooperatively.

So for example, creating a database is an operation on the cluster-level while writing a document to a database only impacts the database layer.

But even if the majority of the cluster is down we can still process reads and writes as long as a single node is available. Because RavenDB uses multi-master replication inside a database.

So by distributing the workload over multiple nodes we can scale our systems pretty much infinitely (only limited by the amount of nodes we can add), we ensure a higher fault tolerance and better security.

ACID

RavenDB is an ACID database meaning it follows 4 rules to make sure the database does not let two transactions trough simultaneously. This means that transactions meet the following requirements:

Atomic: The degree to which the DMBS (Database management system) guarantees that a transaction is either completely executed or completely void.

Consistent: A transaction either creates a new valid state or restores the state it was in (in case of an error or problem). This implies that all database integrity rules must apply after the transaction.

Isolated: Transactions are executed in isolation from each other, i.e. transactions that are executed simultaneously have no insight into each other’s intermediate results.

Durable: a completed transaction cannot be invalidated later.

The opposite of the ACID term is BASE (Basically Available, Soft state, Eventual consistency).

RavenDB and Docker

Why

Docker is the de facto standard for application containerisation. This means running your applications within “containers” under a single operating system. This allows a system to run more apps versus if you had to run each app in its own operating system. Containers are very lightweight and are often used for fast and flexible application development and movement.

Setup

To install Docker go here: https://docs.docker.com/desktop/

I’m using Ubuntu for Linux distribution.

To install RavenDB on Docker we run this script: https://github.com/ravendb/ravendb/blob/v4.0/docker/run-linux.ps1 in PowerShell. If you incur an authentication error you can try running docker logout and then rerunning the script. Now in your browser you can go the localhost port that we used, in this case that’s :8080 so we navigate to http://127.0.0.1:8080/. Now we are asked to setup our server again like we did before with the setup wizard.

Conclusion

RavenDB has a very solid, functioning product that could be a great choice as an alternative for SQL databases with some astonishing features like its SQL-like language: RQL and the Raven Studio which is a substantial addition to the product that is great for querying and managing the databases. Creating a cluster and adding nodes does not require a lot of technical expertise making it a great entry level database. On the contrary, RavenDB does not support as many languages as some competitors and the documentation can feel a little all over the place at times. In my opinion RavenDB feels like a solid, well thought out product that should be on the radar of any organization thinking about changing databases.

Useful links and resources:

The book of RavenDB: https://ravendb.net/learn/inside-ravendb-book/reader/4.0/1-welcome-to-ravendb

Official docs: https://ravendb.net/docs/article-page/5.4/csharp

Follow me on LinkedIn

--

--