EQL Guide - fieldenms/tg GitHub Wiki
Table of contents
- Resolution of component-typed properties
- Yielding component-typed properties
- Source queries
- Values and parameters
- Other topics
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-propertyamount
.RichText
- sub-propertysearchText
.
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 as a yield alias only in a source query. In all other kinds of queries, component sub-properties must be yielded explicitly.
Yielding a component-typed property directly is supported for:
Money
- sub-propertyamount
is implicitly yielded instead.
For the sake of demonstration, assume the following entity:
class Invoice extends AbstractEntity {
@IsProperty
@MapTo
RichText comment;
@IsProperty
@MapTo
Money fee;
}
Properties comment
and fee
are modelled with component types, which means they cannot be directly yielded in a top-level query.
Instead, each component sub-property must be yielded explicitly.
// Invalid query
q1 = select(Invoice.class)
// equivalent to yield().prop("comment.searchText").as("comment")
.yield().prop("comment").as("comment")
// equivalent to yield().prop("fee.amount").as("fee")
.yield().prop("fee").as("fee")
.model();
// Valid query
q2 = select(Invoice.class)
.yield().prop("comment.formattedText").as("comment.formattedText")
.yield().prop("comment.coreText").as("comment.coreText")
// Yielding searchText is optional
.yield().prop("comment.searchText").as("comment.searchText")
.yield().prop("fee.amount").as("fee.amount")
.model();
Entities retrieved with query q2
will have properties comment
and fee
correctly initialised.
As described above, some component types have special support for being used as yield aliases directly.
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(Invoice.class);
q = select(sourceQ).
yield().beginExpr().prop("fee").mult().val(2).endExpr().as("fee.amount"). // (2)
modelAsEntity(Invoice.class);
Source query sourceQ
doesn't need to include .amount
in the alias (1), and its enclosing query q
can refer to fee
(recall that prop("fee")
will expand to prop("fee.amount")
).
The top-level query q
, on the other hand, 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:
-
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) )
-
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) )
-
Values and parameters
EQL queries can contain values (val()
) and parameters (param()
).
The set of types that can be used for values and parameters is constrained.
The following rules to apply to component types:
Money
-- expanded toamount
.RichText
-- cannot be used. One of its components should be used instead.