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

> Explore the Open Notebook graph database relationship model. Learn how SurrealDB connects notebooks, sources, notes, and insights using explicit graph edges for flexible, duplication-free data relationships.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: architecture
- Published: 2026-06-19

---

**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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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:

```sql
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:

```sql
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:

```sql
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:

```python
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:

```python
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:

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/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.