What You’ll Build
Two JSON files —companies.json and people.json — are loaded and mapped onto typed DataPoint classes, then written straight into the graph and vector stores by a two-task custom pipeline: no chunking, no extraction, no cognify pass. What comes out is an org chart you can open in a browser: Company nodes linked to their Department nodes, each department linked to the Person nodes that work in it, with every name embedded for semantic search. Node IDs are derived from the data itself, so the same department name reaching the pipeline twice lands on one node rather than two.
The script closes by asking the graph a question, so the same run that builds the org chart also shows it is retrievable.
The complete runnable script is in
examples/demos/custom_pipelines/organizational_hierarchy —
this page walks through its key moments rather than reproducing it.
Features in Play
- DataPoints —
Person,Department,Company, andCompanyTypeare the node types;index_fieldsdecides what gets embedded andidentity_fieldsderives the deterministic ID that collapses repeats - Custom Data Models —
add_data_pointspersists the typed objects and their nesting as nodes and edges, with no LLM in the path - Custom Tasks and Pipelines — two
Taskobjects run byrun_tasksagainst a dataset: one maps JSON to DataPoints, one stores them - Graph Visualization — the script renders the org chart to an HTML file you can open
- Search — the script finishes with a
GRAPH_COMPLETIONquery run withonly_context=True, proving the hand-declared nodes are retrievable like any other memory without pulling an LLM into the demo
How It Looks
This is the graph the script writes, loaded from a real run of it. Drag a node to pull it out of the tangle, scroll to zoom, and hover one to fade out everything it does not touch — hovering a department is the quickest way to see exactly who works in it.The org chart from a real run: one CompanyType node at the centre, five companies around it, each linked to the departments it declares, and each department to the people in it. Hover a node to list its edges with the text the run stored on each.
visualize_graph export, so the shape is exactly what lands in your graph store: Research & Development sits on GreenFuture with no one attached to it, because the company declares the department but no one in people.json works there.
What to Expect
The lines below come from one real run of the script, trimmed. Nothing in this demo calls an LLM, so the node and edge counts are identical on every run — only the UUIDs, timestamps, and paths change. A first run also prints a long block of relational migrations as cognee builds its tables; that block is setup, not part of the demo. The pipeline starts with both JSON files already in hand.build_lightweight_data_object has wrapped the two files into a single LightweightData DataPoint, so the payload printed at the start of the run is the whole input — five companies and ten people — before any task has touched it.
ingest_files maps the payload and add_data_points writes the result. There is no extraction step between them, which is why the whole pipeline costs less than a single LLM call would.
Company nodes, eleven Department nodes, ten Person nodes, and the one CompanyType all five companies point at make 27. Eleven company-to-department edges, ten department-to-employee edges, and five is_type edges make 26. Had a department name landed as two nodes, both numbers would be higher.
add_data_points did embed every index_fields value — the Person_name, Department_name, and Company_name collections are written and searchable. The warning comes from the renderer’s semantic-map layer, which only recognizes cognee’s built-in node types and so has nothing to plot for custom DataPoints. The graph view itself is complete.
only_context=True, so what comes back is the prompt built around the graph neighbourhood the query matched — the company, its departments, and the people connected to them, rendered as text — rather than an LLM’s answer. That is the point: GreenFuture’s departments and employees coming back proves the hand-declared DataPoints are retrievable, and it costs an embedding call instead of a completion. The run above predates the query, so no output from it is shown here.
Before You Start
- Complete Quickstart to understand basic operations
- Ensure you have LLM Providers configured — an embedding provider is all this demo needs.
add_data_pointsembeds everyindex_fieldsvalue, and the closing search embeds the query; nothing in the script calls a completion model - Run the script from a checkout of the cognee repo: it reads
companies.jsonandpeople.jsonfrom thedata/folder next to it, and writes its HTML render into a sibling.artifacts/folder - The script starts with
prune.prune_data()andprune.prune_system(metadata=True), which wipes the configured instance — point it at a scratch instance rather than memory you want to keep
How It Works
Stage 1: Model the Org Chart as Deduplicating DataPoints
Source:examples/demos/custom_pipelines/organizational_hierarchy/organizational_hierarchy_pipeline_example.py
Company and CompanyType follow the same pattern below them. Two things happen per class: index_fields names the field that gets embedded, so the graph is searchable by name; identity_fields makes the node’s UUID a deterministic function of that name, so building Person(name="John Doe") twice lands on one node instead of two. The employees field is what makes this a graph rather than two tables — a list of Person objects on a Department becomes edges from that department to those people.
Stage 2: Map the JSON Rows onto Nested DataPoints
Source:examples/demos/custom_pipelines/organizational_hierarchy/organizational_hierarchy_pipeline_example.py
department field, build one Department per group, then hang the departments each company declares off a Company. Nothing here checks whether a node already exists. A company that declares a department nobody works in still gets a Department node — created on the spot with no employees — and if another company declares the same one, identity_fields gives both objects the same UUID, so the two collapse into a single shared node when they are stored. The function returns only the companies; the departments and people reach the graph because they hang off them.
Stage 3: Run the Two-Task Pipeline
Source:examples/demos/custom_pipelines/organizational_hierarchy/organizational_hierarchy_pipeline_example.py
main() prunes, calls setup() to create the relational tables, resolves the default user, and creates a test_dataset with load_or_create_datasets before this point — the dataset is what the pipeline run is tracked against. run_tasks then chains the two tasks: ingest_files returns Company objects and add_data_points writes them, their nested departments and people, and the edges between them into the graph and vector stores. build_lightweight_data_object wraps the raw dict in a LightweightData DataPoint with a uuid5 ID, because the pipeline expects DataPoints rather than plain dicts. Once the pipeline drains, visualize_graph writes the org chart to .artifacts/organizational_hierarchy_pipeline_example.html.
Stage 4: Query the Graph You Just Built
Source:examples/demos/custom_pipelines/organizational_hierarchy/organizational_hierarchy_pipeline_example.py
GRAPH_COMPLETION embeds the question, finds the matching nodes, and walks company → department → employee edges out from them exactly as it would over an LLM-built graph. only_context=True stops there and hands back the prompt instead of sending it to a completion model, which is what keeps the whole script free of completion calls. Passing dataset_ids and user scopes the search to the test_dataset this run created.
search returns a list with one entry per dataset searched. In the default multi-user mode each entry is a dict whose search_result key holds the context; with ENABLE_BACKEND_ACCESS_CONTROL=false each entry is the context string itself. The script’s closing loop reads result.search_result as an attribute, which neither shape has, so as of cognee#5049 the final print raises AttributeError after the graph and render are already written. Until that is fixed upstream, read the entry with result["search_result"] in multi-user mode, or print results directly.
Run It
.artifacts/organizational_hierarchy_pipeline_example.html, next to the script — open that file to see companies linked to departments linked to people — and closes with the “Who works for GreenFuture Solutions?” query. The graph and the render are complete before that query runs, so they are there to inspect even though the script’s final print currently fails as described in Stage 4.
DataPoints
How
index_fields, identity_fields, and nested models shape the graph a DataPoint becomes.Custom Data Models
More on modeling your own node types and storing them with
add_data_points.Custom Tasks and Pipelines
Writing your own tasks and running them as a pipeline, step by step.
Reading the Visualization
What each tab of the rendered org chart shows, and which one to reach for.