Hierarchy filtering allows you to query tree-oriented structures or items that refer to a node in that structure.
In e-commerce projects, the hierarchy structure is represented by a category tree and the items that refer to it are
usually products or some kind of "inventory". This functionality is closely related to menu traversal and listing
the items relevant to the currently viewed category.
Hierarchy filtering can be applied only to entities marked as hierarchical
or to entities that reference these hierarchical entities. Hierarchy filtering
allows filtering all direct or transitive children of a given hierarchy node, or entities that are directly or
transitively related to the requested hierarchy node or its children. Filtering allows to exclude (hide) several parts
of the tree from evaluation, which can be useful in situation when part of the store should be (temporarily) hidden
from (some of) clients.
In addition to filtering, there are query requirement extensions that allow you to
compute data to help render (dynamic or static) menus that describe the hierarchy context you request in the query.
The typical use-cases related to hierarchy constraints:
There could be at most one single hierarchyWithin or hierarchyRoot filter constraint in the entire query.
Hierarchy within
The constraint
allows you to restrict the search to only those entities that are part of the hierarchy tree starting with the root
node identified by the first argument of this constraint. In e-commerce systems the typical representative of
a hierarchical entity is a category, which will be used in all of our examples. The examples in this chapter will
focus on the category Accessories in our demo dataset with following layout:
Accessories category listing
Self
filterConstraint:any!
a single mandatory filter constraint that identifies one or more hierarchy nodes that act as hierarchy roots;
multiple constraints must be enclosed in AND / OR containers
The most straightforward usage is filtering the hierarchical entities themselves.
To list all nested categories of Accessories category issue this query:
... and you should get slightly more than one page of categories in the response.
The first argument specifies the filter targets the attributes of the Category entity. In this example we used
attributeEquals for unique attribute code, but you can select the category
by localized url attribute (but then you need to provide also entityLocaleEquals
constraint for determining the proper language), or using entityPrimaryKeyInSet
and passing category primary key.
Yes, it can. Although, it's apparently one of the edge cases, it's possible. This query:
... will return all subcategories of the Wireless headphones and Wired headphones and their subcategories:
Accessories category listing
Referenced entity
argument:string!
a mandatory name of the queried entity reference schema that represents
the relationship to the hierarchical entity type, your entity may target different hierarchical entities in
different reference types, or it may target the same hierarchical entity through multiple semantically different
references, and that is why the reference name is used instead of the target entity type.
filterConstraint:any!
a single mandatory filter constraint that identifies one or more hierarchy nodes that act as hierarchy roots;
multiple constraints must be enclosed in AND / OR containers
The hierarchyWithin constraint can also be used for entities that directly reference a hierarchical entity type.
The most common use case from the e-commerce world is a product that is assigned to one or more categories. To list all
products in the Accessories category of our demo dataset, we issue the following query:
Products assigned to two or more subcategories of Accessories category will only appear once in the response (contrary
to what you might expect if you have experience with SQL).
The query returns the first page of a total of 26 pages of items.
The category filter constraint specifies a condition that targets the referenced entity (i.e., category attributes,
category references). Currently, it's not possible to specify a filter constraint that takes into account the product
reference that leads to its category. An issue #105 is planned to
address this shortcoming.
Hierarchy within root
The constraint
allows you to restrict the search to only those entities that are part of the entire hierarchy tree. In e-commerce
systems the typical representative of a hierarchical entity is a category, which will be used in all of our examples.
The single difference to hierarchyWithin constraint is that it doesn't accept a root node
specification. Because evitaDB accepts multiple root nodes in your entity hierarchy, it may be helpful to imagine
there is an invisible "virtual" top root above all the top nodes (whose parent property remains NULL) you have in
your entity hierarchy and this virtual top root is targeted by this constraint.
The hierarchyWithinRoot, which targets the Category collection itself, returns all categories except those that
would point to non-existent parent nodes, such hierarchy nodes are called orphans
and do not satisfy any hierarchy query.
The query returns the first page of a total of 2 pages of items.
Referenced entity
argument:string!
a mandatory name of the queried entity reference schema that represents
the relationship to the hierarchical entity type, your entity may target different hierarchical entities in
different reference types, or it may target the same hierarchical entity through multiple semantically different
references, and that is why the reference name is used instead of the target entity type.
filterConstraint:(having|excluding)*
optional constraints allow you to narrow the scope of the hierarchy;
none or all of the constraints may be present:
The hierarchyWithinRoot constraint can also be used for entities that directly reference a hierarchical entity type.
The most common use case from the e-commerce world is a product that is assigned to one or more categories. To list all
products assigned to any category of our demo dataset, we issue the following query:
Products assigned to only one orphan category will be missing from
the result. Products assigned to two or more categories will only appear once in the response (contrary to what you
might expect if you have experience with SQL).
The query returns the first page of a total of 212 pages of items:
Direct relation
The constraint
is a constraint that can only be used within hierarchyWithin or hierarchyWithinRoot parent constraints. It simply
makes no sense anywhere else because it changes the default behavior of those constraints. Hierarchy constraints return
all hierarchy children of the parent node or entities that are transitively or directly related to them and the parent
node itself. If the directRelation is used as a sub-constraint, this behavior changes and only direct descendants or
directly referencing entities are matched.
Self
If the hierarchy constraint targets the hierarchy entity, the directRelation will cause only the children of a direct
parent node to be returned. In the case of the hierarchyWithinRoot constraint, the parent is an invisible "virtual"
top root - so only the top-level categories are returned.
In the case of the hierarchyWithin the result will contain direct children of the filtered category (or categories).
Referenced entity
If the hierarchy constraint targets a non-hierarchical entity that references the hierarchical one (typical example is
a product assigned to a category), it can only be used in the hierarchyWithin parent constraint.
In the case of hierarchyWithinRoot, the directRelation constraint makes no sense because no entity can be assigned
to a "virtual" top parent root.
So we can only list products that are directly related to a certain category - if we try to list products that have
Accessories category assigned:
... we get an empty result. There are no products directly assigned to the Accessories category, they all refer to
some of its subcategories. Let's try the Smartwatches subcategory:
... and we get the list of all products related directly to a Smartwatches category.
Excluding root
The constraint
is a constraint that can only be used within hierarchyWithin or hierarchyWithinRoot parent constraints. It simply
makes no sense anywhere else because it changes the default behavior of those constraints. Hierarchy constraints return
all hierarchy children of the parent node or entities that are transitively or directly related to them and the parent
node itself. When the excludingRoot is used as a sub-constraint, this behavior changes and the parent node itself or the
entities directly related to that parent node are be excluded from the result.
Self
If the hierarchy constraint targets the hierarchy entity, the excludingRoot will omit the requested parent node from
the result. In the case of the hierarchyWithinRoot constraint, the parent is an invisible "virtual" top root, and this
constraint makes no sense.
As we can see the requested parent category Accessories is excluded from the result.
Referenced entity
If the hierarchy constraint targets a non-hierarchical entity that references the hierarchical one (typical example is
a product assigned to a category), the excludingRoot constraint can only be used in the hierarchyWithin parent
constraint.
In the case of hierarchyWithinRoot, the excludingRoot constraint makes no sense because no entity can be assigned
to a "virtual" top parent root.
Because we learned that Accessories category has no directly assigned products, the excludingRoot constraint presence
would not affect the query result. Therefore, we choose Keyboard category for our example. When we list all products
in Keyboard category using hierarchyWithin constraint, we obtain 20 items. When the excludingRoot constraint
is used:
... we get only 4 items, which means that 16 were assigned directly to Keyboards category and only 4 of them were
assigned to Exotic keyboards:
Having
The constraint
is a constraint that can only be used within hierarchyWithin or hierarchyWithinRoot parent constraints. It simply
makes no sense anywhere else because it changes the default behavior of those constraints. Hierarchy constraints return
all hierarchy children of the parent node or entities that are transitively or directly related to them, and the parent
node itself.
The having constraint allows you to set a constraint that must be fulfilled by all categories in the category scope
in order to be accepted by hierarchy within filter. This constraint is especially useful if you want to conditionally
display certain parts of the tree. Imagine you have a category Christmas Sale that should only be available during
a certain period of the year, or a category B2B Partners that should only be accessible to a certain role of users.
All of these scenarios can take advantage of the having constraint (but there are other approaches to solving the
above use cases).
The lookup stops at the first node that doesn't satisfy the constraint!
The hierarchical query traverses from the root nodes to the leaf nodes. For each of the nodes, the engine checks whether
the having constraint is still valid, and if not, it excludes that hierarchy node and all of its child nodes (entire
subtree).
filterConstraint:+
one or more mandatory constraints that must be satisfied by all returned hierarchy nodes and that mark
the visible part of the tree, the implicit relation between constraints is logical conjunction (boolean AND)
Self
When the hierarchy constraint targets the hierarchy entity, the children that don't satisfy the inner constraints (and
their children, whether they satisfy them or not) are excluded from the result.
For demonstration purposes, let's list all categories within the Accessories category, but only those that are valid
at 01:00 AM on October 1, 2023.
Accessories category listing with validity constraint
Because the category Christmas electronics has its validity set to be valid only between December 1st and December
24th, it will be omitted from the result. If it had subcategories, they would also be omitted (even if they had no
validity restrictions).
Referenced entity
If the hierarchy constraint targets a non-hierarchical entity that references the hierarchical one (typical example is
a product assigned to a category), the having constraint is evaluated against the hierarchical entity (category), but
affects the queried non-hierarchical entities (products). It excludes all products referencing categories that don't
satisfy the having inner constraints.
Let's use again our example with Christmas electronics that is valid only between 1st and 24th December. To list all
products available at 01:00 AM on October 1, 2023, issue a following query:
You can see that Christmas products like Retlux Blue christmas lightning, Retlux Warm white christmas lightning or
Emos Candlestick are not present in the listing.
When you change the date and time in range constraint for validity attribute to 2nd December:
... you will see all those products in Christmas electronics category.
In the situation where the single product, let's say Garmin Vivosmart 5, is in both the excluded category Christmas
Electronics and the included category Smartwatches, as on the following schematics:
Accessories category listing with validity constraint
... it will remain in the query result because there is at least one product reference that is part of the visible part
of the tree.
Excluding
The constraint
is a constraint that can only be used within hierarchyWithin or hierarchyWithinRoot parent constraints. It simply
makes no sense anywhere else because it changes the default behavior of those constraints. Hierarchy constraints return
all hierarchy children of the parent node or entities that are transitively or directly related to them, and the parent
node itself.
The excluding constraint allows you to exclude one or more subtrees from the scope of the filter. This constraint is
the exact opposite of the having constraint. If the constraint is true for a hierarchy entity, it and all
of its children are excluded from the query. The excluding constraint is the same as declaring
having(not(expression)), but for the sake of readability it has its own constraint.
The lookup stops at the first node that satisfies the constraint!
The hierarchical query traverses from the root nodes to the leaf nodes. For each of the nodes, the engine checks whether
the excluding constraint is satisfied valid, and if so, it excludes that hierarchy node and all of its child nodes
(entire subtree).
filterConstraint:+
one or more mandatory constraints that must be satisfied by all returned hierarchy nodes and that mark
the visible part of the tree, the implicit relation between constraints is logical conjunction (boolean AND)
Self
When the hierarchy constraint targets the hierarchy entity, the children that satisfy the inner constraints (and
their children, whether they satisfy them or not) are excluded from the result.
For demonstration purposes, let's list all categories within the Accessories category, but exclude exactly
the Wireless headphones subcategory.
The category Wireless Headphones and all its subcategories will not be shown in the results list.
Referenced entity
If the hierarchy constraint targets a non-hierarchical entity that references the hierarchical one (typical example is
a product assigned to a category), the excluding constraint is evaluated against the hierarchical entity (category),
but affects the queried non-hierarchical entities (products). It excludes all products referencing categories that
satisfy the excluding inner constraints.
Let's go back to our example query that excludes the Wireless Headphones category subtree. To list all products
available in the Accessories category except those related to the Wireless Headphones category or its subcategories,
issue the following query:
You can see that wireless headphone products like Huawei FreeBuds 4, Jabra Elite 3 or Adidas FWD-02 Sport are not
present in the listing.
When the product is assigned to two categories - one excluded and one part of the visible category tree, the product
remains in the result. See the example.