Graph Database Relationship Model Between Notebooks, Sources, Notes, and Insights in Open Notebook

Open Notebook stores its core entities in SurrealDB and connects them through explicit graph edges—reference, artifact, and refers_to—enabling flexible many-to-many relationships without data duplication.

Open Notebook is an open-source research management application built on SurrealDB that leverages native graph capabilities to link research artifacts. The graph database relationship model between notebooks, sources, notes, and insights relies on directional edges and embedded records to maintain referential integrity while supporting complex, multi-modal research workflows.

Core Entities and Relationship Overview

Open Notebook defines four primary entities in SurrealDB, connected by explicit graph relationships:

Entity SurrealDB Table Relationship Edge Destination Purpose
Notebook notebook reference source Links a notebook to its referenced source materials
Notebook notebook artifact note Connects a notebook to its contained notes
Source source source_insight source_insight Embeds insights derived from source content
ChatSession chat_session refers_to notebook / source Associates chat sessions with specific notebooks or sources

The source_insight relationship differs from the others—it is implemented as an embedded record in the source_insight table with a source field pointing back to the parent source, rather than a graph edge.

How Relationships Are Implemented in Code

The domain logic resides in open_notebook/domain/notebook.py, where each entity class encapsulates its relationship queries.

Notebook to Source References

The reference edge connects notebooks to their source materials. In open_notebook/domain/notebook.py (lines 29-38), the Notebook.get_sources() method queries for all reference edges where the notebook is the out node and the source is the in node:

select *{source_projection} from (
    select in as source from reference where out=$id
    fetch source
) order by source.updated desc

This directional query allows a single source to be referenced by multiple notebooks without duplication.

Notebook to Note Artifacts

Notes are attached to notebooks via the artifact edge. The Notebook.get_notes() method (lines 54-61) retrieves all notes linked to a specific notebook:

select *{note_projection} from (
    select in as note from artifact where out=$id
    fetch note
) order by note.updated desc

Both human-written and AI-generated notes use this same edge type, distinguished by a note_type field on the note record itself.

Source to Insights

Insights are AI-generated extractions scoped to specific sources. Unlike the graph edges, insights are stored in the source_insight table with a foreign key relationship. The Source.get_insights() method (lines 58-66) queries:

SELECT * FROM source_insight WHERE source=$id

This embedded approach keeps insights logically grouped with their originating source while allowing independent querying.

Chat Session Linkage

Chat sessions maintain context through the refers_to edge. The ChatSession class provides helper methods—relate_to_notebook() and relate_to_source() (lines 85-94)—that invoke self.relate("refers_to", ...) to create bidirectional links between conversations and the research materials they discuss.

Practical Code Examples

The following async patterns demonstrate how to traverse these relationships in application code.

Fetching Sources with Full Text

Retrieve all sources for a notebook including their extracted content:

async def list_notebook_sources(notebook_id: str):
    notebook = await Notebook.get(notebook_id)
    sources = await notebook.get_sources(include_full_text=True)
    return [
        {"id": s.id, "title": s.title, "text": s.full_text} 
        for s in sources
    ]

Creating and Linking Notes

Add a new human-written note to an existing notebook:

async def create_note(notebook_id: str, title: str, content: str):
    note = Note(title=title, content=content, note_type="human")
    await note.save()
    await note.add_to_notebook(notebook_id)  # Creates `artifact` edge

    return note.id

Converting Insights to Notes

Promote AI-generated insights from a source into notebook artifacts:

async def insight_to_notes(source_id: str, notebook_id: str):
    source = await Source.get(source_id)
    insights = await source.get_insights()
    for insight in insights:
        await insight.save_as_note(notebook_id)  # Creates note and links via `artifact`

Design Benefits of the Graph Model

The explicit graph architecture provides several advantages for research workflows:

  • Sharing without duplication: A source can be referenced by multiple notebooks via independent reference edges. Deleting a notebook removes only the edge, preserving the underlying source unless explicitly requested otherwise.
  • Bidirectional traversal: You can navigate from a notebook to its sources, or from a source discover all notebooks that reference it by querying the inverse reference edge.
  • Context aggregation: The Notebook.get_context() method leverages these relationships to assemble complete research contexts—combining source text, extracted insights, and human notes—in a single query traversal.

Summary

  • SurrealDB powers Open Notebook's graph model with native edge support.
  • Three graph edges manage core relationships: reference (Notebook→Source), artifact (Notebook→Note), and refers_to (ChatSession→Notebook/Source).
  • Embedded insights use the source_insight table with foreign keys rather than graph edges, scoping extractions to their origin sources.
  • All domain logic resides in open_notebook/domain/notebook.py, with methods like get_sources(), get_notes(), and get_insights() encapsulating the SurrealQL queries.
  • Many-to-many flexibility allows sources and notes to exist across multiple notebooks without data duplication.

Frequently Asked Questions

What graph database does Open Notebook use?

Open Notebook uses SurrealDB as its underlying datastore. SurrealDB provides native graph capabilities through directional edges (e.g., reference, artifact) while also supporting traditional relational queries for embedded records like source_insight.

How are notebooks linked to sources in the Open Notebook data model?

Notebooks link to sources via the reference edge. In the graph model, the notebook is the out node and the source is the in node. This is queried in Notebook.get_sources() using SurrealQL to select all in nodes where out equals the notebook ID.

What is the difference between a note and an insight in Open Notebook?

A note is a first-class entity stored in the note table and linked to notebooks via the artifact edge. It can be human-written or AI-generated. An insight is an AI-generated extraction stored in the source_insight table with a foreign key to its parent source, representing structured data extracted from raw content.

How does Open Notebook handle chat session contexts?

Chat sessions use the refers_to edge to maintain links to either notebooks or specific sources. The ChatSession class provides relate_to_notebook() and relate_to_source() methods that create these edges, enabling the chat interface to retrieve relevant context when processing queries.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →