RavenDB version 2.5. Other versions:

Saving a new document

Before we can start saving information to RavenDB, we must define what we will save. For the purpose of these sections, we will use the following class structure:

    public class BlogPost
    {
    	public string Id { get; set; }
    	public string Title { get; set; }
    	public string Category { get; set; }
    	public string Content { get; set; }
    	public DateTime PublishedAt { get; set; }
    	public string[] Tags { get; set; }
    	public BlogComment[] Comments { get; set; }
    }
    
    public class BlogComment
    {
    	public string Title { get; set; }
    	public string Content { get; set; }
    }
    

To save a new blog post in our database server, we first create a new instance of the BlogPost class, as shown below:

    // Creating a new instance of the BlogPost class
    BlogPost post = new BlogPost()
    					{
    						Title = "Hello RavenDB",
    						Category = "RavenDB",
    						Content = "This is a blog about RavenDB",
    						Comments = new BlogComment[]
                    		           	{
                    		           		new BlogComment() {Title = "Unrealistic", Content = "This example is unrealistic"},
                    		           		new BlogComment() {Title = "Nice", Content = "This example is nice"}
    
                    		           	}
    					};
    

Neither the class itself nor instantiating it requires anything from RavenDB, either in the form of attributes or in the form of special factories. The RavenDB Client API works with simple POCO (Plain Old CLR Objects) objects.

Persisting this entire object graph involves calling Store and then SaveChanges, on a session object we obtained from our document store:

    // Saving the new instance to RavenDB
    session.Store(post);
    session.SaveChanges();
    
    

The Store method operates purely in memory and only the call of SaveChanges will produce the actual communication with the server (a single POST by using request batching).

Note that the entire object graph will serialized and persisted as a single document on the server side, not as a set of distinct objects.

Document IDs

In the example above we had a string Id property for BlogPost, and left it blank. It is this property that will be used as the "primary key" for this document. Note how RavenDB generated an ID for us, "BlogPosts/1", based on the default convention which we will discuss in a second.

If there is no Id property on a document, RavenDB will still generate a unique ID, but it will be retrievable only by calling session.Advanced.GetDocumentId(object). In other words, having an Id property is entirely optional, so you can explicitly define such a property only when you need this information to be more accessible.

Document ID limitations

  1. Maximum number of characters is 1023
  2. Id cannot contain \

Document IDs generation strategies

RavenDB supports 3 ways of figuring out a unique id, or a document key, for a newly saved document.

By default, the HiLo algorithm is used. Whenever you create a new DocumentStore object and it connects to a RavenDB server, HiLo keys are exchanged and your connection is assigned a range of values it can use when a new entity is being stored. A new set of values is negotiated automatically if the range was consumed.

When you store an object with no Id property, or with one that wasn't manually set, RavenDB will assign it with a document ID that is combined of the collection name (which by default is the entity name in its plural form) and the next available ID from the given range.

It is important to understand that a Collection is merely a convention, not something that is enforced by RavenDB. There is absolutely nothing that would prevent you from saving a Post with the document id of "users/1", and that would overwrite any existing document with the id "users/1", regardless of which collection it belongs to.

Numeric or Guid Id properties are supported and will work seamlessly. In this case, RavenDB will automatically make the translation between the inner string ID to the numeric or Guid value shown in the entity and back.

Using this approach, IDs are available immediately after calling Store on the object - the RavenDB session will set a unique ID in memory without asking one from the server.

RavenDB also supports the notion of Identity, for example if you need IDs to be consecutive. By creating a string Id property in your entity, and setting it to a value ending with a slash (/), you can tell RavenDB to use that as a key prefix for your entity. That prefix followed by the next available integer ID for it will be your entity's ID after you call SaveChanges().

You can also assign an ID manually by explicitly setting the string Id property of your object, but then if a document already exists in your RavenDB server under the same key it will be overwritten without any warning.

If you are going to store large number of documents with custom generated IDs, you must necessarily read the topic Dealing with custom ID for high number of documents presented below.

Custom ID generation strategies

You can also setup a custom id generation strategy by supplying a DocumentKeyGenerator to the document store conventions, like so:

    store.Conventions.DocumentKeyGenerator = (dbname, commands, entity) => store.Conventions.GetTypeTagName(entity.GetType()) + "/";
    
    

This will instruct RavenDB to use identity id generation strategy for all the entities that this document store manages.

Overriding default ID generation

To override default document key generation algorithms, we added RegisterIdConvention and RegisterAsyncIdConvention methods to DocumentConvention where you can include your own identifier generation logic.

    DocumentConvention RegisterIdConvention<TEntity>(Func<string, IDatabaseCommands, TEntity, string> func);
    
    DocumentConvention RegisterAsyncIdConvention<TEntity>(Func<string, IAsyncDatabaseCommands, TEntity, Task<string>> func);
    
    

Consider a User class:

    public class User
    {
    	public string Name { get; set; }
    
    	public string FirstName { get; set; }
    
    	public string LastName { get; set; }
    }
    
    

To generate a custom Id with users/ prefix combined with Name of the user you need to do as follows:

    store.Conventions.RegisterIdConvention<User>((dbname, commands, user) => "users/" + user.Name);
    
    

or if you want to register your convention for async operations then:

    store.Conventions.RegisterAsyncIdConvention<User>((dbname, commands, user) => new CompletedTask<string>("users/" + user.Name));
    
    

Note that spectrum of identifier generation abilities is very wide, because DatabaseCommands object is passed into an identifier convention function and can be used for advanced calculation techniques.

As of version 2.0, the dbname is passed to the register convention methods to allow users to make Id generation decision per database (e.g. default document key generator - the MultiDatabaseHiloGenerator - is using this parameter to prevent sharing HiLo values accross the databases)

    using (var session = store.OpenSession())
    {
    	session.Store(new User
    	{
    		Name = "jdoe",
    		FirstName = "John",
    		LastName = "Doe"
    	});
    
    	session.SaveChanges();
    }
    
    

Above code will store new entity in the database with users/jdoe as a key and below code will store user using async operation with users/jcarter key:

    using (var session = store.OpenAsyncSession())
    {
    	await session.StoreAsync(new User
    	{
    		Name = "jcarter",
    		FirstName = "John",
    		LastName = "Carter"
    	});
    
    	await session.SaveChangesAsync();
    }
    
    

Inheritance

Registered conventions are inheritance-aware, so all types that can be assigned from registered type will fall into that convention according to inheritance-hierarchy tree

If we will create a new class PrivilegedUser that will derive from our User class

    using (var session = store.OpenAsyncSession())
    {
    	await session.StoreAsync(new User
    	{
    		Name = "jcarter",
    		FirstName = "John",
    		LastName = "Carter"
    	});
    
    	await session.SaveChangesAsync();
    }
    
    

then if we will add convention for User, both our types will use our custom convention

    store.Conventions.RegisterIdConvention<User>((dbname, commands, user) => "users/" + user.Name);
    
    using (var session = store.OpenSession())
    {
    	session.Store(new User // users/jdoe
    	{
    		Name = "jdoe",
    		FirstName = "John",
    		LastName = "Doe"
    	});
    
    	session.Store(new PrivilegedUser // users/jcarter
    	{
    		Name = "jcarter",
    		FirstName = "John",
    		LastName = "Carter"
    	});
    
    	session.SaveChanges();
    }
    
    

If we register two conventions, one for User and second for PrivilegedUser then they will be picked for their specific types.

    store.Conventions.RegisterIdConvention<User>((dbname, commands, user) => "users/" + user.Name);
    store.Conventions.RegisterIdConvention<PrivilegedUser>((dbname, commands, user) => "admins/" + user.Name);
    
    using (var session = store.OpenSession())
    {
    	session.Store(new User // users/jdoe
    	{
    		Name = "jdoe",
    		FirstName = "John",
    		LastName = "Doe"
    	});
    
    	session.Store(new PrivilegedUser // admins/jcarter
    	{
    		Name = "jcarter",
    		FirstName = "John",
    		LastName = "Carter"
    	});
    
    	session.SaveChanges();
    }
    
    

Dealing with custom ID for high number of documents

The ways shown in this section give to you a great flexibility in creating identifiers of documents. You are able to assign to a document any ID as you can imagine. Everything is going to work correctly however you have to be aware that some kind of IDs might cause performance issues when number of documents with custom generated IDs is very high (millions of documents).

The very unrecommended way is to use non-sequential data as document IDs (e.g. created by hash functions). In such case you might be experiencing the declining performance of searching for an existing document and inserting new ones. If your intention is to use custom identifiers and you expect that very high number of such documents might be saved in your database, then we suggest to make use of incremented IDs in order to ensure the good performance.

Comments add new comment

The comments section is for user feedback or community content. If you seek assistance or have any questions, please post them at our support forums.

Javad Amiry
REPLY Posted by Javad Amiry on

Hi. Is there any way to change HiLo's high and low in RavenDB? Thanks.

Ayende Rahien
REPLY Posted by Ayende Rahien on

Javad,

You can certainly do that. We specify the capacity of a hilo during document store initialization. You can do that like this:

var mk = new MultiTypeHiLoKeyGenerator(5);
store.Conventions.DocumentKeyGenerator = (cmd, o) =&gt; mk.GenerateDocumentKey(cmd, store.Conventions, o);

This setup with a capacity value of 5.

The high value is determined on the server, and you can change that by changing the Raven/Hilo/[CollectionName] document.

Graciela Gonzalez
REPLY Posted by Graciela Gonzalez on

Do I nedd visual studio to add the documents to my new database form the studio i do not know how to add

Fitzchak Yitzchaki
REPLY Posted by Fitzchak Yitzchaki on

No need to Visual Studio, just the RavenDB Management Studio which is a Silverlight UI. In order to create a new document, we used to have there "Create a document" button, but you can do this also by editing an existing document and check its ID to the ID of the new document you want to create.

Mohamed Elmalky
REPLY Posted by Mohamed Elmalky on

Hello. If BlogComment had an Id property on it, "session.Store(post)" will not generate id's for the BlogComments. Is there a way to force that? I'm having that issue currently.

Ayende Rahien
REPLY Posted by Ayende Rahien on

Inner values will not have their ID set, you have to do this manully.

Mohamed Elmalky
REPLY Posted by Mohamed Elmalky on

I see. The reason I needed an Id in the first place was to be able to have an UpdateComment method that takes in a comment id. Do you think just updating the BlogPost document as a whole is just as efficient?

Ayende Rahien
REPLY Posted by Ayende Rahien on

Yes

Peter
REPLY Posted by Peter on

And how would you carry out the update command in the case of updating the inner entity not the whole object graph

Peter
REPLY Posted by Peter on

And how would you carry out the update command in the case of updating the inner entity not the whole object graph

Peter
REPLY Posted by Peter on

How can this be achieved please. Manual ID Generation

Richard Brown
REPLY Posted by Richard Brown on

Hi, I want all my keys to use a "-" instead of a "/", for better routing of URLs. For prefixes following the collection naming convention I can just set MvcApplication.DocumentStore.Conventions.IdentityPartsSeparator = "-". However, if I want to change the prefix to be something else (say "u-1" instead of "users-1") I am unable to do this. When using a "/" as a seperator it was easy to set Id = "u/", but this doesn't seem to work if I set Id = "u-". Any advice would be much appreciated!

Ayende Rahien
REPLY Posted by Ayende Rahien on

That is because an id ending with slash is built into ravendb, which dash does not. You cannot make "u-" work the same as "u/"

Patrick
REPLY Posted by Patrick on

Is there a way to generate custom Id's strategies only for specific entities?

Fitzchak Yitzchaki
REPLY Posted by Fitzchak Yitzchaki on

Yes, conventions.RegisterIdConvention<BlogPost>((commands, blogPost) => string.Format("{0}/{1}/{2}", conventions.GetTypeTagName(prayer.GetType()), blogPost.Category, blogPost.Title));

Fitzchak Yitzchaki
REPLY Posted by Fitzchak Yitzchaki on

Yes, conventions.RegisterIdConvention<BlogPost>((commands, blogPost) => string.Format("{0}/{1}/{2}", conventions.GetTypeTagName(prayer.GetType()), blogPost.Category, blogPost.Title));

Patrick
REPLY Posted by Patrick on

Looks great! Will try that.

Patrick
REPLY Posted by Patrick on

Ok, i see. It looks that this feature currently isnt available in the stable version 1.0...

Fitzchak Yitzchaki
REPLY Posted by Fitzchak Yitzchaki on

That is right.

Arun
REPLY Posted by Arun on

Hi, i am generating customized id for the documents.the problem i am facing now is the document id doesn't get prefixed by the class name. So obviously i couldn't use similar id for some other class too. Is there a way set the class name as a prefix for different classes when storing the ids.

Ayende Rahien
REPLY Posted by Ayende Rahien on

When you customize the document id, you customize the entire id. Including the prefix. Since you know what is the type you are customizing this for, you can prefix the value you want.

Arun
REPLY Posted by Arun on

But actually hear different classes are different tables right? So y can't i store the same id's for documents from different classes. why raven is not allowing me to do so?

Ayende Rahien
REPLY Posted by Ayende Rahien on

They all go to the same place. You cannot store two documents with the same name, even if they belong to different collections. Collections is just a virtual concept.

Lucas Batistussi
REPLY Posted by Lucas Batistussi on

Why in following code, when I open new other session and try to get a property that I defined as a part of key, doesn't retrive this information?

store.Conventions.RegisterIdConvention<User> ((commands, type, entity) => "Users/"+entity.UserName);

....

using (var session = store.OpenSession()) { var user = new User(); user.UserName = "ivone";

  session.Store(user);
  session.SaveChanges();     

  //If I try load the entity persisted here (same session) works fine!!!
   var USER = session.Load&lt;User&gt;(&quot;Users/ivone&quot;);

            if (USER != null)
            {
                System.Console.WriteLine(&quot;User: &quot; + USER.UserName + &quot; - &quot; + USER.LastIP);
            }

}

//Here is the problem... Not load UserName neither some other properties (are null). It's so weird! using (var session = store.OpenSession()) { var USER = session.Load<User>("Users/ivone");

            if (USER != null)
            {
                System.Console.WriteLine(&quot;User: &quot; + USER.UserName + &quot; - &quot; + USER.LastIP);
            }
        }

Thanks in advance,

Lucas.

Ayende Rahien
REPLY Posted by Ayende Rahien on

This is NOT the support forum. Please us the mailing list.

NL
REPLY Posted by NL on

You reference here the method DocumentConvention RegisterIdConvention<TEntity>(Func<IDatabaseCommands, TEntity, string> func); but in the current API (Jan 2013) the method is DocumentConvention RegisterIdConvention<TEntity>(Func<string, IDatabaseCommands, TEntity, string> func); with an additional string at the start. There is no documentation as to what the additional string is for

Ayende Rahien
REPLY Posted by Ayende Rahien on

The first string is the db name.

kamegw
REPLY Posted by kamegw on

Sometimes it is extremely difficult to explore good and useful information out there when doing research. will bookmark this for future reference and refer it to my friends. More power to your blog..<a href="http://www.safeffxivgil.com/" rel="dofollow">click this</a>

Jesse
REPLY Posted by Jesse on

What is the maximum length that a custom defined document ID can be? The reason I ask this here is that it would be nice to glean this from the documentation.

Oren Eini
REPLY Posted by Oren Eini on

The max id is limited to 1,024 characters. But we strongly recommend that you'll keep that as short as you can.

raoof
REPLY Posted by raoof on

in Hbase key-value pairs store in a block indexed file in a lexicographic order named "Hfile" since Hfiles are optimized for block operations such as reading key-value pairs sequentially,we should specify keys to keep related data together.

for example in storing html links,reversing the url keep "net.ravendb.help" and "net.ravendb.payment" togheter

its like your default document id naming convention(same documents type have same prefix id),did u have the same purpose?

and if i set same documents ids without prefix,does it decrease performance for range reads?

Daniel Bradley
REPLY Posted by Daniel Bradley on

Is there any security risk when defining an Id convention like: store.Conventions.RegisterIdConvention<User>((dbname, commands, user) => "users/" + user.Name); Could a malicious user specify their name to be something which could execute other actions?

Praveen Jeganathan
REPLY Posted by Praveen Jeganathan on

Hi, I'm new to Raven. I went through documentation and learnt to create documentation via C#.

Being new to Raven, I would like to start from Raven Studio. I'm stuck here, when I open Raven studio, it showed "New database" popup. I closed it to see the overall functionality of the studio. Here is the problem, there are several option listed (checkboxes) when creating a new database (I can't find what are those).

After creating a new database(without selecting any options), how to add documents to it? Confused. Please guide on this or direct me to any tutorials available regarding ravenDB via Studio.

Thank you, Praveen Jeganathan.

Sanket Nagone
REPLY Posted by Sanket Nagone on

Hi, I'm new to Raven. I am saving documents to Raven via C# .My problem is when i try to insert a record in Raven it takes too much time to save it for first time. I tried to find out whats taking time and i found that there is a call to check replication exist or not, i think it is slowing down my system.but when i try to insert records after the first call it save very quickly as expected ,insert is slow only on first call. Please help me on this

rs
REPLY Posted by rs on

How can I change the default Id property from Id to another name? I want the same functionality as it currently does just as another property name on the entity like Uid?

rs
REPLY Posted by rs on

DocStore.Conventions.RegisterIdConvention<Entity>((dbname, commands, entity) => "not sure what should go here/" + entity.Uid);

I want the change to be global to my Entity type regardless of the database name. Is this possible?

Mutiso Mutee
REPLY Posted by Mutiso Mutee on

A quick one on generating custom IDs. Is it performance prudent to use Sequential GUID's?

SUBMIT COMMENT