evitaDB - Fast e-commerce database
logo
page-background

Query language

The query language is the core of any database machine. evitaDB has chosen a functional form of the language instead of a SQL-like language, which is more consistent with how it works internally and, most importantly, much more open to transformations.

The evitaDB query language consists of a nested set of functions representing individual "constraints". Each constraint (function) has its name and set of arguments enclosed in brackets constraintName(arguments), argument can be a plain value of supported data type or another constraint. Arguments and constraints are separated by a comma argument1, argument2. Strings are enclosed in
'this is string' or
"this is string".

This language is expected to be used by human operators, at the code level the query is represented by a query object tree, which can be constructed directly without any intermediate string language form (as opposed to the SQL language, which is strictly string typed).

Query has these four parts:
  • header: defines the queried entity collection (it's mandatory unless the filter contains constraints targeting globally unique attributes)
  • filter: defines constraints that limit the entities returned (optional, if missing, all entities in the collection are returned)
  • order: defines the order in which entities are returned (optional, if missing entities are sorted by primary integer key in ascending order)
  • require: contains additional information for the query engine - such as pagination settings, requirements for completeness of returned entities, and requirements for calculation of accompanying data structures (optional, if missing, only primary keys of entities are returned).

Grammar

The grammar of a query is as follows:

Or more complex one:

Any part of the query is optional. Only the collection part is usually mandatory, but there is an exception to this rule. If the filterBy part contains a constraint that targets a globally unique attribute, the collection part can be omitted as well because evitaDB can pick the implicit collection of that globally unique attribute automatically. However, there can be at most one part of each collection, filterBy, orderBy, and require in the query. Any part can be swapped (the order is not important). I.e. the following query is still a valid query and represents the simplest query possible:
... or even this one (although it is recommended to keep the order for better readability:
collection
, filterBy, orderBy, require):

Syntax format

In the documentation, constraints are described by a Syntax section that follows this format:
argument:type,specification
argument represents an argument of a particular type, for example: argument:string represents a string argument at a particular position.
constraint:type,specification
constraint represents an argument of constraint type - the supertype (filter/order/require) of the constraint is always specified before the colon, for example: filterConstraint:any;

after the colon, the exact type of allowed constraint is listed, or the keyword `any' is used if any of the standalone constraints can be used

Variadic arguments

If the argument can be multiple values of the same type (an array type), the specification is appended with a special character:

* (asterisk)
denoting the argument must occur zero, one, or more times (optional multi-value argument).
+ (plus)
denoting the argument must one, or more times (mandatory multi-value argument).
Example of variadic arguments
argument:string+
argument at this position accepts an array of that has to have at least one item
argument:int*
argument at this position accepts an array of and may have zero or multiple items
filterConstraint:any*

argument at this position accepts an array of any standalone filter constraints with zero or more occurrences

Mandatory arguments

Mandatory argument is denoted by ! (exclamation) sign or in case of variadic arguments by a + (plus) sign.
Example of mandatory arguments
argument:string
argument at this position accepts a value, that may be null
argument:int!
argument at this position accepts an value that is mandatory and must be provided

Combined arguments

The specification list might have a combined expression using | for combining multiple specification in logical disjunction meaning (boolean OR) and () signs for aggregation.
Example of combined arguments
filterConstraint:(having|excluding)
either having or excluding, or none, but not both, and no filtering constraint of other type is allowed
filterConstraint:(having|excluding)!
either with or exclude filter constraint, but not both, and not none, but no other filter constraint is allowed
filterConstraint:(having|excluding)*
either having or excluding a filter constraint, or both, or none, but no other filter constraint is allowed.
filterConstraint:(having|excluding)+
either having or excluding a filter constraint, or both, but at least one of them and no filter constraint of other type is allowed

Constraint naming rules

To make constraints more understandable, we have created a set of internal rules for naming constraints:

  1. the name of the entity should be in a form (tense) that matches the English query phrase: query collection ..., and filter entities by ..., and order result by ..., and require ...
    • the query should be understandable to someone who is not familiar with evitaDB's syntax and internal mechanisms.
  2. The constraint name starts with the part of the entity it targets - i.e., entity, attribute, reference - followed by a word that captures the essence of the constraint.
  3. If the constraint only makes sense in the context of some parent constraint, it must not be usable anywhere else, and might relax rule #2 (since the context will be apparent from the parent constraint).

Generic query rules

Data type conversion

If the value to be compared in the constraint argument doesn't match the attribute data type, evitaDB tries to automatically convert it into the correct type before the comparison. Therefore, you can also provide string values for comparison with number types. Of course, it's better to provide evitaDB with the correct types and avoid the automatic conversion.

Array types targeted by the constraint

If the constraint targets an attribute that is of array type, the constraint automatically matches an entity in case any of the attribute array items satisfies it.
For example let's have a array attribute named oneDayDeliveryCountries with the following values: GB, FR, CZ. The filtering constraint attributeEquals worded as follows:
attributeEquals("oneDayDeliveryCountries", "GB")
will match the entity, because the GB is one of the array values.
Let's look at a more complicated, but more useful example. Let's have a DateTimeRange array attribute called validity that contains multiple time periods when the entity can be used:
In short, the entity is only valid in January, June, and December 2023. If we want to know if it's possible to access (e.g. buy a product) in May using the constraint
attributeInRange("validity", "2023-05-05T00:00:00+01:00")
, the result will be empty because none of the validity array ranges matches that date and time. Of course, if we ask for an entity that is valid in June using
attributeInRange("validity", "2023-06-05T00:00:00+01:00")
, the entity will be returned because there is a single date/time range in the array that satisfies this constraint.
Only a collection constraint is allowed in this part of the query.
It defines the entity type that the query will target. It can be omitted if the filterBy contains a constraint that targets a globally unique attribute. This is useful for one of the most important e-commerce scenarios, where the requested URI needs to match one of the existing entities (see the routing chapter for a detailed guide).

Filter by

Filtering constraints allow you to select only a few entities from many that exist in the target collection. It's similar to the "where" clause in SQL. Currently, these filtering constraints are available for use.

Logical constraints

Logical constraints are used to perform logical operations on the products of child functions:

Constant constraints

Constant constraints directly specify the entity primary keys that are expected on the output.

Localization constraints

Localization constraints allow you to narrow down the localized attributes to a single , which is used to pick the correct values for comparison in other filter constraints that target those attributes:

Comparable constraints

Comparable constraints compare the constants passed as arguments with a specific attribute of an entity, and then filter the resulting output to only include values that satisfy the constraint.

String constraints

String constraints are similar to Comparable, but operate only on the attribute datatype and allow operations specific to it:

Range constraints

String constraints are similar to Comparable, but operate only on the
attribute datatype and allow operations specific to it:

Price constraints

Price constraints allow filtering entities by matching a price they posses:

Reference constraints

Reference constraints allow filtering of entities by existence of reference attributes specified on their references/relationships to other entities, or a filtering constraint on the referenced entity itself:

Hierarchy constraints

Hierarchy constraints take advantage of references to a hierarchical set of entities (forming a tree) and allow filtering of entities by the fact that they refer to a particular part of the tree:

Special constraints

Special constraints are used only for the definition of a filter constraint scope, which has a different treatment in calculations:

Order by

Order constraints allow you to define a rule that controls the order of entities in the response. It's similar to the "order by" clause in SQL. Currently, these ordering constraints are available for use:

Require

Requirements have no direct parallel in other database languages. They define sideway calculations, paging, the amount of data fetched for each returned entity, and so on, but never affect the number or order of returned entities. Currently, these requirements are available to you:

Paging

Paging requirements control how large and which subset of the large filtered entity set is actually returned in the output.

Fetching (completeness)

Fetching requirements control the completeness of the returned entities.
By default, only a
is returned in query response.
In order an entity body is returned, some of the following requirements needs to be part of it:

Hierarchy

Hierarchy requirements trigger the calculation of additional data structure that can be used to render a menu that organizes the entities into a more understandable tree-like categorization:

Facets

Facet requirements trigger the computation of an additional data structure that lists all entity faceted references, organized into a group with a calculated count of all entities that match each respective facet. Alternatively, the summary could include a calculation of how many entities will be left when that particular facet is added to the filter:

Histogram

Histogram requests trigger the calculation of an additional data structure that contains a histogram of entities aggregated by their numeric value in a particular attribute or by their sales price:

Price

The price requirement controls which form of price for sale is taken into account when entities are filtered, ordered, or their histograms are calculated:

Telemetry

The telemetry requirements trigger the calculation of additional telemetry data for looking under the hood of the database engine:

Author: Ing. Jan Novotný

Date updated: 15.12.2022

Documentation Source