Understanding the Relationship Between Notebooks, Sources, and Notes in Open Notebook
In Open Notebook, Notebooks act as parent containers that link to Sources and Notes through explicit graph edges in SurrealDB, using reference edges for sources and artifact edges for notes.
Open Notebook implements a graph-based domain model where three core entities—Notebooks, Sources, and Notes—are stored in SurrealDB and connected through directed relationships. This architecture enables efficient querying and contextual assembly for LLM workflows while maintaining strict data integrity through cascade deletes.
The Graph-Based Architecture
The relationship between Notebooks, Sources, and Notes relies on SurrealDB's graph capabilities rather than traditional foreign keys. Notebooks serve as the parent node, with directed edges pointing to child Sources and Notes.
Notebook-to-Source Relationships via Reference Edges
Sources connect to Notebooks through the reference edge table. In open_notebook/domain/notebook.py, the Notebook.get_sources() method queries these edges to fetch all sources belonging to a specific notebook.
The edge direction follows out → notebook, in → source, meaning the notebook is the origin and the source is the target. When adding a source to a notebook, Source.add_to_notebook(notebook_id) creates this edge relationship (lines 72-76).
# From open_notebook/domain/notebook.py, lines 29-41
notebook = await Notebook.get(notebook_id)
sources = await notebook.get_sources() # Returns List[Source]
Notebook-to-Note Relationships via Artifact Edges
Notes link to Notebooks through the artifact edge table. The Notebook.get_notes() method retrieves these connections (lines 47-63), while Note.add_to_notebook(notebook_id) establishes the link (lines 61-65).
# Retrieve notes with optional content loading
notes = await notebook.get_notes(include_content=True)
Working with Domain Relationships in Code
The open_notebook/domain/notebook.py file contains the implementation for managing these graph relationships. Here's how to create and query these connections:
# Fetch a notebook instance
notebook = await Notebook.get(notebook_id)
# Get sources without loading full text (efficient)
sources = await notebook.get_sources()
# Get notes with full content
notes = await notebook.get_notes(include_content=True)
# Create and link a new source
new_source = Source(title="Research Article", full_text="Content...")
await new_source.save()
await new_source.add_to_notebook(notebook.id)
# Create and link a new note
new_note = Note(title="Summary", content="Key points...")
await new_note.save()
await new_note.add_to_notebook(notebook.id)
Operational Benefits of the Graph Structure
This directed graph architecture provides specific advantages for the Open Notebook platform.
Efficient Retrieval with Selective Loading
The relationship design supports lazy loading through flags like include_full_text and include_content. When calling get_sources() or get_notes(), the system queries only the edge tables and metadata unless explicitly requested to load the full content.
Context Assembly for LLM Workflows
The Notebook.get_context() method (lines 70-78) leverages these relationships to build unified context strings. It iterates through connected sources and notes, calling their respective get_context methods to assemble a long-form text suitable for LLM processing.
Cascade Delete Integrity
When deleting a notebook, the system handles cleanup through a specific sequence. The code first removes associated notes using note.delete(), optionally removes exclusive sources, then deletes the reference and artifact edges before finally removing the notebook record itself (lines 24-30).
Summary
- Notebooks serve as parent containers in the graph, with outgoing edges to Sources and Notes.
- Sources connect via
referenceedges, accessed throughNotebook.get_sources()and linked viaSource.add_to_notebook(). - Notes connect via
artifactedges, accessed throughNotebook.get_notes()and linked viaNote.add_to_notebook(). - The directed graph structure in SurrealDB enables efficient querying, context building for LLMs, and proper cascade deletion.
- All relationship logic is implemented in
open_notebook/domain/notebook.pywith support fromopen_notebook/domain/base.pyfor generic CRUD operations.
Frequently Asked Questions
How do Notebooks, Sources, and Notes relate in Open Notebook?
Notebooks act as parent containers that own Sources and Notes through explicit graph edges in SurrealDB. Notebooks point to Sources via reference edges and to Notes via artifact edges, creating a directed relationship where the notebook is always the out side and the child entities are the in side.
What is the difference between reference and artifact edges?
The reference edge table links Notebooks to Sources, while the artifact edge table links Notebooks to Notes. This separation allows the system to distinguish between source material (references) and synthesized content (artifacts) when querying relationships.
How does the code retrieve all Sources for a Notebook?
The Notebook.get_sources() method in open_notebook/domain/notebook.py (lines 29-41) queries the reference edge table to fetch all sources where the notebook is the out node. This method supports an include_full_text parameter to control whether the source content is loaded or just metadata.
What happens to related Sources and Notes when a Notebook is deleted?
The deletion process follows a cascade pattern. First, the system deletes all associated Notes using note.delete(), then optionally removes exclusive Sources, followed by the removal of all reference and artifact edges, and finally deletes the Notebook record itself. This prevents orphaned records and maintains database integrity.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →