Counters Batch Operation

CounterBatchOperation allows you to operate on multiple counters (Increment, Get, Delete) of different documents in a single request.

Syntax

public CounterBatchOperation(CounterBatch counterBatch)
Parameter
counterBatch CounterBatch An object that holds a list of DocumentCountersOperation.
Each element in the list describes the counter operations to perform for a specific document

public class CounterBatch
{
    public bool ReplyWithAllNodesValues; // A flag that indicates if the results should include a
                                         // dictionary of counter values per database node
    public List<DocumentCountersOperation> Documents = new List<DocumentCountersOperation>();
}

DocumentCountersOperation

public class DocumentCountersOperation
{
    public string DocumentId; // Id of the document that holds the counters
    public List<CounterOperation> Operations; // A list of counter operations to perform
}

CounterOperation

public class CounterOperation
{
    public CounterOperationType Type;
    public string CounterName;
    public long Delta; // the value to increment by
}

CounterOperationType

public enum CounterOperationType
{
    Increment,
    Delete,
    Get
}

Document updates as a result of a counter operation

A document that has counters holds all its counter names in the metadata.
Therefore, when creating a new counter, the parent document is modified, as the counter's name needs to be added to the metadata.
Deleting a counter also modifies the parent document, as the counter's name needs to be removed from the metadata.
Incrementing an existing counter will not modify the parent document.

Even if a DocumentCountersOperation contains several CounterOperation items that affect the document's metadata (create, delete), the parent document will be modified only once, after all the CounterOperation items in this DocumentCountersOperation have been processed.
If DocumentCountersOperation doesn't contain any CounterOperation that affects the metadata, the parent document won't be modified.

Return Value

  • CounterBatchOperation returns a CountersDetail object, which holds a list of CounterDetail objects.

  • If a CounterOperationType is Increment or Get, a CounterDetail object will be added to the result.
    Delete operations will not be included in the result.

public class CountersDetail
{
    public List<CounterDetail> Counters;
}

public class CounterDetail
{
    public string DocumentId; // ID of the document that holds the counter
    public string CounterName; // The counter name
    public long TotalValue; // Total counter value
    public Dictionary<string, long> CounterValues; // A dictionary of counter values per database node
    public long Etag; // Counter Etag
    public string ChangeVector; // Change vector of the counter
}

Examples

Assume we have two documents, "users/1" and "users/2", that hold 3 counters each -
"likes", "dislikes" and "downloads" - with values 10, 20 and 30 (respectively)


Example #1 : Increment Multiple Counters in a Batch

var operationResult = store.Operations.Send(new CounterBatchOperation(new CounterBatch
{
    Documents = new List<DocumentCountersOperation>
    {
        new DocumentCountersOperation
        {
            DocumentId = "users/1",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Increment,
                    CounterName = "likes",
                    Delta = 5
                },
                new CounterOperation
                {
                    // No Delta specified, value will be incremented by 1
                    // (From RavenDB 6.2 on, the default Delta is 1)

                    Type = CounterOperationType.Increment,
                    CounterName = "dislikes"
                }
            }
        },
        new DocumentCountersOperation
        {
            DocumentId = "users/2",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Increment,
                    CounterName = "likes",
                    Delta = 100
                },
                new CounterOperation
                {
                    // this will create a new counter "score", with initial value 50
                    // "score" will be added to counter-names in "users/2" metadata

                    Type = CounterOperationType.Increment,
                    CounterName = "score", 
                    Delta = 50
                }
            }
        }
    }
}));

Result:

{
	"Counters": 
    [
		{
			"DocumentId" : "users/1",
			"CounterName" : "likes",
			"TotalValue" : 15,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/1",
			"CounterName" : "dislikes",
			"TotalValue" : 20,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/2",
			"CounterName" : "likes",
			"TotalValue" : 110,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/2",
			"CounterName" : "score",
			"TotalValue" : 50,
			"CounterValues" : null
		}
	]
}

Example #2 : Get Multiple Counters in a Batch

var operationResult = store.Operations.Send(new CounterBatchOperation(new CounterBatch
{
    Documents = new List<DocumentCountersOperation>
    {
        new DocumentCountersOperation
        {
            DocumentId = "users/1",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Get,
                    CounterName = "likes"
                },
                new CounterOperation
                {
                    Type = CounterOperationType.Get,
                    CounterName = "downloads"
                }
            }
        },
        new DocumentCountersOperation
        {
            DocumentId = "users/2",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Get,
                    CounterName = "likes"
                },
                new CounterOperation
                {
                    Type = CounterOperationType.Get,
                    CounterName = "score"
                }
            }
        }
    }
}));

Result:

{
	"Counters": 
    [
		{
			"DocumentId" : "users/1",
			"CounterName" : "likes",
			"TotalValue" : 15,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/1",
			"CounterName" : "downloads",
			"TotalValue" : 30,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/2",
			"CounterName" : "likes",
			"TotalValue" : 110,
			"CounterValues" : null
		},
        {
			"DocumentId" : "users/2",
			"CounterName" : "score",
			"TotalValue" : 50,
			"CounterValues" : null
		}
	]
}

Example #3 : Delete Multiple Counters in a Batch

var operationResult = store.Operations.Send(new CounterBatchOperation(new CounterBatch
{
    Documents = new List<DocumentCountersOperation>
    {
        new DocumentCountersOperation
        {
            DocumentId = "users/1",
            Operations = new List<CounterOperation>
            {
                // "likes" and "dislikes" will be removed from counter-names in "users/1" metadata
                new CounterOperation
                {
                    Type = CounterOperationType.Delete,
                    CounterName = "likes"
                },
                new CounterOperation
                {
                    Type = CounterOperationType.Delete,
                    CounterName = "dislikes"
                }
            }
        },
        new DocumentCountersOperation
        {
            DocumentId = "users/2",
            Operations = new List<CounterOperation>
            {
                // "downloads" will be removed from counter-names in "users/2" metadata

                new CounterOperation
                {
                    Type = CounterOperationType.Delete,
                    CounterName = "downloads"
                }
            }
        }
    }
}));

Result:

{
	"Counters": []
}

Example #4 : Mix Different Types of CounterOperations in a Batch

var operationResult = store.Operations.Send(new CounterBatchOperation(new CounterBatch
{
    Documents = new List<DocumentCountersOperation>
    {
        new DocumentCountersOperation
        {
            DocumentId = "users/1",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Increment,
                    CounterName = "likes",
                    Delta = 30
                },
                new CounterOperation
                {
                    // The results will include null for this 'Get'
                    // since we deleted the "dislikes" counter in the previous example flow
                    Type = CounterOperationType.Get,
                    CounterName = "dislikes"
                },
                new CounterOperation
                {
                    Type = CounterOperationType.Delete,
                    CounterName = "downloads"
                }
            }
        },
        new DocumentCountersOperation
        {
            DocumentId = "users/2",
            Operations = new List<CounterOperation>
            {
                new CounterOperation
                {
                    Type = CounterOperationType.Get,
                    CounterName = "likes"
                },
                new CounterOperation
                {
                    Type = CounterOperationType.Delete,
                    CounterName = "dislikes"
                }
            }
        }
    }
}));

Result:

  • Note: The Delete operations are Not included in the results.

{
	"Counters": 
    [
		{
			"DocumentId" : "users/1",
			"CounterName" : "likes",
			"TotalValue" : 30,
			"CounterValues" : null
		},
        null,
        {
			"DocumentId" : "users/2",
			"CounterName" : "likes",
			"TotalValue" : 110,
			"CounterValues" : null
		}
	]
}