Find notes by meaning
Semantic search finds notes by meaning instead of exact words. The engine splits each note into passages, sends them to an embedding model you choose and stores the resulting vectors in the vault. Agents and programs then search with the semantic_search operation. It’s off by default, because note text goes to the model’s provider.
Turn on semantic search
Section titled “Turn on semantic search”Turn it on in the engine’s settings file, settings.json in its store (datanotes store path shows where):
- Stop the engine.
- Pick a provider and a model from the embedding providers table.
- Set
semanticEnabled,semanticProviderandsemanticModelin the settings file. - If the provider needs an API key, pass the key to the engine.
- Start the engine.
The settings below use Ollama, which keeps your notes on your computer:
{ "semanticEnabled": true, "semanticProvider": "ollama", "semanticModel": "nomic-embed-text"}Leave semanticBaseUrl empty to use the provider’s default address. The settings reference lists every key and its default.
Choose an embedding provider
Section titled “Choose an embedding provider”The provider decides where your note text goes. Local providers keep it on your computer; hosted ones send it over the network:
semanticProvider |
Where notes go | Default address | Example model |
|---|---|---|---|
ollama |
Stays on this computer | http://127.0.0.1:11434 |
nomic-embed-text, qwen3-embedding:0.6b |
lmstudio |
Stays on this computer | http://127.0.0.1:1234/v1 |
text-embedding-nomic-embed-text-v1.5 |
openrouter |
OpenRouter and the model’s provider (needs an API key) | https://openrouter.ai/api/v1 |
openai/text-embedding-3-small, qwen/qwen3-embedding-0.6b |
openai |
OpenAI (needs an API key) | https://api.openai.com/v1 |
text-embedding-3-small |
custom |
Any OpenAI-compatible /embeddings API |
None: set semanticBaseUrl |
The model your API serves |
Pass the API key to the engine
Section titled “Pass the API key to the engine”The engine never stores API keys in the settings file. Set the DATANOTES_EMBEDDING_KEY environment variable before you start the engine, or pass a file that holds the key:
npx -y datanotes serve --database your_database_folder \ --embedding-key-file your_key_fileChoose which notes to index
Section titled “Choose which notes to index”By default the engine indexes every note in the vault. Two settings narrow the scope:
semanticScope:"vault"(the default) indexes every note;"tables"indexes only table rowssemanticExclude: folders to leave out
The engine always skips _archive folders.
How the engine builds the index
Section titled “How the engine builds the index”The engine splits each note into passages of at most about 1,500 characters. The splitting follows these rules:
- First passage: holds the title and the front matter, up to 800 characters, without the keys
idandtable - Heading sections: each section starts a new passage
- Long sections: the engine splits them at paragraph boundaries
- Limit: 40 passages per note
The engine sends the passages to the model and stores the vectors as int8 values in semantic/, in its store. It updates the index 10s after edits stop, and 5s after it starts. It embeds again only the notes whose content changed. If you change the provider, the address or the model, the engine starts a new index.
Search from agents and programs
Section titled “Search from agents and programs”Agents and programs use three operations. All three work through the HTTP API, the JavaScript client and the Model Context Protocol (MCP) server:
semantic_search: takes aquery, with optionaltables,folder,min_scoreandlimit; returns one result per note, its best passage, best firstdescribe_semantic_index: shows the index statussync_semantic_index: brings the index up to date, or rebuilds it withrebuild: true
This example asks for the five notes closest in meaning to a question. db is a client connected to the engine:
const r = await db.call("semantic_search", { query: "films that scare without a single spoken word", limit: 5,});Search these docs by meaning
Section titled “Search these docs by meaning”With semantic search on, the engine also embeds the sections of these docs, in an index of their own. search_docs then merges its ranking by words with the engine’s ranking by meaning. Search by words and by meaning explains how.
Search notes by meaning
Section titled “Search notes by meaning”semantic_search finds notes by meaning rather than by words. It’s off by default, because it sends note text to an embedding provider. Until you turn it on in the engine’s settings, the call answers that it’s off; Turn on semantic search explains how. describe_semantic_index reports the state of the index, and sync_semantic_index brings it up to date once it’s on.
semantic_search
{ "query": "the Friday we watched the Cary Grant film" }Refused:
semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settingsHint: Check the arguments of semantic_search: query (string, required), limit (integer), tables (list of strings), folder (string), min_score (number).Docs: https://datanotes.dev/reference/operations/queries.md#semantic_search (offline: read_docs("reference/operations/queries#semantic_search"))describe_semantic_index (no arguments)
Answer:
{ "enabled": false, "provider": "ollama", "model": "", "files": 0, "passages": 0, "pending": 6, "running": false}sync_semantic_index (no arguments)
Refused:
semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settingsHint: Check the arguments of sync_semantic_index: rebuild (boolean).Docs: https://datanotes.dev/reference/operations/auxiliary.md#sync_semantic_index (offline: read_docs("reference/operations/auxiliary#sync_semantic_index"))