What You’ll Build
Four small files — two technical documents (an API reference and an architecture guide) and two sales-call transcripts (where a rep promises “unlimited requests” and “instant sync”) — are ingested three ways: everything in one dataset, one dataset with node-set tags, and two walled-off datasets with node sets inside each. The same questions run under every layout, and each recall prints as a comparableQUERY / SCOPE / ANSWER block, so you can see exactly what each layout changes: the unfiltered pile blends promise with spec, node sets scope a query to one group while still answering cross-domain questions, and datasets make leakage impossible. The run ends with the rule of thumb the demo exists to teach: node sets are tags, datasets are walls.
The complete runnable demo — the script plus the four data files it reads — is
examples/demos/organizing_your_data/ —
this page walks through its key moments rather than reproducing it.
Features in Play
- NodeSets — tags each source with soft, overlappable groups at write time;
node_namescopes retrieval to a tag at read time - Datasets — the hard boundary with its own permissions and storage that keeps sales transcripts out of technical answers entirely
- Remember — ingests the four bundled files under each layout’s labels
- Recall — runs the same questions under every layout, scoped by
datasetsandnode_name
What to Expect
The excerpts below are from a real run, trimmed, and follow the same seven stages as How It Works: that section explains what each stage does, this one shows the output each stage produces. Stages 2 and 5 areremember() calls that print nothing, so they appear together with the recall that first reveals their effect. Every recall prints as a QUERY / SCOPE / NOTE / ANSWER block, and each ANSWER line is prefixed with the dataset it came from, so the same question can be compared across scopes by eye. Every answer is a live LLM call, so the wording varies from run to run.
Stage 1 — one pile, one retrieval pool. With no filter, the rate-limit question retrieves from the API reference and the sales calls alike. In this run the model resolved the conflict in favor of the documentation, but nothing in the query kept the rep’s promise out of the pool; the NOTE line names the competition to watch for.
tech_docs, the answer is the documented limit; scoped to sales_calls with the same call shape, it is what the rep said, with no mention of the spec.
remember() calls in Stage 5 print nothing; the [tech_docs] prefix on the first answer shows it came from that dataset alone, and Stage 6’s combined datasets plus node_name filter narrows the second to the Initech call only.
datasets returns one ANSWER per readable dataset, [tech_docs] and [sales_calls], so the rep’s promise is back in the result. Separating data at write time is not enough; the query has to choose its scope. The run then closes with the TAKEAWAY banner.
Before You Start
- Complete Quickstart to understand basic operations
- Ensure you have LLM Providers configured — every answer is a live LLM call
- Use Ladybug (the default) or Neo4j as your graph store — both support node-set filtering out of the box
- Leave
ENABLE_BACKEND_ACCESS_CONTROLat its default, or set it totrue: dataset scoping inrecall()only walls datasets off in multi-user mode, which the default Ladybug and LanceDB stack turns on automatically - Run it from a checkout of the cognee repo: the script reads its two markdown documents and two transcripts from the sibling
data/folder - The script calls
cognee.forget(everything=True)before each of its three sections, so point it at a scratch instance rather than memory you want to keep — see Forget
How It Works
Stage 1: Ingest Everything into One Dataset
Stage 2: Tag Every Source with Node Sets
acme_api tag cuts across the docs/calls split, which no folder-style hierarchy could express.
Stage 3: Scope a Recall to One Tag
node_name restricts retrieval to the tagged subgraph, so the same question now answers from the documentation alone. The sales calls are still in the graph — they just don’t participate in this query.
Stage 4: Ask a Cross-Domain Question Unfiltered
Stage 5: Wall Off Tech and Sales into Datasets
Stage 6: Combine Dataset and Node-Set Filters
datasets picks the wall, node_name picks the tag inside it, and the answer covers one account’s calls only.
Stage 7: Watch an Unscoped Recall Span Both Datasets
recall() without datasets spans every dataset you can read, so the final answer draws on both sides again — queries must opt into the scope they want.
Run It
Choosing a Layout
Start with the simplest layout that answers your queries cleanly. One dataset with no labels is fine while the corpus covers a single domain for a single audience. Reach for node sets the moment distinct content types share entities you still want linked — scoped questions stay clean, and cross-domain questions keep working. Move a domain into its own dataset when it must never contaminate the other’s answers, or when it needs different access control or retention — and remember the two compose, so datasets can carry node sets inside them.NodeSets
How node-set labels are written and how
node_name filters retrieval by them.Datasets
What a dataset isolates — documents, permissions, and processing.
NodeSet Grouping
The minimal single-feature guide to tagging memories with node sets.
Agentic Procurement Decisions
A sibling demo where node-set scoping powers an agent’s research phase.