Skip to content

EQL Guide

homedirectory edited this page Oct 11, 2024 · 4 revisions

Resolution of component-typed properties

When a component-typed property is used as an operand (e.g., comparison condition, yield operand), it has to be resolved against the source of the query. Since a property is a single-valued (scalar) operand, and a component type may have multiple components, a component-typed property implicitly represents one of its components.

As of TG version 2.0.0, the following implicit representations are used:

  • Money - sub-property amount.
  • RichText - sub-property coreText.

For example, the following yields can be used interchangeably:

yield().prop("price") // implicitly resolved to "price.amount"
yield().prop("price.amount")

Yielding component-typed properties

This section describes how to use component-typed properties in yields, both as yield operands and aliases.

The treatment of component-typed properties as yield operands is the same as in other contexts. Refer to the section about resolution of component-typed properties.

TG version 2.0.0

Support for component-typed properties in yield aliases is limited. Such properties can be used standalone in yield aliases only in a source query. In all other kinds of queries, component sub-properties must be yielded explicitly.

For the sake of demonstration, assume the following entity:

class Invoice extends AbstractEntity {
  @IsProperty
  @MapTo
  RichText comment;

  @IsProperty
  @MapTo
  Money fee;
}

The following yields use standalone component-typed properties as aliases, and are valid only in a source query:

yield().X.as("fee")
yield().X.as("comment")

An outer query that uses a source query with such yields can refer to them via prop as-is. I.e., property resolution works as expected in such cases.

sourceQ = select().
  yield().X.as("fee"). // (1)
  modelAsEntity(Note.class);
q = select(sourceQ).
  yield().beginExpr().prop("fee").mult().val(2).endExpr().as("fee.amount"). // (2)
  modelAsEntity(Note.class);

In the query above, the source query doesn't need to include .amount in the alias (1), and its enclosing query can refer to fee (recall that prop("fee") will expand to prop("fee.amount")). However, the top-level query must specify the full component sub-property path (2) in the yield alias.

This limited form of support is useful for working with synthetic entities. Synthetic entity models are always interpreted as source queries, enabling the use of alias shortcuts described in this section.

Source queries

A source query is a query that is used as a source of another query.

Example:

select(
  // source query 1
  select(InventoryItem.class).yield().prop("price").as("cost").modelAsEntity(ReTransaction.class),
  // source query 2
  select(ReturnReceipt.class).yield().prop("refund").as("cost").modelAsEntity(ReTransaction.class)
)
.where().prop("cost").ge().val(100)
.modelAsEntity(ReTransaction.class)

If two or more source queries are used, they form a union.

A query that contains source queries is subject to the following constraints:

  • If two or more source queries are used, they must have the same number of yields and use the same set of yield aliases.

    The following examples demonstrate invalid queries:

    1. Different numbers of yields.

      select(
        // 2 yields
        select(InventoryItem.class)
          .yield().prop("price").as("cost")
          .yield().prop("id").as("id")
          .modelAsEntity(ReTransaction.class),
        // 1 yield
        select(ReturnReceipt.class)
          .yield().prop("refund").as("cost")
          .modelAsEntity(ReTransaction.class)
      )
    2. Different sets of yield aliases.

      select(
        // { "cost", "id" }
        select(InventoryItem.class)
          .yield().prop("price").as("cost")
          .yield().prop("id").as("id")
          .modelAsEntity(ReTransaction.class),
        // { "expense", "id" }
        select(ReturnReceipt.class)
          .yield().prop("price").as("expense")
          .yield().prop("id").as("id")
          .modelAsEntity(ReTransaction.class)
      )
Clone this wiki locally