Data Subscriptions: Revisions Support


  • The Data Subscription feature supports subscribing not only to documents, but also to document revisions.
  • The revisions support is defined within the subscription.
    A Revisions Configuration must be defined for the subscribed collection.
  • While a regular subscription processes a single document, a Revisions subscription processes pairs of subsequent document revisions.

    Using this functionality allows you to keep track of each change made in a document, as well as compare pairs of subsequent versions of the document.

    Both revisions are accessible for filtering and projection.

  • In this page:


Revisions Processing Order

The Revisions feature allows the tracking of changes made in a document, by storing the audit trail of its changes over time. An audit trail entry is called a Document Revision, and is comprised of a document snapshot.
Read more about revisions here.

In a data subscription, revisions will be processed in pairs of subsequent entries.
For example: Consider the following User document:

{ Name:'James', Age:'21' }

We update the User document twice, in separate operations:
* We update the 'Age' field to the value of 22
* We update the 'Age' field to the value of 23

The data subscriptions revisions processing mechanism will receive pairs of revisions in the following order:

# Previous Current
1 null { Name:'James', Age:'21' }
2 { Name:'James', Age:'21' } { Name:'James', Age:'22' }
3 { Name:'James', Age:'22' } { Name:'James', Age:'23' }

The revisions subscription will be able to function properly only if the revisions it needs to process are available. Please make sure that your revisions configuration doesn't purge revisions before the subscription had the chance to process them.

Simple Declaration and Usage

Here we declare a simple revisions subscription that will send pairs of subsequent document revisions to the client:

Creation:

name = store.Subscriptions.Create(
    new SubscriptionCreationOptions<Revision<Order>>());
name = await store.Subscriptions.CreateAsync(new SubscriptionCreationOptions()
{
    Query = @"From Orders (Revisions = true)"
});

Consumption:

SubscriptionWorker<Revision<Order>> revisionWorker = store.Subscriptions.GetSubscriptionWorker<Revision<Order>>(name);

await revisionWorker.Run((SubscriptionBatch<Revision<Order>> x) =>
{
    foreach (var documentsPair in x.Items)
    {
        var prev = documentsPair.Result.Previous;
        var current = documentsPair.Result.Current;

        ProcessOrderChanges(prev, current);
    }
}
);

Revisions Processing and Projection

Here we declare a revisions subscription that will filter and project data from revisions pairs:

Creation:

name = store.Subscriptions.Create(
    new SubscriptionCreationOptions<Revision<Order>>()
    {
        Filter = tuple => tuple.Current.Lines.Count > tuple.Previous.Lines.Count,
        Projection = tuple => new
        {
            PreviousRevenue = tuple.Previous.Lines.Sum(x => x.PricePerUnit * x.Quantity),
            CurrentRevenue = tuple.Current.Lines.Sum(x => x.PricePerUnit * x.Quantity)
        }
    });
name = await store.Subscriptions.CreateAsync(new SubscriptionCreationOptions()
{
    Query = @"declare function getOrderLinesSum(doc){
                    var sum = 0;
                    for (var i in doc.Lines) { sum += doc.Lines[i];}
                    return sum;
                }

            From Orders (Revisions = true)
            Where getOrderLinesSum(this.Current)  > getOrderLinesSum(this.Previous)
            Select 
            {
                PreviousRevenue: getOrderLinesSum(this.Previous),
                CurrentRevenue: getOrderLinesSum(this.Current)                            
            }"
});

Consumption:

SubscriptionWorker<Revision<Order>> revisionWorker = store.Subscriptions.GetSubscriptionWorker<Revision<Order>>(name);

await revisionWorker.Run((SubscriptionBatch<Revision<Order>> x) =>
{
    foreach (var documentsPair in x.Items)
    {
        var prev = documentsPair.Result.Previous;
        var current = documentsPair.Result.Current;

        ProcessOrderChanges(prev, current);
    }
}
);