see on GitHub

Working with document identifiers

Each document in RavenDB database has a unique string associated with this document, called an identifier or a key. Every entity that you store either by using a session or with a commands will have such identity assigned in the database. In RavenDB there are three options supported by the server to store a document and assign an identifier to it. The client can directly take advantage of them, which takes place most often when you use the command API. The second option is handling the key generation using the knowledge of the type of the entity and the identity number provided by the HiLo algorithm. This is how the identifier is generated by the session, which is described in details below.

Session usage

If you choose to use the session, you don't have to pay any special attention to the identifiers of the stored entities. The session will take care of it by generating the keys automatically. It utilizes conventions and HiLo algorithm to produce the identifiers. Everything is handled by the session's mechanism and is transparent for the user. However, you can influence the key generation strategy by overwriting the key generation conventions. In this article we are going to consider the behavior in accordance with the default conventions.

Identifiers in RavenDB are strings

Identifiers of documents in RavenDB database (server side) are always strings. RavenDB is designed to work with string identifiers and that is the best option you should choose when you model your entities. Still, you can use numeric values or GUIDs and everything will work seamlessly because RavenDB will automatically make the translation between the inner string id to the numeric or GUID value. However you can run into some issues caused by such entity model. Read more in Beware of ... article.

Autogenerated ids

In order to figure out which property (or field) holds the entity's identifier, the convention Conventions.FindIdentityProperty is called. By default, it looks for the property or the field named Id (case sensitive). However this property can have null value or even not be present at all. Then the automatic key generation strategy is performed. The default convention is that such entities get the keys in the following format collection/number. RavenDB client first determines the name of the collection that the entity belongs to, and then contacts with the server in order to retrieve a numeric range of values, which can be used as the number part. The range of available numbers is calculated by using HiLo algorithm and it is tracked per collection. The current maximum value in ranges are stored in documents Raven/Hilo/collectionName.

Let's see the example.

Order order = new Order
{
	Id = null // value not provided
};

session.Store(order);

What will be the identifier of this order? You can check it by calling:

string orderId = session.Advanced.GetDocumentId(order); // "orders/1"

If this is the first Order entity in your database, then it will return orders/1. How does the key generation process proceed? The RavenDB client determines the collection name as orders (by default it is the plural form of the entity name). Then it asks the server for the Raven/Hilo/orders document, creates the numeric range by using HiLo (the first available range is 1 - 32) and updates the HiLo document on the server. The next available identity value (always incrementing number) from the given range is 1 so its combination with the collection name gives the result orders/1.

The next attempt to store another Order object within the same session will result in creating the order/2 key. However, this time asking the server about the HiLo document will not be necessary because the in-memory range (1 - 32) is enough, so simply the next number will be added as the key suffix.

Identity value numeric range generation

Each (in code) document store instance handles the generation of the identity value numeric range. The database stores the last requested number while the document store instances request ranges and caches the returned range of available identities.

The database has a single document (per collection) which stores the last identity value requested by a document store instance. Eg. the document Raven/HiLo/accounts has the following value { "Max": "4000" }, then the next range will be 4001 - 4032, if 32 was range size. (By default, it's 32).

The number of sessions per document store instance play no part in identity value generation.

If your intention is to skip the key creation strategy that relays on the collection and HiLo value pair, then you can allow the RavenDB database to assign the Guid identifier to the stored document. Then you have to provide the string.Empty as the value of the Id property:

Order orderEmptyId = new Order
{
	Id = string.Empty // database will create a GUID value for it
};

session.Store(orderEmptyId);

session.SaveChanges();

string guidId = session.Advanced.GetDocumentId(orderEmptyId); // "6778c231-180b-4715-aad4-253c4c6027a4"

Note that this time the check for the document id is called after SaveChanges because only then we go to the server while the entity's key are generated there.

Custom/semantic ids

The session also supports the option to store the entity and explicitly tell under what identifier it should be stored in the database. To to this you can either set the Id property of the object:

Product product = new Product
{
	Id = "products/ravendb",
	Name = "RavenDB"
};

session.Store(product);

or use the following Store method overload:

session.Store(new Product() { Name = "RavenDB" }, "products/ravendb");

Identity ids

RavenDB also supports the notion of the identity without the usage of the HiLo. 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 identity value will be your entity's key after calling SaveChanges. The identity value will be assigned by the server.

session.Store(new Company()
{
	Id = "companies/"
});

session.Store(new Company()
{
	Id = "companies/"
});

session.SaveChanges();

After execution of the above example, the first entity will get companies/1 key and the second one companies/2. The identity values are stored per prefix. Here, according to the default conventions, we used the collection name as the prefix. Note that we got the same results as we would if we used the the HiLo to auto-generate keys. The difference is that the in this case both identity numbers were generated on the server side.

Prefix convention

Note that we used companies/ as the prefix just to follow the RavenDB convention. However nothing stands in the way to provide the different prefix, which will be completely unrelated to the collection name.

Concurrent writes

The identities are generated and updated on the server side in the atomic fashion. This means you can safely use this approach in the concurrent writes scenario.

Mixing key generation approaches

You should never mix the identity key generation strategy with the auto-generated identifiers based on the HiLo algorithm within the same session. You might run into concurrency issues.

On the other hand, you are allowed to mix the manually assigned keys and the identity ids that starts with /. RavenDB is aware of the existing document keys, so in an example, if you save the entity with companies/1 ID and then the another one by specifying companies/ as the key, RavenDB will set the first free identity values for a given prefix, so in this case the second one will be stored under companies/2 key.

Commands usage

The use of the commands API gives you the full freedom to select the key generation strategy. As in the case of session, you can either ask the server to provide the key, or provide the identifier of the stored entity manually.

Autogenerated Guids

When you add a new document you can pass null as the key parameter of PUT method:

PutResult result = store.DatabaseCommands.Put(null, null, new RavenJObject(){ {"Name", "RavenDB"}}, new RavenJObject());

string key = result.Key; // "9ce12df5-1027-4704-b2cf-d312e9ea0e59"

in result the server will assign the Guid identifier that you can retrieve by PutResult.Key property.

Identity ids

As in the case of session, you can indicate if the identifier that you are passing needs to have the identity suffix added. You have to mark it by ending the key by / character:

result = store
	.DatabaseCommands
	.Put(
		"products/",
		null,
		new RavenJObject { { "Name", "RavenDB" } },
		new RavenJObject());

string identityKey = result.Key; // "products/1"

Using the commands you can manage to build identity keys on the client, but still relaying on the server side identity generator. Simply point out for which prefix you want to fetch the next available identity number. Look at the example:

long identity = store.DatabaseCommands.NextIdentityFor("products");

result = store
	.DatabaseCommands
	.Put(
		"products/" + identity,
		null,
		new RavenJObject { { "Name", "RavenDB" } },
		new RavenJObject());

Note that such construction requires going to the server twice in order to add a single document. The call of DatabaseCommands.NextIdentityFor is necessary for every entity you want to store. Asking the server about the next identity results in increasing this value on the server side. You cannot simply get the next available identity and use it to create the identifiers for the whole collection of the same type objects by locally incrementing this value because you can accidentally overwrite the document or get a conflict exception if someone else is putting documents using the identity mechanism.

There are dedicated commands that allows you to set identity values. The first one allows to perform that for a single given prefix:

store.DatabaseCommands.SeedIdentityFor("products", 42);

The usage of the second one sets a collection of identities at once:

store.DatabaseCommands.SeedIdentities(new Dictionary<string, long>
{
    { "products", 42 },
    { "orders", 11 }
}.ToList());