Aggregate
Projection – Abstract
Purpose: Enables simple calculations for individual property values and aggregations for lists of property The values, which can optionally be formed by grouping as subsets from a basic set defined by the search.
The Aggregate projection type enables various simple calculations (for individual property values) and aggregations for lists of property values, which can optionally be formed by grouping as subsets from a basic set defined by the search.
The following table divides the options for the Type of calculation/aggregation into categories and specifies for each Type which data type is supported per Projection.
Only the types summarized here as aggregation functions can process several return values from the Projection and, if necessary, take into account subsets from a grouping in the context of the search.
The other calculation functions enable simple calculations or 'conversions' of individual values of a specific data type from the Projection.
Type |
Function name |
Data type for the projection |
Description |
Aggregation functions |
|||
Count |
COUNT |
any |
Number of individual values to be aggregated that are not 'No value' ($null). |
Count (distinct) |
COUNT_DISTINCT |
Number of different individual values that are not 'No value' ($null). |
|
Min |
MIN |
Minimum value (according to the sorting logic of the database used for the data type) |
|
Max |
MAX |
Maximum value (according to the sorting logic of the database used for the data type) |
|
Least |
LEAST |
Minimum value (according to the sorting logic of the database used for the data type) |
|
Sum |
SUM |
numerical |
Sum of the individual values to be aggregated |
Average |
AVG |
Arithmetic mean of the individual values to be aggregated that are not 'No value' ($null). |
|
Arithmetic (for single numerical value) |
|||
Absolute |
ABS |
numerical |
Returns the absolute value of a numerical value |
Negate |
NEG |
Changes the sign of a numerical value (+|-) |
|
Square root |
SQRT |
Square root of a numerical value |
|
Logic (for single Boolean value) |
|||
Not |
NOT |
Boolean |
Reverses a truth value (true|false) |
Text processing (for single text value) |
|||
Length |
LENGTH |
String |
Length of a character string (Long value) or $null |
Lower |
LOWER |
Returns a character string in lower case letters |
|
Upper |
UPPER |
Returns a character string in capital letters |
|
Trim |
TRIM |
'Trims' (=removes) marginal space characters from a character string |
Configuration
Parameter |
Type |
Description |
Name |
String |
The optional Name parameter can be used to assign an (alias) name to the projection.
|
Type |
AggregateType
|
The Type determines the type of aggregation, calculation and conversion to be performed on the basis of the Projection.
►NOTE◄ The breakdown of the above table by category ('Aggregation functions', 'Arithmetic', ...) is not reflected in the dropdown. |
Projection |
Projection |
In principle, any Projections can be configured for the Projection parameter as long as they return a data type permitted for the Type and the database system used supports their use in the given context.
|
Examples of the conversion of individual values
Arithmetic – Absolute (ABS): Absolute value of a numerical field as a restriction projection
The default property 'Last modifier ID' (lastModifierId) for entities usually contains a positive Long value that corresponds to the 'ID' (id) of a user account (see Users).
However, if the last change to an entity was made in the context of a guest user account (see Guest users), its 'ID' (id) is entered as a negative value in the lastModifierId property to ensure differentiation from users with the same ID.
In the data grid of an overview, the 'Last modifier ID' column appears empty by default if this property references a guest user account, because looking up the user name (see Subsearch projection), which is implemented as a column projection by default, is 'unproductive' for negative references.
Regardless of the display, the filter function for this column should be adapted so that the filter criteria for the positive Long value of an account ID can be entered, regardless of whether it is a Users or Guest users.
Configuration:
The settings for the 'Last modifier ID' column (lastModifierId) must be displayed in the data grid settings for the relevant overview.
For this column, there is no configuration for a projection in the tab for defining the Restriction projection by default. This means that by default the Projection is also a Restriction projection.
If you configure a Restriction projection as shown on the right, it overrides the default behaviour as follows:
Instead of the text value tailored to Users (scheme: {id} - {username}), an Aggregate with the Type 'Absolute' (ABS) is used as a compare value for the filter, which is used here to remove any minus sign (for Guest users) from the Long value read from the Property projection for the lastModifierId property.
The positive ID values for Users return the Aggregate with the Type 'Absolute' (ABS) unchanged.
The characteristic of the filter adapts to the numerical data type.
►NOTE◄ Of course, it is not ideal that this Restriction projection cannot distinguish between a user account and a guest user account with the same ID. However, if overlaps between the IDs used are relevant in practice, it is still possible to adapt the Projection so that Users and Guest users can still be clearly distinguished from each other.
Runtime example:
|
The screenshot on the left shows an excerpt from the 'Last modifier' column, for which not only the Restriction projection (as above) has been adjusted, but also the Projection, so that for Guest users the (positive) ID of the account appears with the prefix 'G#'. As the filter expression (7251|2701) makes clear, the filter still refers exclusively to the positive ID values of the relevant accounts as desired. ►NOTE◄ The Projection uses a Case projection (see screenshot below), which compiles the display text for references to the Guest users in the right branch of the case differentiation using a simple Concatenated projection. Again, an Aggregate with the Type 'Absolute' (ABS) is used to eliminate the minus sign of the negative lastModifierId value. The default Subsearch projection, which looks up the user name in the account, was placed in the left branch of the case differentiation. |
|
Arithmetic – Negation (NEG): Negating numerical values (sign reversal)
The line items of a user-defined entity type 'Account' (see Custom type definition) are used to record incoming and outgoing payments, which are always maintained as positive numerical values in a 'payment value' property (payment.value), regardless of their type. An associated text property 'payment type' (paymentType) is used to further classify the transaction.
By convention, payouts should be identified by key values for the 'payment type' (paymentType) that begin with the letter 'R'.
Using a tuple search that directly addresses the item level (AccountLineItem), the balances for each 'account' instance are to be calculated in such a way that the total of all payments out is deducted from the total of all payments in.
Configuration:
The tuple search uses the two Projections shown on the right:
►NOTE◄ The example uses the NEG calculation function here more by chance in the context of a 'real' aggregation function (SUM). Calculation functions can also be used at any time without an aggregation function and grouping, for example to 'adjust' values in conditions. |
|
Arithmetic – Square root (SQRT): Square root
A tuple search for Shipments should provide an indication of the complexity of a shipment in a COMPLEXITY column, the square root of the number of different values for the 'parent line item', in addition to other key figures.
Configuration:
The Projection shown on the right, in conjunction with a Group by of the search by the 'ID' (id) property of the shipment – not shown here – provides the desired key figure for the 'complexity':
|
|
Runtime example: ID,VOLUME,COMPLEXITY 8455,1,2.0 |
The example data on the left shows the calculated Doublevalues for 'complexity values' of √4, √3, √2 and √1 for the COMPLEXITY column. The square root is used here because doubling the number of 'parent line items' in the hierarchy should not mean twice the complexity. However, quadrupling does. |
Logic – Not (NOT): Logical negation
In a tuple search for Users, the Boolean property 'Active' should be logically reversed so that with the column labeling such as DEACTIVATED, the value $true appears for the Users for which the 'Active' property contains the value $false and vice versa.
Configuration:
The Aggregate shown on the right defines the output column DEACTIVATED by Name:
|
|
►NOTE◄
The 'Active' property for users is 'not nullable' by definition, so the return value of the Aggregate will be $true or $false in all cases.
If the return value of the Projection can also be $null in addition to $true and $false, the Aggregate with the Type 'Not' (NOT) returns the return value $null if the original value is $null.
String processing – Length (LENGTH): Text length as filter condition
In the address property 'Account number' (address.accNumber) for Company accounts, a nine-digit character string is expected by convention.
A search should return a list of all Company accounts for which this convention for the address property 'Account number' is not fulfilled.
Configuration:
Within an OR conjunction, two instances of the Field restriction must be used to identify Company accounts for which the address property 'Account number' (address.accNumber) is either unassigned or not filled in at all:
The left Field restriction evaluates the text property 'Account number' as a check value using Aggregate with the Type 'Length' to check whether the length of an existing character string deviates from the default value 9 (!=).
The right Field restriction is required to cover the case that there is no information at all for the 'Account number'.
►IMPORTANT◄ The right Field restriction formally checks whether the 'Account number' contains an empty string (''). This condition is actually implemented in the database in such a way that it checks whether there is an empty string or 'No value' ($null).
The Aggregate with the Type 'Length' only returns the value 0 in the special case that the address property 'Account number' actually contains an empty string ('') and not 'No value' ($null).
String processing – Upper (UPPER): Grouping users according to the first letter in the user name
A CSV search should return the number of Users who share the same initial letter for the 'username' (username).
No distinction should be made between upper and lower case letters (e.g. {admin,AEINSTEIN}→ A).
Configuration:
Group by's are required as the basis for the use of an Aggregate with the Type 'Count' (see below) for the actual aggregation of user accounts, which can be configured as shown on the right:
|
|
The following two columns are to be output as Projections for the CSV search:
|
|
String processing – Lower (LOWER): Upper/lower case non-specific comparison
A search should list all Users for which there is a match between the address property 'Country' (address.countryCode) and the first two characters of the internal identifier for the 'Locale' (locale).
Configuration:
The Field restriction shown on the right produces the desired adjustment:
|
|
►IMPORTANT◄ An Aggregate with the Type 'Lower' cannot generally be used to process the internal name of enumeration values (here: Country, Locale). This only works here in exceptional cases, as the relevant properties for the user account (locale) or the address (countryCode) are treated as text properties in the database, in contrast to the general standard. In general, properties that refer to Dynamic enumerations contain the 'ordinal' value of the Long type in the database. A Projection on such a property – e.g. the address property 'Salutation' (address.salutation) for a user that refers to the dynamic enumeration Salutation – is then not suitable as an input value for a calculation function for text processing in an Aggregate. When executing the query, an error message usually appears, which refers to the conflict of types.
String processing – Trim (TRIM): Search for 'untrimmed' texts
A search is to determine Addresses for which 'untrimmed' text – i.e. a character string with at least one bordered space – was entered as the 'Name' (name1).
Configuration:
The Where condition shown on the right uses a Field restriction to check whether the text specified as 'Name' (name1) is different from the return value of the trim function for the same property (!=):
►NOTE◄ A search with this Where condition returns all Addresses that can be accessed in the execution context because Address book entries refer to them. Addresses that were created in the context of other entities (e.g. Company accounts or Users) are not included in the search result, as long as they are not accessed via Joins, a Sub search or similar. |
|
Examples of aggregation functions
Typical example: Aggregation of key figures in a search with grouping
A tuple search determines three 'static key figures' for the use of Guest users accounts by different Company accounts (column: COMPANY):
Number of Guest users per company (column: GUEST_ACCOUNTS)
Sum of logins executed by Guest users per company (column: GUEST_LOGINS)
Average login count executed per account per company (column: LOGIN_AVG)
Runtime example:
The example on the right shows the result of a tuple search with the following characteristics:
|
<core:TupleSearchResult maxResults="100" count="3"> |
Configuration:
The data structure for Guest users provides the following characteristics for our statistics:
The 'Company' (companyId) property is a required field for the guest user account, which must be filled with a reference to the 'ID' (id) of exactly one company account (see Company accounts).
The 'Number of logins' (loginCount) property provides the number of logins carried out with a guest user account as an integer.
As our search is intended to aggregate key figures for Guest users per company, Group by's must be defined for the search:
|
|
The screenshot on the right shows an overview of the Projections set up for the tuple search, which define the output columns:
|
|
The GUEST_ACCOUNTS column uses an Aggregate with the Type 'Count' (COUNT) with a Property projection for the 'ID' (id) property of the Guest users to be aggregated. For the 'Count' aggregation type, the property selection is irrelevant as long as it is ensured that it cannot contain 'No value' ($null). Otherwise, Guest users for which the field does not contain a value in the Projection are not counted. Without an entry for the Name parameter, the title of the output column would be 'COUNT of id'. |
|
The GUEST_LOGINS column uses an Aggregate with the Type 'Sum' (SUM) with a Property projection for the 'Number of logins' (loginCount) property of the Guest users to be aggregated. The 'Sum' aggregation type adds up all the numerical values found. Whether the property in the Projection is filled for all aggregated Guest users is irrelevant for the value returned as 'Sum'. Without an input for the Name parameter, the title of the output column would be 'SUM of loginCount'. |
|
The LOGIN_AVG column uses an Aggregate with the Type 'Average' (AVG) with a Property projection for the 'Number of logins' (loginCount) property of the Gastbenutze to be aggregated. The 'Average' aggregation type relates the sum of all values from the Projection to the number of aggregated individual values that are not 'No value' ($null). Since the loginCount property is initialized with the value 0 and cannot contain a $null value on the database side, the interpretation of the mean value in the context is not critical. Without an entry for the Name parameter, the title of the output column would be 'AVG of loginCount'. |
|
Unusual example: Determining key figures for company accounts
A tuple search should determine three 'statistical key figures' for the addresses of Company accounts maintained in the system:
Count of all Company accounts (column: COMPANIES)
Count of all Company accounts in whose address (address) a Country is selected in the 'Country' (countryCode) property (column: WITH_COUNTRY)
Number of different Country values in the 'Country' (countryCode) property in the addresses of companies (column: COUNTRIES)
Runtime example:
The key figures for the evaluation should 'aggregate' all companies for which read access exists in the execution context. This means:
|
<core:TupleSearchResult maxResults="100" count="1"> |
Configuration:
Three Projections of the Aggregate type are required for the output columns of the tuple search:
The COMPANIES column should show the number of all evaluated Company accounts. The Type 'Count' enables this in conjunction with a Property projection that refers to the 'ID' (id) property of the company. The result provides the number of Company accounts for which read access exists, subject to two conditions:
|
|
The WITH_COUNTRY column shows the number of all evaluated Company accounts in whose address field 'Country' (countryCode) a Country is selected. Since the Type 'Count' only takes into account the values from the Projection that are not $null, it uses the Property projection on the path to the property in question (address.countryCode) to return exactly the number searched for, as long as it is guaranteed that none of the other Projections affect a multi-value property (see the following variant). |
|
The COUNTRIES column is intended to indicate the number of different 'Countries' found in the addresses of the evaluated companies. The Type 'Count (distinct )' (COUNT_DISTINCT) enables a Property projection in connection with the address property 'Country' (countryCode), as in the previous column. ►IMPORTANT◄ The condition that no country is specified in the address is not counted. If the 'Count (distinct)' results in the value 1, this does not mean that all Company accounts are located in the same country, but only that no company address refers to a different Country. In contrast to the Type 'Count', the Type 'Count (distinct)' is fundamentally resistant to the scenario that a multi-value projection generates several result rows per company (see following variant). The variety of values in the Projection does not increase if the same values are mentioned repeatedly. |
|
Variant:
Based on the existing configuration, an additional output column (HAS_PARENT) is added which specifies the number of Company accounts whose list field 'parent companies' (parentCompanies) contains at least one entry.
Based on the projection for the WITH_COUNTRY column, the number of companies with filled 'parent companies' (parentCompanies) could be determined as shown on the right.
It is important to understand what happens when you add the projection shown on the right to the existing ones (see above). With the same company data as in the runtime example above, the extended tuple search returns the following result: |
|
<core:TupleSearchResult maxResults="100" count="1"> |
Without a direct comparison to the previous result, the result data makes a thoroughly 'unsuspicious' impression. However, the COMPANIES and WITH_COUNTRY columns now return higher values (57←40 and 45←28) than before the extension, without additional Company accounts having been created or their addresses having been edited. Why? The added Aggregate refers to a multi-value property via Property projection (see also data type Long[] in the screenshot above), which causes a multiplication of the previous result rows for Company accounts with the associated list of Long references for parent companies. |
The aggregation functions process the result of the 'cross product': {companies} x {parent companies} The data linked directly or in a (1:1) relation with the company (here: the property values for id and address.countryCode) are repeated in as many result rows as 'parent companies' are referenced for the company in question.
|
The following configuration for the HAS_PARENT column prevents the multiplication of the result rows before aggregation and ensures that the search returns correct data overall:
CAUTION
If the database system used does not support the use of the Subsearch projection within an aggregation function, an error message is displayed. This is the case for MSSQL, for example (see 'Alternative configuration' below).
Configuration:
An Aggregate with the Type 'Count' should include all Company accounts that have at least one parent company. For this purpose, the Projection must return any value other than 'No value' ($null) if the number of parent companies is >0.
|
|
Alternative Configuration:
Since not every database system accepts the use of a Subsearch projection within an aggregation function, the following configuration is intended to show how the desired results can be achieved without a Subsearch projection in a given application.
In the previous approach, the Subsearch projection was used to avoid multiplying input data ('cross product' {companies} x {parent companies}) for the aggregation function.
The following alternative approach deliberately accepts the multiplication of the input data and compensates for its effect by adjusting the Type and Projection for the aggregation function.
Column |
Previous projection |
Conversion |
Customized projection |
COMPANIES |
|
Previously, the Company accounts could be counted using the Type 'Count' (COUNT) because exactly one line was output per company. The Aggregate are now converted to the Type 'Count (distinct)' (COUNT_DISTINCT), so that each company 'found' by the search (identified by the unique 'ID') is only included once in the count. |
|
WITH_COUNTRY |
|
The previous Aggregate utilized the effect that an aggregation with the Type 'Count' (COUNT) only counts values from the Projection that are not 'No value' ($null). Instead, we now use a Case projection within the Projection to explicitly check whether the address property 'Country' (countryCode) to be evaluated contains a value. Only then should the Case projection transfer the 'ID' (id) of the company return value to the Aggregate. For the Aggregate, the Type is changed to 'Count (distinct)' (COUNT_DISTINCT). This compensates for multiple mentions of the same 'ID' value if the return value is 'multiplied' because the additional projection for the HAS_PARENT column (see below) finds several parent companies. |
|
COUNTRIES |
|
The projection for the COUNTRIES column can be retained unchanged, as the Type 'Count (distinct)' (COUNT_DISTINCT) is already used here. Multiplying the input values for the aggregation does not change the variety of countries. |
unchanged: |
HAS_PARENT |
not yet available |
The projection for the HAS_PARENT column reproduces the concept for the WITH_COUNTRY column:
|
|