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).
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 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.
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.
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));
DatabaseCommands object is passed into an identifier convention function and can be used for advanced calculation techniques.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.
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.
Hi. Is there any way to change HiLo's high and low in RavenDB? Thanks.
Javad,
You can certainly do that. We specify the capacity of a hilo during document store initialization. You can do that like this:
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.
Do I nedd visual studio to add the documents to my new database form the studio i do not know how to add
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.
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.
Inner values will not have their ID set, you have to do this manully.
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?
Yes
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!
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/"
Is there a way to generate custom Id's strategies only for specific entities?
Yes, conventions.RegisterIdConvention<BlogPost>((commands, blogPost) => string.Format("{0}/{1}/{2}", conventions.GetTypeTagName(prayer.GetType()), blogPost.Category, blogPost.Title));
Yes, conventions.RegisterIdConvention<BlogPost>((commands, blogPost) => string.Format("{0}/{1}/{2}", conventions.GetTypeTagName(prayer.GetType()), blogPost.Category, blogPost.Title));
Looks great! Will try that.
Ok, i see. It looks that this feature currently isnt available in the stable version 1.0...
That is right.
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.
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.
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?
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.
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";
}
//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");
Thanks in advance,
Lucas.
This is NOT the support forum. Please us the mailing list.
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
The first string is the db name.
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>
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.
The max id is limited to 1,024 characters. But we strongly recommend that you'll keep that as short as you can.