Facet JSON Structure¶
Structure¶
The overall structure of filters is as follows. Each facet term combines a data source and constraint spec, and so all combinations of constraint kind and data source path are possible in the syntax.
<FILTERS>: { <logical-operator>: <TERMSET> }
<TERMSET>: '[' <TERM> [, <TERM>]* ']'
<TERM>: { <logical-operator>: <TERMSET> }
or
{ "source": <data-source>, <constraint(s)>, <extra-properties(s)> }
or
{ "sourcekey": <source-key>, <constraint(s)>, <extra-properties(s)> }
In the following sections each of location operators, data source, constraints, and extra properties are explained. You can also find some examples at the end of this document.
Logical operators¶
We want the structure to be as general as possible, so we don’t need to redesign the whole structure when we need to support more complex queries. Therefore as the top level, we have logical operators.
{ "and": [ term... ] }
{ "or": [ term... ]}
{ "not": term }
The current implementation of faceting in Chaise only supports and
. The rest of logical operators are currently not supported.
Source path¶
Source path captures the source of filter. It can either be
- one of current table’s column.
- a column in a table that has a valid foreign key relationship with the current table.
Even if we are faceting on a vocabulary concept and just want the user to pick values by displayed row name and we substitute the actual entity keys in the ERMrest query, we must record this column choice explicitly in the facet spec so that the resulting faceting app URL is unambiguous even if there have been subtle model changes in the interim, which might change the default key selection heuristics etc.
Based on this, we are not supporting filtering on foreign keys with composite keys.
For more information about source path refer to column directive’s documentation in here.
Source key¶
Instead of defining a new source
, you can refer to the sources that are defined in 2019:source-definitions by using sourcekey
attribute. For instance, assuming path_to_table_1
is a valid source definition, you can do
{"sourcekey": "path_to_table_1"}
Fast filter source¶
While defining the source
of a column directive, you must be mindful of the structure of the path and the projected table. This can cause performance issues as ERMrestJS would introduce joins for filtering matching results. If you would like to improve the performance of your queries, you could define an alternative fast_filter_source
that will only be used for filtering. With this alternative source, you can denormalize the tables and create data warehouses that are more optimized for filtering.
- The defined
fast_filter_source
attribute supports the same syntax as source path. - As the name suggests, the new source will only be used for filtering requests and won’t be used for defining a projection. That’s why we’ll only use this property while parsing the
facets
. - ERMrestJS only ensures that the given path exists and won’t do any further checks. It’s your responsibility to ensure the projected columns of both
source
andfast_filter_source
have compatible values. - To make sure ERMrestJS is enabling this feature, you need to set
"aggressive_facet_lookup": true
intable-config
annotation as well.
In the following you can see an example of this feature:
{
"source": [
{"sourcekey": "S_core_fact"},
{"inbound": ["CFDE", "core_fact_assay_type_core_fact_fkey"]},
{"outbound": ["CFDE", "core_fact_assay_type_assay_type_fkey"]},
"nid"
],
"fast_filter_source": [
"assay_types"
]
}
Notes:
- When we want to show the facet panel, we use the
source
for fetching the options. This path is important as it will dictate the entity mode as well as the displayed table, etc. - After users select any of the displayed options in this facet, instead of sending a request will multiple join, we’re going to filter based on the local
assay_types
column. Theassay_types
column has been specifically populated for each row to work with this given path.
Constraints¶
There are three kinds of constraint right now:
- Discrete choice e.g. maps to a checklist or similar UX
- Half-open or closed intervals, e.g. maps to a slider or similar UX
- Substring search, e.g. maps to a search box UX
- Match any record with value (not-null).
Conceptually, this should correspond to three possible syntactic forms:
{"choices": [ value, ... ]}
{"ranges": [ {"min": lower, "max": upper}, ...]}
{"search": [ "box content" ]}
{"not_null": true}
A half-open range might be {"min" lower}
or {"max": upper}
. By default both min
and max
are inclusive. To use exclusive ranges you can use min_exclusive:true
or max_exclusive: true
.