Attributes
Attribute Syntax
The simplest way to retrieve an attribute value is to specify its name:
name
Note
The colon is part of the name and is not a special character in this context.
If no scalar is specified, it defaults to “?disp”. That is, the expression above is equivalent to the following:
name?disp
For values of type “String”, there is no difference between the scalars “?disp” and “?str” since they return the same value.
To access a nested attribute, names should be separated by a dot:
counterparty.fullOrgName?str
If a list of values is expected at some level in an attribute, use square brackets “[]” after the attribute name:
counterparty[].fullOrgName?str
manager.subordinates[].userName?str
manager.department.managers[].subordinates[].userName?str
If we requested an attribute without square brackets but the data source returned a list, we will get only the first element of that list, or null if the list is empty.
To retrieve multiple attributes of a nested value at once, you can use curly braces:
manager.subordinates[]{userName:"userName?str",firstName:"firstName"}
The result will have the following structure:
[
{
"userName": "ivan.ivanov",
"firstName": "Ivan"
},
{
"userName": "petr.petrov",
"firstName": "Petr"
}
]
Attributes support post-processors, which allow performing operations on the result before returning it to the client.
Post-processors are specified after the attribute using the pipe symbol “|”.
Date formatting:
_created|fmt("yyyy__MM__dd")
More details about the date format pattern can be found here: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
Number formatting:
documentAmount|fmt("00000.00")
This format pads the number with leading zeros if its integer part is fewer than 5 digits and limits the decimal places to two.
More details about the number format pattern can be found here: https://docs.oracle.com/javase/7/docs/api/java/text/DecimalFormat.html
Default value:
documentAmount?num|or(0)
If the documentAmount attribute returns null, we will receive the number 0 instead.
The “or” processor has a short notation using “!”:
documentAmount?num!0
Other attributes can be used in the “or” processor:
title?str!name?str
title?str|or("a:name?str")
In this example, we will get the value of title or the value of name if title is null or an empty string.
Note
This attribute is given as an example; to get “title or name”, it is better to use the scalar “?disp”.
In the full form, you need to specify the “a:” prefix to indicate that you need the attribute value, not the constant “name?str”. If you need a constant string value in the short form, enclose the value in quotes:
title?str!"name"
Adding a prefix or suffix:
name|presuf("prefix-","-suffix")
If the value of name is “Name”, the output will be “prefix-Name-suffix”. The suffix value is optional. If no prefix is needed but a suffix is, an empty string can be passed as the first argument.
Processors can be chained:
title!name!"n-a"|presuf("prefix-","-suffix")
Take the title;
If the title is empty, take the name;
If the name is empty, take the constant “n-a”;
Add the prefix “prefix-” to the result of steps 1–3;
Add the suffix “-suffix” to the result of step 4.
Name |
Arguments |
Description |
|---|---|---|
presuf |
prefix: Stringsuffix: String |
Add a constant to the beginning and/or end of a string |
or |
orValue0: AnyorValue1: AnyorValueN: Any |
Return a default value if the attribute value is null. If the argument is a string
and starts with “a:”, the remaining part is treated as another attribute that
should be evaluated and returned as the result.
The number of arguments is unlimited. Arguments are evaluated in order
and if it is not null (neither directly null nor resolved to null via “a:”), the result is returned immediately.
|
rxg |
pattern: StringgroupIdx: Int = 1 |
Apply a regular expression to the result and return the specified group.
Examples:
"some-text" | rxg("some-(.+)") -> text"some-text-and-more" | rgx("(some)-(text)-(and)-(more)", 2) -> text |
join |
|
Join a list of values into a string using the specified delimiter |
hex (3.26.0+) |
delimiter: String = "" |
Represent a base64 string as a HEX string (a list of hexadecimal numbers,
where each byte is represented by two characters)
|
fmt |
format: Stringlocale: String = "en"timezone: String = "UTC" |
Format a number or date according to the specified format |
cast |
type: { "str", "num", "bool" } |
Converts the value to the specified type. |
yaml |
Converts any structure to a YAML string. | Example:
|
Search Queries
Grouping
In a query, grouping attributes can be specified via the groupBy parameter. If the Records DAO supports grouping (implements the RecsGroupQueryDao interface), RecordsService passes the query as-is to the DAO. If the Records DAO does not support grouping, RecordsService attempts to perform grouping on its own using additional queries. This mechanism is called “auto-grouping”.
Since auto-grouping may be undesirable in some cases, a flag is available to disable it: ecos.webapp.records.queryAutoGroupingEnabled.
If this flag is set to false and the target Records DAO does not support grouping, all queries with a non-empty groupBy will return an empty list.
Working with MLText Fields (3.26.0+)
If it is known that a particular attribute contains a string or an MLText structure (an object where keys are locales and values are the corresponding strings), the “mltext” transformation can be applied.
Example:
some.att._as.mltext // получение актуального значения по локали пользователя
some.att._as.mltext.ru // получение значения для конкретной локали
some.att._as.mltext.closest.ru // получение значения для конкретной локали с попыткой вычислить ближайшее не пустое значение
some.att._as.mltext?json // получение значения для всех локалей (если some.att является строкой, то она будет соответствовать локали "en")
The transformation works for String, DataValue, MLText, ObjectData, JsonNode (jackson).
Using Dynamic Attributes in Predicates (3.26.0+)
When using predicate-based search across all record sources, it is possible to specify dynamically computed attributes instead of static values.
Example query with the current user:
{
"t": "eq",
"att": "actor",
"val": "${$user.userName}"
}
If ${} is the only expression and occupies the entire string, "${...}" is fully replaced with the computed value. Thus, the result of evaluating the template can be any JSON type, including null. Dynamic insertions can be used at any nesting level for any values in objects (t, att, val can all be specified).
The list of available attributes can be found in the “Context Attributes” section.
Context Attributes
There are often situations where attributes need to be loaded that are not directly related to the entity but are contextual.
Examples of such attributes:
Current user
Current date
To access such attributes when querying data, a “$” sign is prepended to the attribute name.
So, if we need to get the current user’s name, we can load the following attribute:
$user.userName
If we need to get the current date and format it:
$now|fmt("yyyy")
List of context attributes available in all sources:
user — Current user
now — Current date
auth — Authentication of the current user. This attribute can be used to check whether the user is a member of a group or a global role:
$auth._has.GROUP_ECOS_ADMINISTRATORS?bool $auth._has.ROLE_ADMIN?boolstr — Attribute for specifying a constant string value
ref — Attribute for specifying a reference to another entity
appName — Name of the current application
appInstanceId — Identifier of the current application instance
If the available list of context attributes needs to be extended in server-side code, RecordsService should be used as follows:
val contextAtts = mutableMapOf<String, Object>()
contextAtts["customVariable"] = RecordRef.valueOf("emodel/person@someuser")
String result = RequestContext.doWithAtts(contextAtts) {
recordsService.getAtt("any-record", "$customVariable?disp").asText()
}
Values for context attributes can be EntityRefs (for accessing other entities) or values of any other type.