EQL - fieldenms/tg GitHub Wiki

EQL -- Entity Query Language.

EQL is a Java DSL that compiles to SQL. EQL is an expression-based language -- everything is an expression. EQL expressions are written using a Fluent API.

An EQL expression is represented by a query model, which is a sequence of tokens that represents a method call chain in the Fluent API. A query model is the equivalent of the output of a tokenizer for a standalone programming language (e.g., SQL).

Compilation to SQL is performed in stages. The input of the initial stage is a query model and the result of the last stage is:

  • an SQL statement;
  • information required to transform the raw results of executing the SQL into meaningful results (entity instances).

Grammar

A variant of the Backus-Naur Form (BNF) is employed with the following extensions:

  • { s1, ..., sn } -- grouping of symbols
  • { ... }? -- optional (none or one)
  • { ... }* -- repetition (none or more)
  • { ... }+ -- one or more
  • s(<Type1>, ..., <Typen>) -- parameterisation of a symbol; the corresponding Java method will declare n parameters with respective types
  • s(..., <Typen>*) -- parameterisation of a symbol with variable arity in the last parameter

Terminals start with a lowercase letter, non-terminals -- with an uppercase letter.

HTML version.

Query = Select
      | StandaloneExpression
      | StandaloneCondExpr
      | StandaloneOrderBy;

SourcelessSelect = select {GroupBy}? SelectEnd;

SelectEnd = Model
          | AnyYield;

Where = where Condition;

Condition = Predicate
          | AndCondition
          | OrCondition
          | CompoundCondition
          | NegatedCompoundCondition;

Predicate = UnaryPredicate
          | ComparisonPredicate
          | QuantifiedComparisonPredicate
          | LikePredicate
          | MembershipPredicate
          | SingleConditionPredicate;

UnaryComparisonOperator = isNull
                        | isNotNull;

LikeOperator = like
             | iLike
             | likeWithCast
             | iLikeWithCast
             | notLike
             | notLikeWithCast
             | notILikeWithCast
             | notILike;

ComparisonOperand = SingleOperand
                  | MultiOperand;

ComparisonOperator = eq
                   | gt
                   | lt
                   | ge
                   | le
                   | ne;

QuantifiedOperand = all(<SingleResultQueryModel>)
                  | any(<SingleResultQueryModel>);

ExprBody = SingleOperand {ArithmeticalOperator SingleOperand}*;

ArithmeticalOperator = add
                     | sub
                     | div
                     | mult
                     | mod;

SingleOperand = Prop
              | ExtProp
              | Val
              | Param
              | expr(<ExpressionModel>)
              | model(<SingleResultQueryModel>)
              | UnaryFunction
              | IfNull
              | now
              | DateDiffInterval
              | DateAddInterval
              | Round
              | Concat
              | CaseWhen
              | Expr;

UnaryFunctionName = upperCase
                  | lowerCase
                  | secondOf
                  | minuteOf
                  | hourOf
                  | dayOf
                  | monthOf
                  | yearOf
                  | dayOfWeekOf
                  | absOf
                  | dateOf;

DateIntervalUnit = seconds
                 | minutes
                 | hours
                 | days
                 | months
                 | years;

CaseWhenEnd = end
            | endAsInt
            | endAsBool
            | endAsStr(<Integer>)
            | endAsDecimal(<Integer>, <Integer>);

MultiOperand = anyOfProps(<CharSequence>*)
             | allOfProps(<CharSequence>*)
             | anyOfValues(<Object>*)
             | allOfValues(<Object>*)
             | anyOfParams(<CharSequence>*)
             | anyOfIParams(<CharSequence>*)
             | allOfParams(<CharSequence>*)
             | allOfIParams(<CharSequence>*)
             | anyOfModels(<PrimitiveResultQueryModel>*)
             | allOfModels(<PrimitiveResultQueryModel>*)
             | anyOfExpressions(<ExpressionModel>*)
             | allOfExpressions(<ExpressionModel>*);

MembershipOperator = in
                   | notIn;

MembershipOperand = values(<Object>*)
                  | props(<CharSequence>*)
                  | params(<CharSequence>*)
                  | iParams(<CharSequence>*)
                  | model(<SingleResultQueryModel>);

Join = JoinOperator {as(<CharSequence>)}? JoinCondition {Join}?;

JoinOperator = join(<Class>)
             | join(<EntityResultQueryModel>)
             | join(<AggregatedResultQueryModel>)
             | leftJoin(<Class>)
             | leftJoin(<EntityResultQueryModel>)
             | leftJoin(<AggregatedResultQueryModel>);

JoinCondition = on Condition;

GroupBy = {groupBy SingleOperand}+;

AnyYield = YieldAll
         | YieldSome;

YieldTail = Yield1Tail
          | YieldManyTail;

AliasedYield = yield YieldOperand YieldAlias;

YieldOperand = SingleOperand
             | YieldOperandExpr
             | countAll
             | YieldOperandFunction;

YieldOperandFunctionName = maxOf
                         | minOf
                         | sumOf
                         | countOf
                         | avgOf
                         | sumOfDistinct
                         | countOfDistinct
                         | avgOfDistinct;

YieldAlias = as(<CharSequence>)
           | as(<Enum>)
           | asRequired(<CharSequence>)
           | asRequired(<Enum>);

Yield1Model = modelAsEntity(<Class>)
            | modelAsPrimitive;

YieldManyModel = modelAsEntity(<Class>)
               | modelAsAggregate;

Model = model
      | modelAsEntity(<Class>)
      | modelAsAggregate;

StandaloneCondition = Predicate
                    | AndStandaloneCondition
                    | OrStandaloneCondition;

OrderBy = orderBy {OrderByOperand}+ {Limit}? {Offset}?;

OrderByOperand = OrderByOperand_Single
               | OrderByOperand_Yield
               | OrderByOperand_OrderingModel;

Order = asc
      | desc;

Select = SelectFrom
       | SourcelessSelect;

SelectFrom = SelectSource {as(<CharSequence>)}? {Join}? {Where}? {GroupBy}? {OrderBy}? SelectEnd;

SelectSource = select(<Class>)
             | select(<EntityResultQueryModel>*)
             | select(<AggregatedResultQueryModel>*);

StandaloneExpression = expr YieldOperand {ArithmeticalOperator YieldOperand}* model;

StandaloneCondExpr = cond StandaloneCondition model;

StandaloneOrderBy = orderBy {OrderByOperand}+ {Limit}? {Offset}? model;

AndCondition = Condition and Condition;

OrCondition = Condition or Condition;

CompoundCondition = begin Condition end;

NegatedCompoundCondition = notBegin Condition end;

UnaryPredicate = ComparisonOperand UnaryComparisonOperator;

ComparisonPredicate = ComparisonOperand ComparisonOperator ComparisonOperand;

QuantifiedComparisonPredicate = ComparisonOperand ComparisonOperator QuantifiedOperand;

LikePredicate = ComparisonOperand LikeOperator ComparisonOperand;

MembershipPredicate = ComparisonOperand MembershipOperator MembershipOperand;

SingleConditionPredicate = exists(<QueryModel>)
                         | notExists(<QueryModel>)
                         | existsAnyOf(<QueryModel>*)
                         | notExistsAnyOf(<QueryModel>*)
                         | existsAllOf(<QueryModel>*)
                         | notExistsAllOf(<QueryModel>*)
                         | critCondition(<CharSequence>, <CharSequence>)
                         | critCondition(<ICompoundCondition0>, <CharSequence>, <CharSequence>)
                         | critCondition(<ICompoundCondition0>, <CharSequence>, <CharSequence>, <Object>)
                         | condition(<ConditionModel>)
                         | negatedCondition(<ConditionModel>);

Prop = prop(<CharSequence>)
     | prop(<Enum>);

ExtProp = extProp(<CharSequence>)
        | extProp(<Enum>);

Val = val(<Object>)
    | iVal(<Object>);

Param = param(<CharSequence>)
      | param(<Enum>)
      | iParam(<CharSequence>)
      | iParam(<Enum>);

UnaryFunction = UnaryFunctionName SingleOperand;

IfNull = ifNull SingleOperand then SingleOperand;

DateDiffInterval = count DateIntervalUnit between SingleOperand and SingleOperand;

DateAddInterval = addTimeIntervalOf SingleOperand DateIntervalUnit to SingleOperand;

Round = round SingleOperand to(<Integer>);

Concat = concat SingleOperand {with SingleOperand}* end;

CaseWhen = caseWhen Condition then SingleOperand {when Condition then SingleOperand}* {otherwise SingleOperand}? CaseWhenEnd;

Expr = beginExpr ExprBody endExpr;

YieldAll = yieldAll {AliasedYield}* YieldManyModel;

YieldSome = yield YieldOperand YieldTail;

Yield1Tail = Yield1Model;

YieldManyTail = YieldAlias {AliasedYield}* YieldManyModel;

YieldOperandFunction = YieldOperandFunctionName SingleOperand;

YieldOperandExpr = beginExpr YieldOperand {ArithmeticalOperator YieldOperand}* endExpr;

AndStandaloneCondition = StandaloneCondition and StandaloneCondition;

OrStandaloneCondition = StandaloneCondition or StandaloneCondition;

OrderByOperand_Single = SingleOperand Order;

OrderByOperand_Yield = yield(<CharSequence>) Order;

OrderByOperand_OrderingModel = order(<OrderingModel>);

Limit = limit(<long>)
      | limit(<Limit>);

Offset = offset(<long>);

Reference

caseWhen

If all clauses return val(null), then one of endAs* constructs must be used. It is illegal to use end() in such an expression, otherwise EQL would have to guess which data type the user intended.

Example:

// illegal!
caseWhen().prop("amount").eq().val(54).then().val(null).otherwise().val(null).end();
// illegal!
caseWhen().prop("amount").eq().val(54).then().val(null).end();
// good
caseWhen().prop("amount").eq().val(54).then().val(null).endAsInt();

Glossary

  • Subchaining -- a style of fluent API design that passes a chain to a method call in another chain as its argument.

    select(X, where( prop("x").gt(5) ))

  • Non-subchaining -- style of fluent API design that is the opposite subchaining -- all methods must be called sequentially.

    select(X).where().prop("x").gt(5)

⚠️ **GitHub.com Fallback** ⚠️