Predicate Language

The predicate language is designed for convenient use on both the frontend and backend sides — for modification, analysis, and other operations.

From an abstract definition standpoint, a predicate is a function that takes an element of some set and returns True or False.

The ecos-records library provides an implementation of this concept.

A predicate has two aggregate representations — JSON or Java classes. To convert between these two states, the PredicateService and the writeJson/readJson methods are available. The Java representation is simply a model.

Model processing is a separate concern handled by data sources or query transformers (for example, for Alfresco queries, predicates are transformed into an fts-alfresco query in the PredicateToFtsAlfrescoConverter class).

Java Representation

Predicate
  • Predicate — base marker interface with no methods

  • AttributePredicate — predicate associated with a specific attribute

  • EmptyPredicate — predicate “Attribute value is empty”

  • ValuePredicate — predicate “Attribute value equals / contains / is one of / greater than / less than / matches / greater than or equal / less than or equal”. The comparison type is defined by the type field. The comparison value is in the value field

  • NotPredicate — predicate “NOT some_other_predicate”

  • ComposedPredicate — predicate that combines other predicates

  • AndPredicate — AND predicate

  • OrPredicate — OR predicate

  • VoidPredicate — empty predicate produced when t (type) is not set. Usually means that no checks are needed during filtering/search (all records are returned).

JSON Representation

Example:

{
   "t": "and",
   "val": [
       {
           "t": "or",
           "val": [
               {
                   "att": "country",
                   "t": "contains",
                   "val": "milan"
               },
               {
                   "att": "state",
                   "t": "contains",
                   "val": "milan"
               },
               {
                   "att": "name",
                   "t": "contains",
                   "val": "milan"
               }
           ]
       }
   ]
}

The “t” field holds the predicate type, “att” holds the attribute name (if the predicate type requires it), and “val” holds the value (again, only meaningful for some predicates).

To convert Java predicates to JSON and back:

String predicateStr = Json.getMapper().toString(predicate);
Predicate predicate = Json.getMapper().read(predicateStr, Predicate.class);

To read predicates from a RecordsQuery request:

Predicate predicate = recordsQuery.getQuery(Predicate.class);

Predicate Types

Predicate Types

Predicate

Description

FTS

starts

The string starts with val.
Example:
{t: «starts», att:«title», val: «startsWIth»}

title:«startsWith»

ends

The string ends with val.
Example:
{t: «ends», att:«title», val: «endsWIth»}

title:«*endsWith»

or

Combines the array of predicates in val using OR.
Example:
{t: «or», val: [{t: «starts», att:«title», val: «startsWIth»}, {t: «ends», att:«title», val: «endsWIth»}]}

and

Combines the array of predicates in val using AND.
Example:
{t: «and», val: [{t: «starts», att:«title», val: «startsWIth»}, {t: «ends», att:«title», val: «endsWIth»}]}

empty

The field specified in att is empty.
Example:
{t: «empty», att: «title»}

not

Negation of the predicate in val.
Example:
{t: «not», val:{t: «empty», att: «title»}}
This predicate can be automatically prepended to others by adding the “not-” prefix.
Example:
{t: «not-eq», att:«title», val: «startsWIth»}

eq

The value of the att field is exactly equal to val.
Example:
{t: «eq», att:«title», val: «SomeValue»}

gt

The value of the att field is greater than val

ge

The value of the att field is greater than or equal to val

lt

The value of the att field is less than val

le

The value of the att field is less than or equal to val

like

The att value matches the val pattern.
In the pattern, % is used as a wildcard for any number of characters (as in a database SELECT query)

in

The att value matches the val pattern.

contains

The att value contains the val substring (also works for associations)

For example:

Additional Predicate Features

Additional predicate features

Feature

Description

Ranges and Durations

For attributes of type date and datetime, the ability to compute durations and ranges has been added
When a range is specified with the first boundary, it is calculated from the current date and time
When a range is specified with the second boundary, it is calculated from the first boundary
The search is inclusive of boundaries, i.e. [DurationOrDateTime1, DurationOrDateTime2]
Two constants are available: $NOW — current date and time, $TODAY — current date
Examples:
Find documents created within the last 10 days:
{t: "ge", att:"_created", val: "-P10D"}
Find documents where the range is from $NOW minus two years, to ($NOW minus two years) plus one year
{t: "eq", att:"_created", val: "-P2Y/P1Y"}
Find documents created within the last 2 years, up to the current date and time
{t: "eq", att:"_created", val: "-P2Y/$NOW"}
Find documents created within the last 2 years, up to today
{t: "eq", att:"_created", val: "-P2Y/$TODAY"}
from 2020-01-01 to 2020-02-01
{t: "eq", att:"_created", val: "2020-01-01T00:00:00Z/2020-02-01T00:00:00Z"}
from 2020-01-01 to 2020-01-01 plus 1 month
{t: "eq", att:"_created", val: "2020-01-01T00:00:00Z/P1M"}
from minus 10 days to 2020-01-01
{t: "eq", att:"_created", val: "-P10D/2020-01-01T00:00:00Z"}
from today
{t: "eq", att:"_created", val: "$TODAY"}

Predicate Debugging

  1. To debug predicates, you can use the following script in the browser console:

await Records.queryOne({
    sourceId: 'uiserv/predicate',
    query: {
        record: 'emodel/sed-agreement@8a2964d4-e83b-4337-b033-05e7e4a232fc',
        predicate: {
          "t": "and",
          "val": [
            {
              "t": "not",
              "val": {
                "t": "eq",
                "att": "_status?str",
                "val": "Canceled"
              }
            }
          ]
        }
    }
}, 'result?bool')

JSON <-> YAML conversion can be done unambiguously in any online service by searching for “yaml to json converter”.

  1. It is also useful for debugging to load attribute values and verify what is being compared with what:

await Records.get(
    'emodel/sed-agreement@8a2964d4-e83b-4337-b033-05e7e4a232fc'
).load([
    '_status?str',
    "_status?id",
    "_status?disp",
    "_roles.isCurrentUserMemberOf.author?bool",
    "_roles.isCurrentUserMemberOf.admin?bool"
]);
{
  "_status?str": "Canceled",
  "_status?id": "emodel/sed-agreement@Canceled",
  "_status?disp": "Отменено",
  "_roles.isCurrentUserMemberOf.author?bool": false,
  "_roles.isCurrentUserMemberOf.admin?bool": true
}

Showing a button under specific conditions described by a predicate

_status is a localized text field, therefore _status?str is used

t: not
val:
  t: in
  att: _status?str
  val:
    - draft
    - Canceled
    - Rejected