Changes API

The RavenDB client offers a push notification feature that allows you to receive messages from a server about events that occurred there. You are able to subscribe to events for all documents or indexes as well as to indicate a particular one that you are interested in. This mechanism lets you to notify users if something has changed without the need to do any expensive pooling.

IDatabaseChanges interface

The changes subscription is accessible by a document store. Depending on the type of the store you use (DocumentStore, ShardedDocumentStore or EmbeddableDocumentStore) you will get an appropriate instance which is an implementation of a common IDatabaseChanges interface.

IDatabaseChanges changes = store.Changes("DatabaseName");

The parameter of the Changes method is an optional database name that you want get notifications from. If it is not provided the default database will be chosen.

Connection properties

IDatabaseChanges has three properties that are related to the server connection:

// represents the task responsible for establishing connection with the server
Task Task { get; }
// returns state of the connection
bool Connected { get; }
// the event raised if a connection state is changed
event EventHandler ConnectionStatusChanged

Subscribing methods

In order to retrieve notifications you have to subscribe server-side events by using either document or index related methods.

Information

To achieve a better experience of subsribing methods by using delegates, please add Reactive Extensions package to your project.

Document notifications

You are able to subscribe changes of all documents in database. For example to get information from the server about all document changes use the following code:

store.Changes()
     .ForAllDocuments()
     .Subscribe(change => Console.WriteLine("{0} on document {1}", change.Type, change.Id));

The very common scenario is to subscribe changes for documents within the same collection. To achieve that you should use the ForDocumentsStartingWith method:

store.Changes()
	.ForDocumentsStartingWith("users")
	 .Subscribe(change =>
	 {
		 if (change.Type == DocumentChangeTypes.Put)
		 {
			 Console.WriteLine("New user has been added. Its ID is " + change.Id + ", document ETag " + change.Etag);
		 }
	 });

You can also observe the particular document by specifing its identifier:

store.Changes()
	.ForDocument("users/1")
	 .Subscribe(change =>
	 {
		 if (change.Type == DocumentChangeTypes.Delete)
		 {
			 Console.WriteLine("User " + change.Id + " has been deleted.");
		 }
	 });

The result of subscription methods above is DocumentChangeNotification object. It consists of the following properties:

DocumentChangeTypes Type { get; set; }
string Id { get; set; }
Etag Etag { get; set; }

where DocumentChangeTypes is the enum with the values as follow:

None,
Put,
Delete,
ReplicationConflict,
AttachmentReplicationConflict,
BulkInsertStarted,
BulkInsertEnded,
BulkInsertError
Common (Put | Delete)

Index notifications

The same way like observing changes for documents you are allowed to grab information about indexes from the server. In order to look for changes for all indexes you should use the ForAllIndexes method. For example to get info about newly created indexes use the code:

store.Changes()
	.ForAllIndexes()
	 .Subscribe(change =>
	 {
		 if (change.Type == IndexChangeTypes.IndexAdded)
		 {
			 Console.WriteLine("Index " + change.Name + " has been added.");
		 }
	 });

If you are interested in observing changes of a specified index only, you can pass the index name:

store.Changes()
	.ForIndex("Orders/Total")
	 .Subscribe(change =>
	 {
		 if (change.Type == IndexChangeTypes.ReduceCompleted)
		 {
			 Console.WriteLine("Index 'Orders/Total' has finished the reduce work.");
		 }
	 });

This sample shows how to get information about completed reduce work by the map/reduce index.

As the result of subscribing the index notifications you will get IndexChangeNotification object that contains the following properties:

IndexChangeTypes Type { get; set; }
string Name { get; set; }
Etag Etag { get; set; }

IndexChangeTypes is the enum that has the flags:

None,
MapCompleted,
ReduceCompleted,
RemoveFromIndex,
IndexAdded,
IndexRemoved,
IndexDemotedToIdle,
IndexPromotedFromIdle,
IndexDemotedToAbandoned

Replication conflict notifications

With the changes API you also can listen to replication conflicts, for both documents and attachements. Here is the sample code:

store.Changes()
	.ForAllReplicationConflicts()
	.Subscribe(conflict =>
		{
			if (conflict.ItemType == ReplicationConflictTypes.DocumentReplicationConflict)
			{
				Console.WriteLine("Conflict detected for {0}. Ids of conflicted docs: {1}. " +
				                  "Type of replication operation: {2}",
				                  conflict.Id, 
								  string.Join(", ", conflict.Conflicts), 
								  conflict.OperationType);
			}						
		});

After subscribe you will be getting ReplicationConflictNotification objects, which look as follow:

ReplicationConflictTypes ItemType { get; set; }
string Id { get; set; }
Etag Etag { get; set; }
ReplicationOperationTypes OperationType { get; set; }
string[] Conflicts { get; set; }

ReplicationConflictTypes is the enum that determines the type of conflicted item:

DocumentReplicationConflict = 1,
AttachmentReplicationConflict = 2

There are two ReplicationOperationTypes available:

Put = 1,
Delete = 2

Automatic document conflict resolution

In RavenDB client you have an opportunity to register a conflict listeners which are used to resolve conflicted document. However this can happen only if you get the conflicted document. The ability to subscribe to the replication conflicts gives the client more power. Now if you listen to the conflicts and have any conflict listener registered then the client will automatically resolve the conflict right after the arrival of the notification.

BulkInsert notifications

To observe the bulk insert operations, you can subscribe using the ForBulkInsert method from the API with bulk insert operation id as a parameter.

In a result of subscribing to bulk insert notifications you will get BulkInsertChangeNotification objects that contain same properties as DocumentChangeNotification with additional OperationId property to mark for which bulk insert notification the notification occured.

using (var bulkInsert = store.BulkInsert())
{
	store.Changes()
		.ForBulkInsert(bulkInsert.OperationId)
		.Subscribe(change =>
		{
			switch (change.Type)
			{
				case DocumentChangeTypes.BulkInsertStarted:
					// do something
					break;
				case DocumentChangeTypes.BulkInsertEnded:
					// do something
					break;
				case DocumentChangeTypes.BulkInsertError:
					// do something
					break;
			}
		});

	// process bulk insert here
}