There are two big types of relations in Episerver (Optimizely) B2C Commerce: relations between entries and nodes, and between nodes. As you will most likely have to touch one or another, or both, sooner or later, this post aims to give you some understanding on how they are structured/work.
Node-Entry relation
When you add a product (or variant, or package, or bundle) to a category, you are creating a NodeEntryRelation
. And there are a two types of NodeEntryRelation
- Primary
NodeEntryRelation
, which means the category is counted as true parent of the entry. Each entry can only have at most one primaryNodeEntryRelation
(Which means it can have no primaryNodeEntryRelation
at all). - Secondary
NodeEntryRelation
, which means the entry is linked to the category. You do that when it makes sense for the product to be in additional categories. for example, a GoPro can be in Camera (primary category), but can also be in Sport Gears (linked). An entry can have no, or multiple secondaryNodeEntryRelation
.
The concept of primary NodeEntryRelation
was added to Commerce 11. Before that, it’s a bit more of a guess work – the “main” category is determined by the sort order – the relation with lowest sort order is considered “main relation”. That introduces some inconsistency, which prompted the rework on that.
What is the main different between those two things? For one thing, access rights. For Commerce, you can set access rights down to categories, but not entries. The entries will inherit access rights from their true parents (i.e. primary nodes). An entry without primary node-entry relation is considered the direct children of a catalog, and inherits its access right settings.
Another smaller difference is that if you delete a category, true children entries will be deleted. Linked entries will only be detached from that category.
NodeEntryRelation
can be managed fully by IRelationRepository
, using the NodeEntryRelation
type, and you can use a few extension methods to make it easier – for example EntryContentBase.GetCategories()
.
How are your actions in Catalog UI reflected on a data level:
- When you create a new entry (product/SKU/etc.) in a category, you create a primary node-entry relation for them
- When you move (cut then paste) an entry to a new category, you are creating a new primary node-entry relation. If the entry already has a primary node-entry relation, the new one will take over.
- When you link/detach an entry to/from a new category, you are creating/removing a non-primary node-entry relation
Node-Node Relation
Like Node-Entry relation, a node can be a true parent of a node, or just be a “linked” parent.
Unlike Node-Entry relation, Node-Node relation is quite different that it’s separated in two places.
- Linked nodes are represented by
NodeRelation(s)
(it might be interesting to know thatNodeRelation
is the parent class ofNodeEntryRelation
. The interpretation is that aNodeRelation
is – a relation of a node, which can be with another node, or an entry) - There is no primary
Node
Relation, the true parent node is identified by a property of the category itself. When you have aNodeContent
content, then theParentLink
property points to the true parent.
For that reason, a node will always have a true parent, either a catalog, or a node. You can’t use IRelationRepository
(and therefore, ServiceAPI) to manage (delete or update) a true parent of a node , you would have to:
- Set its
ParentLink
to something else - Use
IContentRepository.Move
to move it to a new parent.
Note that this is the limitation of the Relations API in ServiceAPI. You can technically change the
ParentLink
of a node and update via POST. It’s just more work and not as intuitive as the Relations API.
Why the disparity, you might ask? Well, a lot of design decisions in Commerce comes from historical reasons, and after that, constrained resources (time/man power) and priority. While the disparity is probably not the best thing you want, it still works fairly well, and if you understand the quirk then it is all well.