Skip to content

Expressions

Expressions define values in the Projection Declaration Language. They can reference event properties, event context, literals, and create formatted strings.

Access properties from the event using dot notation:

name
email
address.city
contactInfo.email
order.customer.name

Examples:

from UserRegistered
Name = name
Email = contactInfo.email
City = address.city

Access event metadata using $eventContext.{property}:

$eventContext.occurred
$eventContext.sequenceNumber
$eventContext.correlationId
$eventContext.eventSourceId

Available Properties:

  • occurred - When the event occurred (timestamp)
  • sequenceNumber - Sequence number in the event stream
  • correlationId - Correlation ID for tracking related operations
  • eventSourceId - The event source identifier

Examples:

from OrderPlaced
PlacedAt = $eventContext.occurred
Sequence = $eventContext.sequenceNumber
Correlation = $eventContext.correlationId

See Event Context for more details.

Quick access to the event source identifier:

$eventSourceId

This is equivalent to $eventContext.eventSourceId.

Example:

from UserAssignedToGroup
GroupId = $eventSourceId
UserId = userId

Access the identity that caused the event using $causedBy:

$causedBy.subject
$causedBy.name
$causedBy.userName

Available Properties:

  • subject - The identifier of the identity (unique ID)
  • name - Display name of the identity
  • userName - Username of the identity

Examples:

from OrderPlaced
CreatedBySubject = $causedBy.subject
CreatedByName = $causedBy.name
CreatedByUser = $causedBy.userName
from DocumentUpdated
UpdatedBy = `${$causedBy.name} (${$causedBy.userName})`
UpdatedById = $causedBy.subject

Direct values of various types:

Boolean:

IsActive = true
IsDeleted = false

String:

Status = "Pending"
Category = "Electronics"

Number:

Count = 0
Price = 99.99
Tax = 0.08

Null:

OptionalField = null

Examples:

from AccountCreated
Balance = 0.0
IsActive = true
Status = "New"
Notes = null

Create formatted strings with embedded expressions:

`${expression}`
`${firstName} ${lastName}`
`Order: ${orderNumber}`

Syntax:

  • Wrap in backticks: `
  • Use ${...} for expression substitution
  • Can include multiple expressions

Examples:

from PersonRegistered
FullName = `${firstName} ${lastName}`
DisplayInfo = `${name} (${email})`
from OrderPlaced
Reference = `ORD-${orderNumber}`
Summary = `Order ${orderNumber} for ${customerName}`

Expressions are evaluated in different contexts:

When mapping from events, expressions reference event properties:

from UserCreated
Name = name # References event.name
Email = email # References event.email

Special $eventContext provides metadata:

from UserCreated
CreatedAt = $eventContext.occurred
CreatedBy = $eventContext.eventSourceId

Combine event data with event context:

from OrderPlaced
OrderNumber = orderNumber # Event data
PlacedAt = $eventContext.occurred # Event context
Reference = `${orderNumber}-${$eventContext.sequenceNumber}` # Both

The declaration syntax is transformed when compiled into Chronicle projection definitions:

Declaration SyntaxCompiled FormatDescription
namenameEvent property remains unchanged
address.cityaddress.cityNested properties remain unchanged
$eventContext.occurred$eventContext(occurred)Dot notation becomes function-style
$eventSourceId$eventSourceIdRemains unchanged
truetrueLiterals remain unchanged
`${name}``${name}`Templates remain unchanged

This transformation ensures compatibility with Chronicle’s internal expression format while keeping the declaration syntax clean and intuitive.

Expressions must be compatible with the target property type:

public class UserReadModel
{
public string Name { get; set; } // Requires string
public int LoginCount { get; set; } // Requires number
public bool IsActive { get; set; } // Requires boolean
public DateTime CreatedAt { get; set; } // Requires timestamp
}

Valid:

Name = name # string
LoginCount = 0 # number
IsActive = true # boolean
CreatedAt = $eventContext.occurred # timestamp

Invalid:

Name = 123 # number to string (invalid)
LoginCount = "five" # string to number (invalid)
IsActive = "yes" # string to boolean (invalid)

Access deeply nested properties:

from OrderPlaced
CustomerEmail = order.customer.contactInfo.email
ShippingCity = order.shipping.address.city
BillingStreet = billing.address.line1
Property = expression

Examples:

Name = name
Total = order.total
CreatedAt = $eventContext.occurred
DisplayName = `${firstName} ${lastName}`
key expression

Examples:

from UserCreated key userId
from OrderPlaced key $eventSourceId
from LineItem key order.id
key Type {
Property = expression
...
}

Examples:

key OrderLineKey {
OrderId = orderId
LineNumber = lineNumber
Sequence = $eventContext.sequenceNumber
}
add Property by expression
subtract Property by expression

Examples:

add Balance by amount
subtract Stock by quantity
add Total by lineItem.total
parent expression

Examples:

parent groupId
parent $eventContext.eventSourceId
parent order.customerId
id expression

Examples:

children members identified by userId
children items identified by lineNumber
children versions identified by $eventContext.sequenceNumber
  1. Use Property Paths: Directly access nested properties rather than mapping intermediate objects
  2. Event Context for Metadata: Use $eventContext for timestamps, IDs, and correlation
  3. Templates for Display: Create formatted display values with templates
  4. Literals for Defaults: Set initial values with appropriate literal types
  5. Type Awareness: Ensure expressions produce values compatible with target properties
  6. Shorthand When Clear: Use $eventSourceId shorthand for clarity
  7. Null Handling: Use null literal for optional fields
CreatedAt = $eventContext.occurred
CreatedBy = $eventContext.eventSourceId
LastUpdated = $eventContext.occurred
Version = $eventContext.sequenceNumber
FullName = `${firstName} ${lastName}`
Address = `${street}, ${city}, ${state} ${zipCode}`
Summary = `${product} x ${quantity} @ ${price}`
Email = contact.email
Phone = contact.phoneNumbers.primary
City = addresses.shipping.city
Status = "Pending"
IsActive = true
Count = 0
Balance = 0.0
Notes = null