Advanced filter options
Dynamic filter values
Besides static filter values, ADITO allows the client user to specify also dynamic filter values. These are retrieved via a Provider, whose property filterVariablesProcess includes the core code of the feature:
var res = {
// key : display value
"$xxx.yyy.myId": translate.text("${FILTER_DYNAMIC_VALUE_xxx}")
};
result.object(res);
See examples.
Filter presets
Besides filters set by the client user, it is also possible to include customized filter presets, e.g., to be automatically executed when a Context is opened.
FilterBuilder
JDito includes the FilterBuilder, which is a builder pattern that allows you to define/configure even complex filters quite comfortably and intuitively. (In earlier ADITO ersions you had to realize this via a big JSON string, created manually.)
FilterGroup and FilterCondition
To use this functionality, you first need to import the system module neonFilter. It includes the following methods:
| Name | Description |
|---|---|
.createFilterGroup | Creates an object of class FilterGroup |
.createFilterCondition | Creates an object of class FilterCondition |
You can create multiple instances of these objects. Each can be configured in detail and then nested - which finally results in an (extended) filter.
Creating a FilterGroup is always the starting point. "Into" this FilterGroup, you can add ("nest")
- either a
FilterCondition - or another
FilterGroup, which in turn can haveFilterGroupsorFilterConditionsadded ("nested")
This "nesting" of FilterGroups and other FilterGroups or FilterConditions can, in theory, be continued up to an unlimited depth (with limits as for performance, of course).
Methods of FilterGroup:
| Name | Description |
|---|---|
.mergeOperator | The operator (mathematical term: "logical connective") to be applied for connecting the filter group's conditions and subordinated filter groups. Possible values: "and", "or". The parameter should be specified by using the respective constants given in neonFilter, e.g., neonFilter.MERGE_OPERATOR_AND. |
.addFilterGroup | "Nests" one filter group "into" another. The subordinated filter group's result will be connected with the filter conditions using the specified mergeOperator (see above). |
.addFilterCondition | Specifies a filter condition to be applied in this filter group. This filter condition will be connected with further filter conditions as well as with subordinated filter groups' results using the specified mergeOperator (see above). |
.toJson() | Converts the filter group and all its nested content into a JSON string. In earlier ADITO versions, this was required for passing the filter to specific JDito methods that did not yet accept the filter object as argument, but only a JSON string. |
Methods of FilterCondition:
| Name | Description |
|---|---|
.field | The EntityField the filter condition refers to |
.contentType | The contentType of the EntityField. The parameter should be specified by using the respective constants given in neonFilter, e.g., neonFilter.CONTENT_TYPE_TEXT. |
.searchOperator | The EntityField the filter condition refers to |
.field | The search operator (relational operator) to be applied for the comparison between the values of the EntityField and the value given in method .key. Examples: "greater than", "equals", "starts with". The parameter should be specified by using the respective constants given in neonFilter, e.g., neonFilter.SEARCH_OPERATOR_EQUAL. |
.key | The value to compare the EntityField's values with when the filter is executed. This value is NOT displayed in the client. |
.value | The value to be displayed in the client when the filter has been set (NOT used for filtering, only for displaying purposes!) If there is no difference between the values of .key and .value, then .value must be specified anyway - simply repeating the value of .key |
See examples.
initFilterProcess
If you want a specific filter to be preset when a Context is opened (i.e., when calling the FilterView), the most common way is to use the Entity's initFilterProcess. The required result can easily be configured using the FilterBuilder (see previous chapter).
See examples.
Filter referencing a FilterExtension
If your filter condition is related to a FilterExtension, the syntax of the argument of method field is like this:
var filterCondition = neonFilter.createFilterCondition()
.field("#EXTENSION.TestfilterExtension.TestfilterExtension#TEXT")
neon.setFilter
If you want a specific filter to be applied in another part of your project - e.g., in an Action - you need to use method neon.setFilter:
var filter = neonFilter.createFilterGroup();
(...) // configuration of filter - cf. above examples
neon.setFilter("#ENTITY", filter);
Pattern for setting a filter in JDito
FilterExtension
FilterExtension is an ADITO model used for extending the standard filter options (which are controlled via the "isFilterable" properties of the RecordFieldMappings in the RecordContainer) by an additional filter option - e.g., a filter criteria that is related to a field of another Entity.
Examples in ADITO xRM:
Favorites_filter, a FilterExtension included in the RecordContainers of several Entities, e.g., of Organisation_entity.Phase_filter(Phase_filterExtention), a FilterExtension included in the RecordContainer of Salesproject_entity.
To understand these examples, you might first study the following paragraphs.
General example
Generally, a FilterExtension can be added as follows:
Creating a new FilterExtension
Open your Entity in the Navigator, right-click on its RecordContainer, and choose "Add Filter Extension" from the context menu. Enter a name of your choice, e.g., "hasMyEntityFieldSet".
Now, fill the new FilterExtension's properties as follows:
General properties
title: The text of the respective list item to appear in the filter's combo box "Property", e.g. "Has my EntityField set".
contentType: The data type of the values to be entered or selected in the filter's field "Value". This data type will then, amongst others, determine the list of relational operators from which the user can select in the filter component's field "Operator". For our example, the contentType should be set to "TEXT".
filterValuesProcess
Fill property filterValuesProcess only if you want to select the values (in the filter's field "Value") via a combo box. The result of this property's process must be an array of value pairs:
var myFilterValues = [];
myFilterValues.push(["myID1", "my combobox ListItem1"]);
myFilterValues.push(["myID2", "my combobox ListItem2"]);
myFilterValues.push(["myID3", "my combobox ListItem3"]);
(...)
result.object(myFilterValues);
Example of data structure of filterValuesProcess result
The second values of the value pairs are displayed in the combo box, while the first values are to evaluate the selection in the filterConditionProcess (see below); often this first value is a key (e.g., the UID of a dataset).
In many cases, the above result array is generated via an SQL selection:
var myFilterValues = newSelect("MYIDCOLUMN, MYCOLUMNFORCOMBOBOXLISTITEMS")
.from("MYTABLE")
.table();
result.object(myFilterValues);
Example of filterValuesProcess using an SQL selection
📝 Note: If, however, you want to enter the value as free text (no selection via combo box), then property filterValuesProcess must remain in default status (empty).
useConsumer/consumer
Checking property useConsumer allows you to use lookup functionality via a Consumer. It is an alternative to specifying a filterValuesProcess: As soon as you have checked this checkbox, property filterValuesProcess disappears and property consumer appears instead. Here, you can select a Consumer that delivers the selectable filter values, with the corresponding Provider Entity's
contentTitleProcessor (if set) LookupView to be used for displaying the selectable values in the combo box;- UID column (= its primary key) acting as value to be evaluated in the
filterConditionProcess(see chapter below)
If you uncheck property useConsumer again, property filterValuesProcess (and its value) will reappear instead of property "consumer". The values of these alternative properties will always remain stored, independently from the value of property useConsumer.
filterConditionProcess
The property filterConditionProcess is used for reacting to the value the user has input/selected in the "Value" field of the filter component. The result of this process is an SQL condition that will be added to the conditionProcess of the RecordContainer (i.e., to the "WHERE" part of the SQL code used for filling the FilterView). Like in the conditionProcess, the SQL code words "WHERE" and "AND" must not be included.
The following variables are available in the filterConditionProcess:
| Name | Description |
|---|---|
$local.rawvalue | the text typed in by the user (if the input was done via a free text field) or the first value of the value pair corresponding to the user's selection (if the input was done via a combo box) |
$local.comparison | a String value representation of the selected operator, e.g. "EQUAL", "CONTAINS", or "STARTSWITH" |
$local.operator | an Integer value representation, deduced from the selected operator. Some operator selections result in equal Integer values. Experienced users can use this value to simplify the evaluation of the selected operator, especially in SQL statements. |
$local.operator2 | The relational operator as special character, mainly to be used in SQL statements, e.g. ">". |
See examples.
groupQueryProcess
groupQueryProcess: This property is an option to group data provided via a FilterExtension. It requires no specific EntityField, because the grouping is created in the process itself. The result of thegroupQueryProcessis an SQL string that returns the grouping.
The groupQueryProcess is triggered when the client user selects a FilterExtension-related "Group-by" value in the "Grouping" section of the filter component of the FilterView.
The grouping will only work if a filterConditionProcess is defined.
See examples.
supportsFilterExtensionGrouping
It is possible to use FilterExtensions and FilterExtensionSets (see chapter FilterExtensionSet) on groups with RecordContainers without paging. If the property "isPageable" is disabled on a RecordContainer, the additional property supportsFilterExtensionGrouping is shown. If this property is set to true, the FilterExtensions are also shown when using grouping.
⚠️ Important: This has to be used with care, because it leads to all data being reloaded for every row of grouped data. The mechanism may cause a huge amount of data being loaded and most likely will negatively affect the system's performance!
Filter extensions and index
Filter extensions are not automatically respected by the index. This means, if, e.g., you want to use a FilterExtension like "Supervisor assignment equals YES" in the access rights, you need to re-build this filter in the index. If you do not do this, then, in the index, there will be shown no result for the respectiv IndexGroup. Find more information in the ADITO document AID093_Indexsearch.pdf.
FilterExtensionSet
💡 Tip: You will understand the content of this chapter better, if you first read the previous chapter FilterExtension.
FilterExtensionSet is an ADITO model used for extending the standard filter (controlled via the "isFilterable" properties of the record fields in the RecordContainer) by additional filter options, partly similar to the FilterExtension, but more complex and powerful.
See examples.
Consumer filter
A Consumer filter makes filter criteria of a dependent Entity available. This enhances the range of filtering options.
See examples.
EntityRecordsRecipe
As the name suggests, EntityRecordsRecipe is a definition ("recipe") of datasets (records) of a specific Entity. It can be built and configured quite intuitively, with several options, e.g., to specify a filter (via a FilterGroup object, see chapter FilterBuilder) or a list (as array) of UIDs of records to be excluded.
You can consider EntityRecordsRecipe to be a kind of extended filter that can be applied, e.g., when a Context is opened, or when records are loaded via "LoadEntity".
Technical background
An EntityRecordsRecipe does NOT hold the records (datasets) theirselves, but it defines them - like a recipe defines the ingredients required to cook a meal. Basically, it's a filter, whose result can optionally be further reduced by a set of "UIDs to be excluded".
Formerly, this definition could exclusively be done via a set of the single UIDs of all corresponding records. This principle
- did not scale very well, as additional or changed records always meant that the set of UIDs had to be adjusted
- could cause performance and memory issues, if the Entity held millions of records, causing a huge set of UIDs.
A scalable and well-performing solution
Therefore, EntityRecordsRecipe was introduced, providing a scalable and well-performing solution for defining an entirety of records without having to indicate every single UID. An example in the client showing the benefit of this approach is the "select all" button of a table: If you check it and subsequently uncheck 3 single records, an EntityRecordsRecipe is being built defining "all datasets without UIDs xxx, yyy, and zzz". (This EntityRecordsRecipe is available in variable "$sys.selectionsRecordsRecipe" - see chapter further below.)
General usage
The ADITO system uses EntityRecordsRecipe internally, e.g., to hold and process a definition of the records selected by the client user - see chapter on variable "$sys.selectionsRecordsRecipe" further below.
If you, however, want to create a new EntityRecordsRecipe, then proceed as follows.
How to create a new EntityRecordsRecipe?
The first step is to create a builder object:
var entityRecordsRecipeBuilder = neonFilter.createEntityRecordsRecipeBuilder();
This object has several methods, allowing to specify different options. Each method returns the builder object itself, so a "chaining" of methods is possible - similar to "LoadEntity" or "SqlBuilder". Technically, the conditions resulting from these methods are combined with a logical "AND".
Various methods available
The most common methods to call are:
| Name | Description |
|---|---|
.entity(<Name of Entity>) | Definition of the Entity, given as its name String, e.g., .entity("Person_entity"). If this is the only method you call, then the definition is "ALL records of the Entity". In SQL terms, the effect of this method is "... FROM joined database tables of the Entity's RecordContainer". |
.filter(<filter>) | Filter that restricts the record definition to specific conditions. The filter can be given as FilterGroup object (see chapter FilterBuilder) or as JSON String. In SQL terms, the effect of this method is "... WHERE filter_conditions". |
.uidsExcludelist(<Array of UIDs>) | Specifies a list of UIDs of all records that are to be excluded from the remaining records. In SQL terms, the effect of this method is "... WHERE xxxID NOT IN ('UID1', 'UID2', ...)". |
.uidsIncludelist(<Array of UIDs>) | Specifies a list of UIDs of records to which the definition is to be restricted. In other words: This is the "maximum" of records - which can be further reduced by the conditions given in the other methods (.uidsExcludelist or .filter). This list does NOT "add" additional records to those specified in .filter, but it defines that the UIDs of the records described by the EntityRecordsRecipe must be included in this list of UIDs. Or, in SQL terms, the effect of this method is a condition like "... where MYTABLEID in ('UID1', 'UID2', ...) Note: If the argument of method uidsIncludelist is an empty Array, then nothing is loaded subsequently; if it is null, then there is not any UID-related restriction at all (= same as if this setter method was not executed at all). |
See examples.
$sys.selectionsRecordsRecipe
The system variable "$sys.selectionsRecordsRecipe" holds an EntityRecordsRecipe describing the Entity's name and the records selected by the client user.
The ADITO system automatically reacts to changes of the value of "$sys.selectionRecordsRecipe" - i.e., subsequent processes will be executed, e.g., the titleProcess of an Action.
The content of an EntityRecordsRecipe depends on the value of the Entity's property "recordsRecipeSupported". If this property's value is
- false, then the description of the selected records is exclusively done via "uidsIncludelist" - while the variables "$sys.selections" and "$sys.selectionRows" are still working. This allows you to write and apply processes already using EntityRecordsRecipe even for Entitys that do not yet support it.
- true, then also EntityRecordsRecipe's methods "filter" and "uidsExcludelist" might be used - depending on the situation ("select all" button checked or not, filter defined or not, etc.). For most Entitys, this state should be the standard.
❗ Warning: If property recordsRecipeSupported is set to true, then variables "$sys.selections" and "$sys.selectionRows" will be deactivated (holding "null"). This is intended, to draw your attention to those parts of your code that have not yet been transferred to support of EntityRecordsRecipe. Please refer to the corresponding chapter in the Update Manual, which is available in the customer area of the ADITO web site.
(The above principle is also used for "$field.consumer.selectionRecordsRecipe".)
Context filter (content search)
Several ViewTemplates provide the option to display a so-called Context filter. Its visual expression is the horizontal content search bar that is shown in the upper part of the ViewTemplate.
Example:
Example: The Context filter of Context KeywordEntry
->
Example: Filter criteria "fax"
Via the Context filter, you can enter filter terms that are then processed in different ways, depending on the ViewTemplate's type and some specific settings. This chapter gives an overview and further details.
Availability
Specific ViewTemplate types are able to show and process a Context filter, if
. their property hideContentSearch is set to false and
. the corresponding RecordFieldMappings' (e.g., NAME.value) property isLookupFilter is set to true.
These are the following ViewTemplateTypes:
- BreadCrumbTreeTable: This type is not yet available via "Add View Template...", but it is automatically used on devices MOBILE and TABLET, if a Tree or TreeTable type is used, and property
useBreadCrumbsis set to true. - CardTable
- DynamicMultiDataChart
- DynamicSingleDataChart
- MultiEditTable
- ResourceTimeline
- Table
- Tiles
- Timeline
- Tree
- TreeTable
Usage in "DefaultLookup_view"
Furthermore, this filter/search feature is also available in the ADITO xRM project's View "DefaultLookup_view" (of Context "Default_context"), which has a ViewTemplate of type "List". Here, you can configure the lookup component that is to be used, if no specific lookupView is set in a Context. As the ListViewTemplate is not yet available via "Add View Template...", it needs to be configured in the source code of DefaultLookup_view:
.Source code of "DefaultLookup_view", including its ListViewTemplate "DefaultList"
<?xml version="1.0" encoding="UTF-8"?>
<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.3" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.2.3">
<name>DefaultLookup_view</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<layout>
<noneLayout />
</layout>
<children>
<listViewTemplate>
<name>DefaultList</name>
<entityField>#ENTITY</entityField>
</listViewTemplate>
</children>
</neonView>
Evaluation
The term(s) entered in the Context filter are evaluated by different instances in different ways, depending on the ViewTemplate type and the RecordContainer's property settings:
- The ViewTemplate component performs the filtering, if the RecordContainer's properties
isPageableandisRequireContainerFilteringare both set to false.- ResourceTimeline: Checks, if at least one of the strings of the filter value is included in the title (logical "OR" - unlike all other cases).
- BreadCrumbTreeTable (see chapter Availability), Tiles, Tree, and TreeTable: Checks, if the columns of a row include all filter terms.
- DynamicMultiDataChart and DynamicSingleDataChart: no filtering
- The RecordContainer performs the filtering, if it is filterable, i.e., if property
isRequireContainerFilteringis set to true. Then, the filter checks, if the columns of a row include all filter terms. If, additionally, a filter is set in the FilterView's filter component (right hand side of the FilterView), then a combined filter is constructed, joined with a logical "AND".- JDitoRecordContainer, IndexRecordContainer, DBRecordContainer: Filter will be set, and the data will be loaded anew.
- DatalessRecordContainer or no RecordContainer: no filtering