Hierarchies in MakeItMakeSense.io
This document describes the structure, management, and usage of hierarchies within the MakeItMakeSense.io platform. Hierarchies provide a way to organize nodes into structured, multi-level categorizations, and a single node can belong to multiple hierarchies simultaneously.
Core Schema Entities
The hierarchy system is built upon several key Dgraph schema types that define its structure and relationships:
Node
: These are the primary content entities within the graph. Nodes can be associated with one or more hierarchies throughHierarchyAssignment
objects.Hierarchy
: This type represents a distinct hierarchical tree or organizational structure (e.g., "Technical Skills," "Project Categories"). Each hierarchy has a name and contains multiple levels.HierarchyLevel
: This type defines a specific tier or depth within a particularHierarchy
(e.g., "Programming Languages" as a level within "Technical Skills"). Each level has a number and an optional descriptive label. It can also (optionally) specify allowed node types.HierarchyLevelType
: Specifies whichNode
types are permitted at a givenHierarchyLevel
, controllingallowedTypes
consumed by both backend validation and UI filtering.HierarchyAssignment
: This is the central linking type. An instance ofHierarchyAssignment
explicitly connects aNode
to a specificHierarchyLevel
within a particularHierarchy
. This record is what places a node into the hierarchical structure.
For detailed Dgraph schema definitions and GraphQL interaction patterns related to these types, please refer to the Dgraph Schema Notes.
(Note: The HierarchyLevelType
feature for restricting node types at specific levels is now actively enforced by the backend API during node assignment.)
Rationale for the HierarchyAssignment
Model
The use of an intermediate HierarchyAssignment
type (rather than a direct link from Node
to HierarchyLevel
) offers several advantages:
-
Multi-Hierarchy Support: This is the primary benefit. A single
Node
can have multipleHierarchyAssignment
objects, each linking it to a differentHierarchy
(or even different levels within the same hierarchy, though less common). This allows for rich, multi-faceted organization of knowledge. For example, a "Python" node could be in a "Technical Skills" hierarchy at the "Programming Language" level, and simultaneously in a "Project Stack" hierarchy at the "Backend Technologies" level. -
Rich Relationship Information (Future-Proofing): The
HierarchyAssignment
node itself can be expanded to store metadata about the assignment. For instance, one could add fields likeassignmentDate
,assignedBy
, orrelevanceInHierarchy
directly to theHierarchyAssignment
type. This would be difficult if nodes linked directly to levels. -
Querying Flexibility: This model allows for versatile querying, such as finding all nodes in a specific level of a specific hierarchy, or all hierarchical contexts a particular node belongs to.
-
Clear Semantics: It explicitly models the "act of assignment" as a first-class entity in the graph.
The main trade-off is a slightly more verbose data structure (an extra "hop" through the assignment node), but this is generally outweighed by the flexibility gained, especially for multi-hierarchy requirements.
Hierarchy Management and Usage
Backend API
-
Direct Management (CRUD Operations):
- The API provides RESTful endpoints (primarily under
/api/hierarchy/...
) for creating, reading, updating, and deletingHierarchy
,HierarchyLevel
, andHierarchyAssignment
entities. These are publicly accessible as hierarchies are user-managed knowledge organization tools. - Refer to
docs/api-endpoints.md
for detailed endpoint specifications.
- The API provides RESTful endpoints (primarily under
-
Automatic Assignment during Node Creation (
POST /api/mutate
withaddNode
):- When new nodes are created via the
addNode
GraphQL mutation, the API server includes logic (implemented inroutes/graphql.ts
andservices/nodeEnrichment.ts
) to automatically create aHierarchyAssignment
. - Processing Priority for Assignment (Current Implementation):
- Client-Provided
hierarchyAssignments
Array (Primary): The server first checks if the client provides ahierarchyAssignments
array in the input. This is the standard structure sent by the frontendNodeFormModal
. If present:- Extracts
hierarchy.id
andlevel.id
from the nested structure - Validates the hierarchy and level IDs exist
- Validates the node type is allowed at the specified level
- Preserves the client's
hierarchyAssignments
structure in the Dgraph mutation
- Extracts
- Top-Level
levelId
(Compatibility): If nohierarchyAssignments
array is provided, checks for a top-levellevelId
property. If present:- Uses the
X-Hierarchy-Id
header for the hierarchy context - Validates the level ID and node type compatibility
- Constructs a
hierarchyAssignments
structure for Dgraph
- Uses the
- Parent Node Context (Calculated): If neither
hierarchyAssignments
norlevelId
is provided, but aparentId
is specified:- Uses
getLevelIdForNode
to calculate the appropriate level (parent's level + 1) - Validates the calculated level and node type compatibility
- Constructs a
hierarchyAssignments
structure for Dgraph
- Uses
- Client-Provided
- Header Requirements: The
X-Hierarchy-Id
header is required foraddNode
operations to provide hierarchy context for cases 2 and 3. - Validation: All hierarchy and level IDs are validated for existence, and node types are checked against level restrictions (
allowedTypes
). - Error Handling: Returns 400 Bad Request for missing headers, invalid IDs, or type restrictions violations.
- When new nodes are created via the
Frontend Interaction
-
HierarchyContext
(frontend/src/context/HierarchyContext.tsx
):- Manages the application-wide state for available hierarchies, the currently selected
hierarchyId
, and the levels of that selected hierarchy. - Fetches hierarchy data from
GET /api/hierarchy
and level data using a GraphQL query. - The
setHierarchyId
function updates the selected hierarchy in the context and automatically updateslocalStorage.setItem('hierarchyId', newId)
, ensuring synchronization forApiService
.
- Manages the application-wide state for available hierarchies, the currently selected
-
ApiService
(frontend/src/services/ApiService.ts
):- When
executeMutation
is called, it readshierarchyId
fromlocalStorage
and, if present, adds it as anX-Hierarchy-Id
header to the request. This provides the backend with the "active hierarchy" context for its automatic assignment logic.
- When
-
NodeFormModal
(frontend/src/components/NodeFormModal.tsx
):- Allows users to select the target
Hierarchy
andHierarchyLevel
when creating a new node. - Automatically updates available node types based on the selected level's restrictions.
- Synchronizes the selected type with available types when level selection changes.
- On submission, constructs a
hierarchyAssignments
array with the chosenhierarchyId
andlevelId
. - Passes this structure to
useGraphState.addNode
, which sends it to the backend as the primary source of hierarchy assignment information.
- Allows users to select the target
Data Flow Summary
The complete node creation flow with hierarchy assignment works as follows:
- User Interaction: User opens NodeFormModal and selects hierarchy, level, type, and label
- Frontend Validation: Modal ensures selected type is allowed at the chosen level
- Form Submission: Modal constructs
hierarchyAssignments
array and callsuseGraphState.addNode
- API Request: Frontend sends mutation with
hierarchyAssignments
structure andX-Hierarchy-Id
header - Server Processing: Server processes the
hierarchyAssignments
array (Case 1), validates hierarchy/level/type compatibility - Database Storage: Server preserves the client's hierarchy assignment structure in the Dgraph mutation
- Response: Node is created with proper hierarchy assignment
Seeding
- The
tools/seed_data.py
script demonstrates the programmatic creation of hierarchies, levels, andHierarchyLevelType
entities using the API. It also shows how to create nodes and assign them to hierarchies, respecting the validation rules (e.g., by passing theX-Hierarchy-Id
header for mutations).
This multi-faceted approach allows for both explicit control over hierarchy assignments (primarily driven by the frontend UI or direct API calls) and robust server-side validation, with the frontend-provided hierarchyAssignments
structure taking precedence over other assignment methods.