# How Open Notebook Implements Vector Search for Knowledge-Base Data

> Discover how Open Notebook uses vector search with Esperanto AI and SurrealDB to efficiently query knowledge-base data using cosine similarity.

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

---

**Open Notebook implements vector search by generating query embeddings through the Esperanto AI layer and executing a SurrealDB stored procedure `fn::vector_search` that performs cosine similarity matching against pre-computed embeddings stored on Source and Note records.**

Open Notebook is an open-source knowledge management system that stores notebook content in **SurrealDB**, a multi-model graph database. The platform provides semantic search capabilities through a **vector search** implementation that leverages SurrealDB's built-in vector storage and similarity functions. This article examines the exact mechanisms, file paths, and function calls that enable fast, scalable semantic retrieval across Source and Note records.

## Architecture Overview

The vector search system integrates three core layers: the **Esperanto AI provider** for embedding generation, **SurrealDB** for vector storage and similarity computation, and a **Python domain layer** that orchestrates the workflow. When users submit a vector search query, the system embeds the text, invokes SurrealDB's `fn::vector_search` stored procedure, and returns ranked results based on cosine similarity scores.

## The Vector Search Pipeline

### Step 1: Generate Query Embeddings

The embedding process begins in [`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py) via the `generate_embedding` function. This utility lazily loads the configured embedding model through the Esperanto AI-provider layer (`open_notebook/ai/models/model_manager`) by calling `model_manager.get_embedding_model()`.

For text input, the system executes `model.aembed([text])` to generate vector representations. If the query exceeds the model's payload limit, the helper automatically chunks the text, embeds each segment individually, and applies **mean pooling** through the `mean_pool_embeddings` function to produce a single representative vector.

### Step 2: Execute SurrealDB Vector Search

The domain layer in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) contains the `vector_search` wrapper function that receives the query embedding and forwards it to SurrealDB. The system executes the following raw SurrealQL query through the `repo_query` helper in [`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py):

```sql
SELECT * FROM fn::vector_search($embed, $results, $source, $note, $minimum_score);

```

Parameters include:

- `$embed`: The pre-computed query embedding vector
- `$results`: Maximum number of matches to return
- `$source`: Boolean flag to include Source records
- `$note`: Boolean flag to include Note records
- `$minimum_score`: Cosine similarity threshold for filtering

### Step 3: Return Ranked Matches

SurrealDB computes the **cosine similarity** between the query embedding and pre-computed embeddings stored on each Source and Note record. The database returns the top-N matching records already ordered by similarity, which the API layer forwards directly to the client without additional sorting.

## Fallback Behavior and Error Handling

When a standard text search fails due to highlighted position overflow, the system automatically falls back to the vector search path. This error-handling block in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) ensures users receive relevant semantic results even when exact text matching encounters boundary limitations.

## Programmatic Usage and API Examples

### Embedding a Query and Searching Programmatically

```python
from open_notebook.domain.notebook import vector_search

async def find_similar(term: str):
    # Returns up to 10 most similar sources/notes

    results = await vector_search(
        keyword=term,
        results=10,
        source=True,
        note=True,
        minimum_score=0.2,
    )
    return results

```

### Using the HTTP API Endpoint

Access the search functionality via the `/search` endpoint defined in [`api/routers/search.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/search.py):

```bash
curl -X POST http://localhost:5055/search \
  -H "Content-Type: application/json" \
  -d '{
        "type": "vector",
        "term": "machine learning pipelines",
        "results": 5,
        "minimum_score": 0.3
      }'

```

### Generating Embeddings for Sources

While embeddings generate automatically upon source creation, you can manually trigger the process:

```python
from open_notebook.commands.embedding_commands import embed_source_command

# Assume `source_id` is the UUID of the source

await embed_source_command(source_id=source_id)

```

## Key Source Files and Components

- **[`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py)**: Contains the `vector_search()` wrapper that builds embeddings and calls SurrealDB's `fn::vector_search`.
- **[`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py)**: Implements `generate_embedding()`, `mean_pool_embeddings()`, and batch handling with retry logic.
- **[`open_notebook/database/repository.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/database/repository.py)**: Provides `repo_query()` for executing raw SurrealQL queries against the async driver.
- **[`api/routers/search.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/search.py)**: Defines the HTTP endpoint that routes requests to text or vector search based on payload parameters.
- **[`commands/embedding_commands.py`](https://github.com/lfnovo/open-notebook/blob/main/commands/embedding_commands.py)**: Background job handlers for generating and storing embeddings.
- **[`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py)**: Houses the `ModelManager` that selects appropriate embedding providers via the Esperanto framework.

## Summary

- Open Notebook stores all content in **SurrealDB**, which provides native vector search capabilities through the `fn::vector_search` stored procedure.
- The **Esperanto AI layer** handles embedding generation with automatic chunking and mean-pooling for large text inputs.
- The **`vector_search`** function in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) orchestrates the query embedding and database execution.
- **Cosine similarity** matching occurs inside SurrealDB, returning pre-sorted results filtered by minimum score thresholds.
- The system includes automatic **fallback to vector search** when text search operations fail due to position overflow.

## Frequently Asked Questions

### What database does Open Notebook use for vector storage?

Open Notebook uses **SurrealDB** as its primary data store. The database stores pre-computed embeddings directly on Source and Note records and provides the built-in stored procedure `fn::vector_search` for performing cosine similarity calculations.

### How does Open Notebook handle text that exceeds the embedding model's token limit?

The `generate_embedding` function in [`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py) automatically chunks large text inputs, generates embeddings for each chunk individually, and then applies **mean pooling** via `mean_pool_embeddings` to combine them into a single representative vector.

### Can I use vector search through the REST API?

Yes. The `/search` endpoint in [`api/routers/search.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/search.py) accepts a JSON payload with `"type": "vector"` to trigger semantic search. You can specify parameters including `term`, `results`, `minimum_score`, and boolean flags for including sources or notes.

### What happens if a text search fails?

When a text search fails due to highlighted position overflow errors, the code automatically falls back to the `vector_search` path in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py), ensuring users still receive relevant results through semantic similarity matching.