CEL dialect for document validation

Document AI validation and correction leverages the Common Expression Language (CEL) to allow for flexible data validation and manipulation within your document processing workflows. Document AI offers a set of custom functions, macros, and behavioral modifications tailored for document entity data.

Access entities in CEL expression

All expressions are evaluated against a root variable named doc, which consists of entities that are phrases or properties belonging to the document. These entities closely follow the structure of the entities of your extracted document.

While an extracted entity contains many properties, only three of them are available for CEL evaluation.

  • mention_text: Raw extracted text as present in an extracted entity. It defaults to an empty string.
  • normalized_value: Normalized mention text as present in an extracted entity. It defaults to null. Read more on Normalization.
  • bounding_poly: A special object containing a representation of the extracted entity's placement in the document and used for alignment checks. It defaults to null.

Data model

The exact structure of an extracted entity within the doc map depends on two factors. The first is whether its structure is a concrete value, such as a number or plain text, or if it's a complex object. The second factor is whether its occurrence type is single or multiple. For more information, see OccurrenceType.

A key feature of the validation data model is that any entity defined in the schema but not extracted from the document is automatically populated with default values. This design lets you skip most of the explicit null checks in your CEL expressions, significantly simplifying your validation expressions. You only need to explicitly write null checks to ensure a selected entity was indeed extracted.

Leaf entities example cases

The following sections describe how to access the entities in a leaf entity, which is one without nested child entities. Leaf entities directly hold a value.

Leaf entity with a single occurrence

This is the most basic case, using OccurrenceType of OPTIONAL_ONCE or REQUIRED_ONCE. The entity is represented as an object containing the three standard properties.

An example of how to access these values is doc.invoice_date.normalized_value.

It has the structure:

  "invoice_date": {
    "mention_text": "1",
    "normalized_value": 1.0,
    "bounding_poly": bounding_poly_object
  }

And default value:

  "invoice_date": {
    "mention_text": "",
    "normalized_value": null,
    "bounding_poly": null
  }

Leaf entity with multiple occurrences

This case applies to leaf entities which can occur multiple times and have an OccurrenceType of OPTIONAL_MULTIPLE or REQUIRED_MULTIPLE. For example, in a list of payment due dates, it's represented as an object where each property holds a list of the corresponding values from all occurrences. So properties like mention_text, normalized_value, and bounding_poly might have multiple entities.

An example of how to access these values is doc.payment_due_dates.normalized_value[0].

It has the structure:

  "payment_due_dates": {
    "mention_text": ["Mar 1, 2024", "Apr 1, 2024"],
    "normalized_value": [null, proto.timestamp(2024-04-01)],
    // Note: If a value is not normalized, it is stored as a null.
    "bounding_poly": [bounding_poly_object,bounding_poly_object]
  }

And default value:

  "payment_due_dates": {
    "mention_text": [],
    "normalized_value": []
    "bounding_poly": []
  }

Nested entities

A nested entity is a container for other entities, which are its "children."

Nested entity with one occurrence

If a nested entity occurs only once, for example a single receiver_address, it's represented as an object where the keys are the names of its child entities.

An example of how to access these values is doc.receiver_address.city.mention_text.

It has the structure:

  "receiver_address": {
    "street": {
      "mention_text": "123 Main St",
      "normalized_value": "123 Main St",
      "bounding_poly":