Include Revisions



Overview

Why include revisions:


  • Including revisions may be useful, for example, when an auditing application loads or queries for a document.
    The document's past revisions can be included with the document to make the document's history available for instant inspection.

  • Once loaded to the session, there are no additional trips to the server when accessing the revisions.
    Getting a revision that was included with the document will retrieve it directly from the session.
    This also holds true when attempting to include revisions but none are found.

Including by Creation Time:


  • You can include a single revision by specifying its creation time, see examples below.

  • You can pass local time or UTC, either way the server will convert it to UTC.

  • If the provided time matches the creation time of a document revision, this revision will be included.

  • If no exact match is found, then the first revision that precedes the specified time will be returned.

Including by Change Vector:


  • Each time a document is modified, its Change Vector is updated.

  • When a revision is created,
    the revision's change vector is the change vector of the document at the time of the revision's creation.

  • To include single or multiple document revisions by their change vectors:

    • When modifying the document, store its updated change vector in a property in the document.
      Can be done by patching the document from the Client API or from the Studio.

    • Specify the path to this property when including the revisions, see examples below.

    • e.g.:
      Each time an employee's contract document is modified (e.g. when their salary is raised),
      you can add the current change vector of the document to a dedicated property in the document.
      Whenever the time comes to re-evaluate an employee's terms and their contract is loaded,
      its past revisions can be easily included with it by their change vectors.

Include revisions when Loading document

Include a revision by Time

// The revision creation time
// For example - looking for a revision from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Load a document:
var order = session.Load<Order>("orders/1-A", builder => builder
    // Pass the revision creation time to 'IncludeRevisions'
    // The revision will be 'loaded' to the session along with the document
    .IncludeRevisions(creationTime));

// Get the revision by creation time - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = session
    .Advanced.Revisions.Get<Order>("orders/1-A", creationTime);
// The revision creation time
// For example - looking for a revision from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Load a document:
var order = await asyncSession.LoadAsync<Order>("orders/1-A", builder => builder
    // Pass the revision creation time to 'IncludeRevisions'
    // The revision will be 'loaded' to the session along with the document
    .IncludeRevisions(creationTime));

// Get the revision by creation time - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = await asyncSession
    .Advanced.Revisions.GetAsync<Order>("orders/1-A", creationTime);

Include revisions by Change Vector

// Load a document:
var contract = session.Load<Contract>("contracts/1-A", builder => builder
     // Pass the path to the document property that contains the revision change vector(s)
     // The revision(s) will be 'loaded' to the session along with the document
    .IncludeRevisions(x => x.RevisionChangeVector)    // Include a single revision
    .IncludeRevisions(x => x.RevisionChangeVectors)); // Include multiple revisions

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = session
    .Advanced.Revisions.Get<Contract>(contract.RevisionChangeVector);
var revisions = session
    .Advanced.Revisions.Get<Contract>(contract.RevisionChangeVectors);
// Load a document:
var contract = await asyncSession.LoadAsync<Contract>("contracts/1-A",builder => builder
    // Pass the path to the document property that contains the revision change vector(s)
    // The revision(s) will be 'loaded' to the session along with the document
    .IncludeRevisions(x => x.RevisionChangeVector)    // Include a single revision
    .IncludeRevisions(x => x.RevisionChangeVectors)); // Include multiple revisions

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = await asyncSession
    .Advanced.Revisions.GetAsync<Contract>(contract.RevisionChangeVector);
var revisions = await asyncSession
    .Advanced.Revisions.GetAsync<Contract>(contract.RevisionChangeVectors);

// Sample Contract document
private class Contract
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string RevisionChangeVector { get; set; }        // A single change vector
    public List<string> RevisionChangeVectors { get; set; } // A list of change vectors
}

Include revisions when making a Query

Include revisions by Time

// The revision creation time
// For example - looking for revisions from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Query for documents:
var orderDocuments = session.Query<Order>()
    .Where(x => x.ShipTo.Country == "Canada")
     // Pass the revision creation time to 'IncludeRevisions'
    .Include(builder => builder.IncludeRevisions(creationTime))
     // For each document in the query results,
     // the matching revision will be 'loaded' to the session along with the document
    .ToList();

// Get a revision by its creation time for a document from the query results
// It will be retrieved from the SESSION - no additional trip to the server is made
var revision = session
    .Advanced.Revisions.Get<Order>(orderDocuments[0].Id, creationTime);
// The revision creation time
// For example - looking for revisions from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Query for documents:
var orderDocuments = await asyncSession.Query<Order>()
    .Where(x => x.ShipTo.Country == "Canada")
     // Pass the revision creation time to 'IncludeRevisions'
    .Include(builder => builder.IncludeRevisions(creationTime))
     // For each document in the query results,
     // the matching revision will be 'loaded' to the session along with the document
    .ToListAsync();

// Get a revision by its creation time for a document from the query results
// It will be retrieved from the SESSION - no additional trip to the server is made
var revision = await asyncSession
    .Advanced.Revisions.GetAsync<Order>(orderDocuments[0].Id, creationTime);

Include revisions by Change Vector

// Query for documents:
var orderDocuments = session.Query<Contract>()
     // Pass the path to the document property that contains the revision change vector(s)
    .Include(builder => builder
        .IncludeRevisions(x => x.RevisionChangeVector)   // Include a single revision
        .IncludeRevisions(x => x.RevisionChangeVectors)) // Include multiple revisions
     // For each document in the query results,
     // the matching revisions will be 'loaded' to the session along with the document
    .ToList();

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = session.
    Advanced.Revisions.Get<Contract>(orderDocuments[0].RevisionChangeVector);
var revisions = session
    .Advanced.Revisions.Get<Contract>(orderDocuments[0].RevisionChangeVectors);
// Query for documents:
var orderDocuments = await asyncSession.Query<Contract>()
    // Pass the path to the document property that contains the revision change vector(s)
    .Include(builder => builder
        .IncludeRevisions(x => x.RevisionChangeVector)   // Include a single revision
        .IncludeRevisions(x => x.RevisionChangeVectors)) // Include multiple revisions
    // For each document in the query results,
    // the matching revisions will be 'loaded' to the session along with the document
    .ToListAsync();

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = await asyncSession.
    Advanced.Revisions.GetAsync<Contract>(orderDocuments[0].RevisionChangeVector);
var revisions = await asyncSession
    .Advanced.Revisions.GetAsync<Contract>(orderDocuments[0].RevisionChangeVectors);
  • See the Contract class definition above.

Include revisions when making a Raw Query

  • Use include revisions in your RQL when making a raw query.

  • Pass either the revision creation time or the path to the document property containing the change vector(s),
    RavenDB will figure out the parameter type passed and include the revisions accordingly.

  • Aliases (e.g. from Users as U) are Not supported by raw queries that include revisions.


Include revisions by Time

// The revision creation time
// For example - looking for revisions from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Query for documents with Raw Query:
var orderDocuments = session.Advanced
     // Use 'include revisions' in the RQL   
    .RawQuery<Order>("from Orders include revisions($p0)")
     // Pass the revision creation time 
    .AddParameter("p0", creationTime)
     // For each document in the query results,
     // the matching revision will be 'loaded' to the session along with the document
    .ToList();

// Get a revision by its creation time for a document from the query results
// It will be retrieved from the SESSION - no additional trip to the server is made
var revision = session
    .Advanced.Revisions.Get<Order>(orderDocuments[0].Id, creationTime);
// The revision creation time
// For example - looking for revisions from last month
var creationTime = DateTime.Now.AddMonths(-1);

// Query for documents with Raw Query:
var orderDocuments = await asyncSession.Advanced
    // Use 'include revisions' in the RQL   
    .AsyncRawQuery<Order>("from Orders include revisions($p0)")
    // Pass the revision creation time 
    .AddParameter("p0", creationTime)
    // For each document in the query results,
    // the matching revision will be 'loaded' to the session along with the document
    .ToListAsync();

// Get a revision by its creation time for a document from the query results
// It will be retrieved from the SESSION - no additional trip to the server is made
var revision = await asyncSession
    .Advanced.Revisions.GetAsync<Order>(orderDocuments[0].Id, creationTime);

Include revisions by Change Vector

// Query for documents with Raw Query:
var orderDocuments = session.Advanced
     // Use 'include revisions' in the RQL   
    .RawQuery<Contract>("from Contracts include revisions($p0, $p1)")
     // Pass the path to the document properties containing the change vectors
    .AddParameter("p0", "RevisionChangeVector")
    .AddParameter("p1", "RevisionChangeVectors")
     // For each document in the query results,
     // the matching revisions will be 'loaded' to the session along with the document
    .ToList();

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = session.
    Advanced.Revisions.Get<Contract>(orderDocuments[0].RevisionChangeVector);
var revisions = session
    .Advanced.Revisions.Get<Contract>(orderDocuments[0].RevisionChangeVectors);
// Query for documents with Raw Query:
var orderDocuments = await asyncSession.Advanced
    // Use 'include revisions' in the RQL   
    .AsyncRawQuery<Contract>("from Contracts include revisions($p0, $p1)")
    // Pass the path to the document properties containing the change vectors
    .AddParameter("p0", "RevisionChangeVector")
    .AddParameter("p1", "RevisionChangeVectors")
    // For each document in the query results,
    // the matching revisions will be 'loaded' to the session along with the document
    .ToListAsync();

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
var revision = await asyncSession.
    Advanced.Revisions.GetAsync<Contract>(orderDocuments[0].RevisionChangeVector);
var revisions = await asyncSession
    .Advanced.Revisions.GetAsync<Contract>(orderDocuments[0].RevisionChangeVectors);
  • See the Contract class definition above.

Syntax

// Include a single revision by Time
TBuilder IncludeRevisions(DateTime before);

// Include a single revision by Change Vector
TBuilder IncludeRevisions(Expression<Func<T, string>> path);

// Include an array of revisions by Change Vectors
TBuilder IncludeRevisions(Expression<Func<T, IEnumerable<string>>> path);
Parameters Type Description
before DateTime
  • Creation time of the revision to be included.
  • Pass local time or UTC.
    The server will convert the param to UTC.
  • If no revision was created at this time then the first revision that precedes it is returned.
path Expression<Func<T, string>>
  • The path to the document property that contains
    a single change vector of the revision to be included.
path Expression<Func<T, IEnumerable<string>>>
  • The path to the document property that contains
    an array of change vectors of the revisions to be included.
Return value
TBuilder
  • When loading a document:
    A builder object that is used to build the include part in the Load request.
  • When querying for a document:
    A builder object that is used to build the include part in the Query RQL expression.
  • Can be used in chaining.

Patching the revision change vector

  • To include revisions when making a query or a raw query,
    you need to specify the path to the document property that contains the revision change vector(s).

  • The below example shows how to get and patch a revision change vector to a document property.

using (var session = store.OpenSession())
{
    // Get the revisions' metadata for document 'contracts/1-A'
    List<MetadataAsDictionary> contractRevisionsMetadata =
        session.Advanced.Revisions.GetMetadataFor("contracts/1-A");

    // Get a change vector from the metadata
    string changeVector = 
        contractRevisionsMetadata.First().GetString(Constants.Documents.Metadata.ChangeVector);

    // Patch the document - add the revision change vector to a specific document property
    session.Advanced
        .Patch<Contract, string>("contracts/1-A", x => x.RevisionChangeVector, changeVector);

    // Save your changes
    session.SaveChanges();
}
using (var asyncSession = store.OpenAsyncSession())
{
    // Get the revisions' metadata for document 'contracts/1-A'
    List<MetadataAsDictionary> contractRevisionsMetadata =  
        await asyncSession.Advanced.Revisions.GetMetadataForAsync("contracts/1-A");

    // Get a change vector from the metadata
    string changeVector = 
        contractRevisionsMetadata.First().GetString(Constants.Documents.Metadata.ChangeVector);

    // Patch the document - add the revision change vector to a specific document property
    asyncSession.Advanced
        .Patch<Contract, string>("contracts/1-A", x => x.RevisionChangeVector, changeVector);

    // Save your changes
    await asyncSession.SaveChangesAsync();
}
  • See the Contract class definition above.