Skip to content

Hierarchies

Who this guide is for

Supply chain planners, inventory managers, and system administrators who need to organise items and locations into navigable tree structures for filtering, grouping, and aggregation across the Mirabelle dashboard and downstream engines.


Table of Contents


Overview

A hierarchy in Mirabelle is a user-defined tree structure that organises your master data — items or sites — into levels for aggregation and navigation. Typical examples:

  • Product hierarchy: Department → Category → Subcategory → Item
  • Location hierarchy: Country → Region → Site

Once defined, hierarchies power three capabilities across the application:

Capability How hierarchies help
Dashboard filtering Select a node in the tree to filter every tab and the series table to only that branch
Report grouping Group KPIs and charts by hierarchy level instead of raw item/site codes
Aggregate views KPIs (demand, fill rate, safety stock) are automatically summed at each level, with roll-up from children to parents

Hierarchy Overview


Core Concepts

Hierarchy

A hierarchy is a named tree definition stored in master.hierarchy. It has:

Field Description
name Display name, e.g. "Product Hierarchy"
entity_type Either "item" or "site" — determines whether the tree references items or locations
levels Ordered JSON array of {key, label} pairs defining the tree levels (top to bottom)
description Optional free-text description
sort_order Numeric sort weight (lower = first)
is_active Soft-delete flag; inactive hierarchies are hidden everywhere

Example levels definition for a 4-level product hierarchy:

[
  {"key": "department",  "label": "Department"},
  {"key": "category",    "label": "Category"},
  {"key": "subcategory", "label": "Subcategory"},
  {"key": "item_class",  "label": "Item Class"}
]

The key field must match an attribute key stored in the attributes JSONB column of master.item (for entity_type = "item") or master.location (for entity_type = "site").

Hierarchy Node

A node is a single entry in the tree — for example "Bikes" under Department = Sports Equipment. Each node carries:

Property Description
level Zero-based depth index (0 = root)
key Attribute key this level maps to
label Human-readable level label
value The actual attribute value (e.g. "Bikes")
parent_value Value of the parent node (null for roots)
count Number of leaf-level members (items/sites) under this node, rolled up from children
children Array of child nodes

Members

Members are the items or sites that belong to a leaf node. When you select a node in the hierarchy filter, Mirabelle resolves the selection to a list of item_id / site_id integers (and their unique_id combinations). Parent nodes automatically roll up — their member list is the union of all descendant leaf members.

Multiple Hierarchies

You can create independent hierarchies for different dimensions. The most common pattern is one item hierarchy and one site hierarchy. When both are active simultaneously in the dashboard, their member sets are AND-intersected — only series matching both selections are shown.


Creating a Hierarchy

Via the API

Use POST /api/hierarchies with a JSON body:

{
  "name": "Product Hierarchy",
  "entity_type": "item",
  "levels": [
    {"key": "department",   "label": "Department"},
    {"key": "category",     "label": "Category"},
    {"key": "subcategory",  "label": "Subcategory"},
    {"key": "item_class",   "label": "Item Class"}
  ],
  "description": "4-level product tree for sports equipment",
  "sort_order": 0
}

Response:

{"id": 1}

Prerequisites

Before a hierarchy can produce a tree, the attribute keys referenced in its levels array must exist in the attributes JSONB column of the corresponding master table (master.item or master.location). These attributes are populated during the ETL/import step. If an attribute key is missing from all rows, the tree will have no nodes at that level.


Building the Tree

The tree is auto-built from distinct attribute values — you do not manually add nodes. When you call the tree endpoint, Mirabelle:

  1. Queries master.item (or master.location) for every distinct combination of the level attribute values.
  2. Constructs a nested node structure where each unique value at level 0 becomes a root, and subsequent levels become children.
  3. Counts the members at each leaf and rolls up the counts to parents so every node shows the total number of items/sites beneath it.

Tree endpoint

GET /api/hierarchies/{hierarchy_id}/tree

Response shape:

{
  "hierarchy": {
    "id": 1,
    "name": "Product Hierarchy",
    "entity_type": "item",
    "levels": [
      {"key": "department", "label": "Department"},
      {"key": "category", "label": "Category"},
      {"key": "subcategory", "label": "Subcategory"},
      {"key": "item_class", "label": "Item Class"}
    ]
  },
  "nodes": [
    {
      "level": 0,
      "key": "department",
      "label": "Department",
      "value": "Sports Equipment",
      "parent_value": null,
      "count": 342,
      "children": [
        {
          "level": 1,
          "key": "category",
          "label": "Category",
          "value": "Bikes",
          "parent_value": "Sports Equipment",
          "count": 128,
          "children": [
            {
              "level": 2,
              "key": "subcategory",
              "label": "Subcategory",
              "value": "Mountain",
              "parent_value": "Bikes",
              "count": 64,
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

Hierarchy Tree


Assigning Members

Members are not manually assigned — they are derived from the master data. Each item or site row has an attributes JSONB column. When the hierarchy tree is built, items/sites whose attribute values match a node's path are counted as members of that node (and every ancestor above it).

Members endpoint

GET /api/hierarchies/{hierarchy_id}/members?selections=[...]

The selections query parameter is a URL-encoded JSON array of {level, key, value} objects identifying the path from the root to the desired node.

Example — get all items under Department = Sports Equipment, Category = Bikes:

GET /api/hierarchies/1/members?selections=[
  {"level":0,"key":"department","value":"Sports Equipment"},
  {"level":1,"key":"category","value":"Bikes"}
]

Response:

{
  "entity_type": "item",
  "item_ids": [101, 102, 103, 104],
  "site_ids": [],
  "unique_ids": [
    "101-1", "101-2", "102-1", "103-1", "103-2", "104-1"
  ],
  "count": 6
}

Automatic roll-up

Selecting a parent node (e.g. only department=Sports Equipment) returns all items under every descendant path. The roll-up happens transparently — no additional request is needed.


Using Hierarchies in the Dashboard

The HierarchyFilter component appears in the Dashboard toolbar next to the segment selector.

Hierarchy Filter

Selecting a node

  1. Click the Hierarchy button in the toolbar. A dropdown panel opens, grouped into Items and Sites sections (one section per entity_type).
  2. Expand a hierarchy by clicking its header row.
  3. Navigate the tree by expanding/collapsing nodes. Each node shows its member count on the right.
  4. Click a node to select it. The selected node is highlighted in violet, and a badge appears on the Hierarchy button showing the number of active filters.

When a hierarchy node is selected, the active filter is displayed in the dropdown's Active filters section. Each active filter shows:

  • An icon indicating item (cube) or site (pin) entity type
  • The hierarchy name and selected node value
  • A dismiss button (×) to clear that individual filter

Click Clear all to remove every hierarchy selection at once.

How filtering works

When you select a node, the HierarchyFilter component:

  1. Calls /api/hierarchies/{id}/members to resolve the selection to a set of unique_id strings.
  2. If multiple hierarchies are active, their member sets are AND-intersected — only series present in all active selections remain.
  3. Passes {hierarchy_id, hierarchy_selections} as query parameters to every Dashboard API call, enabling DB-side pushdown filtering for optimal performance on large datasets.

All dashboard tabs — Overview, Forecast, EOQ, IO, Supply, Allocation — respect the hierarchy filter. The series table is also filtered client-side.


Hierarchy in MEIO

MEIO segment rules can reference hierarchy levels to assign differentiated service-level targets. This allows you to set policies like:

"All items in Product Hierarchy → Bikes → Mountain get a 97 % service level target, while items under Bikes → Road get 95 %."

How it works

When the MEIO engine evaluates segment rules, it can use the same attribute keys that define hierarchy levels. Because hierarchies are built from the attributes JSONB column, any rule that filters on attributes->>'category' = 'Bikes' naturally aligns with the hierarchy tree.

This means:

  • Segment definitions and hierarchy nodes share the same attribute vocabulary.
  • Changing a hierarchy level key automatically affects any MEIO rule that references the same key — keep attribute keys stable across both.

See MEIO User Guide and MEIO Parameters for details on configuring segment rules.


Hierarchy in Allocation

The allocation engine uses hierarchies for fairness tracking across hierarchy nodes. This ensures that, when supply is constrained, items in the same business category receive proportional fill rates rather than one category monopolising available stock.

Example

Suppose your allocation policy defines three priority classes (A, B, C). Within the "Bikes" category of your product hierarchy, you want to ensure that Class A items receive their proportional share before Class B or C items are considered.

The hierarchy filter propagated to the allocation tab enables:

  • Fill-rate heatmap — grouped by hierarchy node, showing how fill rates vary across the tree.
  • Allocation overview — KPIs broken down by hierarchy level so planners can spot imbalances early.

Hierarchy Allocation

When the hierarchy filter is active on the Dashboard, the AllocationOverview and FillRateHeatmap components receive the filtered unique_id set, restricting their displays to the selected branch.

See Allocation User Guide for the full allocation workflow.


API Reference

Method Endpoint Description
GET /api/hierarchies List all active hierarchy definitions. Optional query param entity_type filters to "item" or "site".
POST /api/hierarchies Create a new hierarchy. Body: {name, entity_type, levels, description?, sort_order?}. Returns {id}.
GET /api/hierarchies/{id}/tree Return the full tree structure for a hierarchy, auto-built from distinct attribute values. Includes rolled-up member counts.
GET /api/hierarchies/{id}/members Resolve node selections to entity IDs. Query param selections — JSON array of {level, key, value}. Returns {entity_type, item_ids, site_ids, unique_ids, count}.
PUT /api/hierarchies/{id} Update a hierarchy definition. Body: {name?, levels?, description?, sort_order?, is_active?}. Returns {ok: true}.
DELETE /api/hierarchies/{id} Permanently delete a hierarchy. Returns {ok: true}.

Common query parameters (Dashboard endpoints)

Many Dashboard API endpoints accept hierarchy filter parameters for DB-side pushdown:

Parameter Type Description
hierarchy_id int The master.hierarchy row ID to filter by
hierarchy_selections string JSON array of selections, e.g. '[{"level":1,"key":"category","value":"Bikes"}]'

When both are present, the backend resolves the selection to (item_ids, site_ids) integer lists and applies them as SQL WHERE conditions, avoiding the need to ship large unique_id lists over the wire.