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
$creationTime = (new DateTime())->sub(new DateInterval("P1M"));

// Load a document:
$order = $session->load(Order::class, "orders/1-A", function($builder) use ($creationTime) {
        return $builder
            // Pass the revision creation time to 'IncludeRevisionsBefore'
            // The revision will be 'loaded' to the session along with the document
            ->includeRevisionsBefore($creationTime);
    });

// Get the revision by creation time - it will be retrieved from the SESSION
// No additional trip to the server is made
$revision = $session
    ->advanced()->revisions()->get(Order::class, "orders/1-A", $creationTime);

Include revisions by Change Vector:

// Load a document:
$contract = $session->load(Contract::class, "contracts/1-A", function($builder) {
    return $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("RevisionChangeVector")
        ->includeRevisions("RevisionChangeVectors");
});

// Get the revision(s) by change vectors - it will be retrieved from the SESSION
// No additional trip to the server is made
$revision = $session->advanced()->revisions()->get(Contract::class, "RevisionChangeVector");
$revisions = $session->advanced()->revisions()->get(Contract::class, "RevisionChangeVectors");

// Sample Contract document
class Contract
{
    private ?string $id = null;
    private ?string $name = null;
    private ?string $revisionChangeVector = null; // A single change vector
    private ?array $revisionChangeVectors = null; // A list of change vectors

    public function getId(): ?string
    {
        return $this->id;
    }

    public function setId(?string $id): void
    {
        $this->id = $id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(?string $name): void
    {
        $this->name = $name;
    }

    public function getRevisionChangeVector(): ?string
    {
        return $this->revisionChangeVector;
    }

    public function setRevisionChangeVector(?string $revisionChangeVector): void
    {
        $this->revisionChangeVector = $revisionChangeVector;
    }

    public function getRevisionChangeVectors(): ?array
    {
        return $this->revisionChangeVectors;
    }

    public function setRevisionChangeVectors(?array $revisionChangeVectors): void
    {
        $this->revisionChangeVectors = $revisionChangeVectors;
    }
}

Include revisions when making a Query

Include revisions by Time:

// The revision creation time
// For example - looking for revisions from last month
$creationTime = (new DateTime())->sub(new DateInterval("P1M");

// Query for documents:
$orderDocuments = $session->query(Order::class)
    ->whereEquals("ShipTo.Country",  "Canada")
     // Pass the revision creation time to 'IncludeRevisionsBefore'
    ->include(function($builder) use ($creationTime) { return $builder->includeRevisionsBefore($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
$revision = $session
    ->advanced()->revisions()->getBeforeDate(Order::class, $orderDocuments[0]->getId(), $creationTime);

Include revisions by Change Vector:

// Query for documents:
$orderDocuments = $session->query(Contract::class)
     // Pass the path to the document property that contains the revision change vector(s)
    ->include(function($builder) {
         return $builder
             ->includeRevisions("getRevisionChangeVector")   // Include a single revision
             ->includeRevisions("getRevisionChangeVectors"); // 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
$revision = $session
    ->advanced()->revisions()->get(Contract::class, $orderDocuments[0]->getRevisionChangeVector());
$revisions = $session
    ->advanced()->revisions()->get(Contract::class, $orderDocuments[0]->getRevisionChangeVectors());
  • 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
$creationTime = (new DateTime())->sub(new DateInterval("P1M"));

// Query for documents with Raw Query:
$orderDocuments = $session->advanced()
     // Use 'include revisions' in the RQL
    ->rawQuery(Order::class, "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
$revision = $session
    ->advanced()->revisions()->getBeforeDate(Order::class, $orderDocuments[0]->getId(), $creationTime);

Include revisions by Change Vector:

// Query for documents with Raw Query:
$orderDocuments = $session->advanced()
     // Use 'include revisions' in the RQL
    ->rawQuery(Contract::class, "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
$revision = $session
    ->advanced()->revisions()->get(Contract::class, $orderDocuments[0]->getRevisionChangeVector());
$revisions = $session
    ->advanced()->revisions()->get(Contract::class, $orderDocuments[0]->getRevisionChangeVectors());
  • See the Contract class definition above.

Syntax

// Include a single revision by Time
public function includeRevisionsBefore(DateTime $before): IncludeBuilderInterface;

// Include a single revision by Change Vector path(s)
public function includeRevisions(string $changeVectorPaths): IncludeBuilderInterface;
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.
changeVectorPaths string
  • The path to the document property that contains
    an array of change vectors of the revisions to be included.

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.

$session = $store->openSession();
try {
    // Get the revisions' metadata for document 'contracts/1-A'
    /** @var array<MetadataAsDictionary> $contractRevisionsMetadata */
    $contractRevisionsMetadata =
        $session->advanced()->revisions()->getMetadataFor("contracts/1-A");

    // Get a change vector from the metadata
    $changeVector = $contractRevisionsMetadata[array_key_first($contractRevisionsMetadata)]->getString(DocumentsMetadata::CHANGE_VECTOR);

    // Patch the document - add the revision change vector to a specific document property
    $session->advanced()
        ->patch( "contracts/1-A", "RevisionChangeVector", $changeVector);

    // Save your changes
    $session->saveChanges();
} finally {
    $session->close();
}
  • See the Contract class definition above.