# Load your data

Source: https://docs.littlebigbrain.com/start/load-data/

You load your data as JSON, from your own code. Rows from a database and
sections from your documents become records and links in one graph.

## Four calls to start

| Call | What it does |
| --- | --- |
| `define` | Declares your types and links |
| `evolve` | Declares your fields |
| `commit` | Writes your records and links as JSON |
| `embeddings.declare` | Turns on search by meaning |

Then you search in plain words and follow links with a query. You run no
embedding model and no vector store.

## Pick your use case

Every example on this page follows the use case you pick here. So does the
guide [Add links with an LLM](https://docs.littlebigbrain.com/guides/add-links-with-an-llm/).

### Support

**Your sources.** A ticket database and your release notes.

**The question.** Which release fixed the login bug?

**The graph.**

```text
Ticket → REPORTED_AS → Bug ← COVERS ← Section → PART_OF → Document
```

### Accounting

**Your sources.** A ledger database and your accounting manual.

**The question.** Why was JE-1042 booked to account 6815?

**The graph.**

```text
JournalEntry → BOOKED_TO → Account ← COVERS ← Section → PART_OF → Document
```

### Legal

**Your sources.** A contract database and the signed contract files.

**The question.** Which contracts need consent after a merger?

**The graph.**

```text
Party ← HAS_PARTY ← Contract ← COVERS ← Section → PART_OF → Document
```

### Life sciences

**Your sources.** A batch record database and your deviation reports.

**The question.** Which batches were made from the lot that failed testing?

**The graph.**

```text
Batch → MADE_FROM → Lot ← COVERS ← Section → PART_OF → Document
```

### Security

**Your sources.** A service catalog and your security advisories.

**The question.** Which services are exposed to the log4j bug?

**The graph.**

```text
Service → DEPENDS_ON → Library ← COVERS ← Section → PART_OF → Document
```

Each use case loads two sources. The database gives the records and their
links. The documents give the text that explains them. The `COVERS` link joins
the two.

## What becomes what

| Your data | In the graph | See |
| --- | --- | --- |
| A database or a spreadsheet | Rows become records. Foreign keys become links. | [Load a database](#2-load-a-database) |
| Documents: a wiki, Notion pages, PDFs | Each section becomes a record with its text. | [Load documents](#3-load-documents) |
| Facts that only the text states | An LLM reads the text and writes links. | [Add links with an LLM](https://docs.littlebigbrain.com/guides/add-links-with-an-llm/) |
| RDF files: Turtle, N-Triples | Each statement is stored as written. | [Load RDF files](#load-rdf-files) |

## Set up the client

The examples read the files of your use case from the current folder. They
write to a graph named `kb`. To try a second use case, change the graph name.

### TypeScript

```ts
import { readFileSync } from "node:fs";
import { LbbClient } from "@littlebigbrain/client";

const lbb = new LbbClient({
  baseUrl: process.env.LBB_URL!,
  apiKey: process.env.LBB_API_KEY!,
  graph: "kb",
});
const read = (file: string) => JSON.parse(readFileSync(file, "utf8"));
```

### Python

```python
import json
import os
import time

from lbb import LbbClient

lbb = LbbClient(os.environ["LBB_URL"], api_key=os.environ["LBB_API_KEY"], graph="kb")

def read(file):
    with open(file) as f:
        return json.load(f)
```

### cURL

```sh
export LBB_URL="https://<tenant>--<stack>.db.eu.littlebigbrain.com"
export LBB_API_KEY="<your stack API key>"
```

The commands also use `jq`.

The [quickstart](https://docs.littlebigbrain.com/start/quickstart/#1-get-your-credentials) shows where to find
the endpoint and the API key.

## 1. Declare the schema

A graph accepts only the types, links and fields you declare. So the schema
comes first. `schema.json` names the types and the links between them.
`fields.json` names the fields and their value types.

### Support

```json title="schema.json"
{
  "entity_types": ["Ticket", "Bug", "Document", "Section"],
  "relation_types": [
    { "name": "REPORTED_AS", "source": "Ticket", "target": "Bug" },
    { "name": "PART_OF", "source": "Section", "target": "Document" },
    { "name": "COVERS", "source": "Section", "target": "Bug" }
  ]
}
```

```json title="fields.json"
{
  "ops": [
    { "op": "add_property", "name": "priority", "value_type": "keyword" },
    { "op": "add_property", "name": "opened_at", "value_type": "date_time" },
    { "op": "add_property", "name": "text", "value_type": "text" }
  ]
}
```

### Accounting

```json title="schema.json"
{
  "entity_types": ["JournalEntry", "Account", "Document", "Section"],
  "relation_types": [
    { "name": "BOOKED_TO", "source": "JournalEntry", "target": "Account" },
    { "name": "PART_OF", "source": "Section", "target": "Document" },
    { "name": "COVERS", "source": "Section", "target": "Account" }
  ]
}
```

```json title="fields.json"
{
  "ops": [
    { "op": "add_property", "name": "amount", "value_type": "f64" },
    { "op": "add_property", "name": "booked_on", "value_type": "date_time" },
    { "op": "add_property", "name": "text", "value_type": "text" }
  ]
}
```

### Legal

```json title="schema.json"
{
  "entity_types": ["Contract", "Party", "Document", "Section"],
  "relation_types": [
    { "name": "HAS_PARTY", "source": "Contract", "target": "Party" },
    { "name": "PART_OF", "source": "Section", "target": "Document" },
    { "name": "COVERS", "source": "Section", "target": "Contract" }
  ]
}
```

```json title="fields.json"
{
  "ops": [
    { "op": "add_property", "name": "value", "value_type": "f64" },
    { "op": "add_property", "name": "signed_on", "value_type": "date_time" },
    { "op": "add_property", "name": "text", "value_type": "text" }
  ]
}
```

### Life sciences

```json title="schema.json"
{
  "entity_types": ["Batch", "Lot", "Document", "Section"],
  "relation_types": [
    { "name": "MADE_FROM", "source": "Batch", "target": "Lot" },
    { "name": "PART_OF", "source": "Section", "target": "Document" },
    { "name": "COVERS", "source": "Section", "target": "Lot" }
  ]
}
```

```json title="fields.json"
{
  "ops": [
    { "op": "add_property", "name": "quantity", "value_type": "i64" },
    { "op": "add_property", "name": "made_on", "value_type": "date_time" },
    { "op": "add_property", "name": "text", "value_type": "text" }
  ]
}
```

### Security

```json title="schema.json"
{
  "entity_types": ["Service", "Library", "Document", "Section"],
  "relation_types": [
    { "name": "DEPENDS_ON", "source": "Service", "target": "Library" },
    { "name": "PART_OF", "source": "Section", "target": "Document" },
    { "name": "COVERS", "source": "Section", "target": "Library" }
  ]
}
```

```json title="fields.json"
{
  "ops": [
    { "op": "add_property", "name": "team", "value_type": "keyword" },
    { "op": "add_property", "name": "version", "value_type": "keyword" },
    { "op": "add_property", "name": "text", "value_type": "text" }
  ]
}
```

`define` creates the graph with your types and links. `evolve` then adds the
fields. `merge_default: false` leaves out the built-in starter types.

### TypeScript

```ts
await lbb.ontology.define({
  format: "spec",
  merge_default: false,
  source: readFileSync("schema.json", "utf8"),
});
await lbb.ontology.evolve(read("fields.json"));
```

### Python

```python
with open("schema.json") as f:
    lbb.ontology.define({"format": "spec", "merge_default": False, "source": f.read()})
lbb.ontology.evolve(read("fields.json"))
```

### cURL

```sh
jq -n --rawfile source schema.json '{format: "spec", merge_default: false, source: $source}' |
  curl --fail-with-body -sS -X POST "$LBB_URL/v1/ontology/define?graph=kb" \
    -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" -d @-

curl --fail-with-body -sS -X POST "$LBB_URL/v1/ontology/evolve?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" \
  -d @fields.json
```

Both calls are safe to repeat. If you write before you declare a schema, the
graph gets a small starter schema. Your own types are then refused.

## 2. Load a database

One rule maps each table to the graph:

| In your database | In the graph |
| --- | --- |
| A table | A type |
| A row | A record. The row's id is its `key`. A readable column is its `name`. |
| A foreign key | A link |
| A join table | One link per row |
| A column | A field |

A short script reads the rows and writes this JSON. Here are the tables and the
JSON they become:

### Support

Table `tickets`:

| id | title | bug_id | priority | opened_at |
| --- | --- | --- | --- | --- |
| 4821 | login fails after 3.1 | AUTH-112 | high | 2026-09-02T09:15:00Z |
| 4822 | invoice PDF is blank | PDF-31 | medium | 2026-09-03T14:40:00Z |

Table `bugs`:

| id | title |
| --- | --- |
| AUTH-112 | session token expiry |
| PDF-31 | PDF fonts missing |

```json title="records.json"
{
  "triplets": [
    {
      "source": { "type": "Ticket", "key": "4821", "name": "login fails after 3.1" },
      "relation": "REPORTED_AS",
      "target": { "type": "Bug", "key": "AUTH-112", "name": "session token expiry" }
    },
    {
      "source": { "type": "Ticket", "key": "4822", "name": "invoice PDF is blank" },
      "relation": "REPORTED_AS",
      "target": { "type": "Bug", "key": "PDF-31", "name": "PDF fonts missing" }
    }
  ],
  "entity_properties": [
    {
      "type": "Ticket",
      "key": "4821",
      "name": "login fails after 3.1",
      "properties": { "priority": "high", "opened_at": "2026-09-02T09:15:00Z" }
    },
    {
      "type": "Ticket",
      "key": "4822",
      "name": "invoice PDF is blank",
      "properties": { "priority": "medium", "opened_at": "2026-09-03T14:40:00Z" }
    }
  ]
}
```

### Accounting

Table `journal_entries`:

| id | memo | account_id | amount | booked_on |
| --- | --- | --- | --- | --- |
| JE-1042 | office chairs | 6815 | 1240.00 | 2026-09-12T00:00:00Z |
| JE-1043 | design software | 4400 | 960.00 | 2026-09-15T00:00:00Z |

Table `accounts`:

| id | name |
| --- | --- |
| 6815 | Low-value office equipment |
| 4400 | Software subscriptions |

```json title="records.json"
{
  "triplets": [
    {
      "source": { "type": "JournalEntry", "key": "JE-1042", "name": "JE-1042: office chairs" },
      "relation": "BOOKED_TO",
      "target": { "type": "Account", "key": "6815", "name": "Low-value office equipment" }
    },
    {
      "source": { "type": "JournalEntry", "key": "JE-1043", "name": "JE-1043: design software" },
      "relation": "BOOKED_TO",
      "target": { "type": "Account", "key": "4400", "name": "Software subscriptions" }
    }
  ],
  "entity_properties": [
    {
      "type": "JournalEntry",
      "key": "JE-1042",
      "name": "JE-1042: office chairs",
      "properties": { "amount": 1240.0, "booked_on": "2026-09-12T00:00:00Z" }
    },
    {
      "type": "JournalEntry",
      "key": "JE-1043",
      "name": "JE-1043: design software",
      "properties": { "amount": 960.0, "booked_on": "2026-09-15T00:00:00Z" }
    }
  ]
}
```

### Legal

Table `contracts`:

| id | title | party_id | value | signed_on |
| --- | --- | --- | --- | --- |
| MSA-204 | master services agreement | P-17 | 480000.00 | 2025-03-01T00:00:00Z |
| NDA-311 | mutual NDA | P-22 | 0.00 | 2026-01-15T00:00:00Z |

Table `parties`:

| id | name |
| --- | --- |
| P-17 | Meridian Cloud |
| P-22 | Harbor Analytics |

```json title="records.json"
{
  "triplets": [
    {
      "source": {
        "type": "Contract",
        "key": "MSA-204",
        "name": "MSA-204: master services agreement"
      },
      "relation": "HAS_PARTY",
      "target": { "type": "Party", "key": "P-17", "name": "Meridian Cloud" }
    },
    {
      "source": { "type": "Contract", "key": "NDA-311", "name": "NDA-311: mutual NDA" },
      "relation": "HAS_PARTY",
      "target": { "type": "Party", "key": "P-22", "name": "Harbor Analytics" }
    }
  ],
  "entity_properties": [
    {
      "type": "Contract",
      "key": "MSA-204",
      "name": "MSA-204: master services agreement",
      "properties": { "value": 480000.0, "signed_on": "2025-03-01T00:00:00Z" }
    },
    {
      "type": "Contract",
      "key": "NDA-311",
      "name": "NDA-311: mutual NDA",
      "properties": { "value": 0.0, "signed_on": "2026-01-15T00:00:00Z" }
    }
  ]
}
```

### Life sciences

Table `batches`:

| id | lot_id | quantity | made_on |
| --- | --- | --- | --- |
| CVX-A17 | 73091 | 120000 | 2026-08-04T00:00:00Z |
| CVX-A18 | 73112 | 118500 | 2026-08-11T00:00:00Z |

Table `lots`:

| id | name |
| --- | --- |
| 73091 | Lot 73091 |
| 73112 | Lot 73112 |

```json title="records.json"
{
  "triplets": [
    {
      "source": { "type": "Batch", "key": "CVX-A17", "name": "Batch CVX-A17" },
      "relation": "MADE_FROM",
      "target": { "type": "Lot", "key": "73091", "name": "Lot 73091" }
    },
    {
      "source": { "type": "Batch", "key": "CVX-A18", "name": "Batch CVX-A18" },
      "relation": "MADE_FROM",
      "target": { "type": "Lot", "key": "73112", "name": "Lot 73112" }
    }
  ],
  "entity_properties": [
    {
      "type": "Batch",
      "key": "CVX-A17",
      "name": "Batch CVX-A17",
      "properties": { "quantity": 120000, "made_on": "2026-08-04T00:00:00Z" }
    },
    {
      "type": "Batch",
      "key": "CVX-A18",
      "name": "Batch CVX-A18",
      "properties": { "quantity": 118500, "made_on": "2026-08-11T00:00:00Z" }
    }
  ]
}
```

### Security

Table `services`:

| id | name | team |
| --- | --- | --- |
| svc-checkout | Checkout API | payments |
| svc-search | Search API | discovery |

Table `libraries`:

| id | version |
| --- | --- |
| log4j-core | 2.14.1 |
| jackson-databind | 2.15.2 |

Table `service_libraries`:

| service_id | library_id |
| --- | --- |
| svc-checkout | log4j-core |
| svc-search | jackson-databind |

```json title="records.json"
{
  "triplets": [
    {
      "source": { "type": "Service", "key": "svc-checkout", "name": "Checkout API" },
      "relation": "DEPENDS_ON",
      "target": { "type": "Library", "key": "log4j-core", "name": "log4j-core" }
    },
    {
      "source": { "type": "Service", "key": "svc-search", "name": "Search API" },
      "relation": "DEPENDS_ON",
      "target": { "type": "Library", "key": "jackson-databind", "name": "jackson-databind" }
    }
  ],
  "entity_properties": [
    {
      "type": "Service",
      "key": "svc-checkout",
      "name": "Checkout API",
      "properties": { "team": "payments" }
    },
    {
      "type": "Service",
      "key": "svc-search",
      "name": "Search API",
      "properties": { "team": "discovery" }
    },
    {
      "type": "Library",
      "key": "log4j-core",
      "name": "log4j-core",
      "properties": { "version": "2.14.1" }
    },
    {
      "type": "Library",
      "key": "jackson-databind",
      "name": "jackson-databind",
      "properties": { "version": "2.15.2" }
    }
  ]
}
```

`triplets` holds the links. `entity_properties` holds the field values. Send
the file as one commit:

### TypeScript

```ts
const records = await lbb.graph("kb").facts.create(read("records.json"), {
  idempotencyKey: "kb-records-1",
});
console.log("commit", records.commit_seq);
```

### Python

```python
records = lbb.graph("kb").facts.create(read("records.json"), idempotency_key="kb-records-1")
print("commit", records["commit_seq"])
```

### cURL

```sh
curl --fail-with-body -sS -X POST "$LBB_URL/v1/graph/commit?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: kb-records-1" -d @records.json
```

The idempotency key makes a retry safe. Use a new key for new data. For a table
with many rows, stream the rows through an
[import job](https://docs.littlebigbrain.com/sdks/http-api/#bulk-ndjson-ingest).

## 3. Load documents

You split each document at its headings. Each section becomes a `Section`
record. Its `text` field holds the words, and a `PART_OF` link names its
document.

Export PDFs and wiki pages to text first. Keep each section to a few
paragraphs. Give each section a stable `key`, such as the document id plus the
section number. A later load with the same key updates the section.

### Support

```md title="Release notes 3.2"
## Sign-in fixes

Sessions no longer expire right after login. People who were sent back to the login screen after the 3.1 upgrade can sign in again. This fixes AUTH-112.

## Faster exports

CSV exports now run on several threads. An export of 50,000 rows finishes in under ten seconds.
```

```json title="sections.json"
{
  "triplets": [
    {
      "source": { "type": "Section", "key": "release-notes-3.2#1", "name": "Sign-in fixes" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "release-notes-3.2", "name": "Release notes 3.2" }
    },
    {
      "source": { "type": "Section", "key": "release-notes-3.2#2", "name": "Faster exports" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "release-notes-3.2", "name": "Release notes 3.2" }
    }
  ],
  "entity_properties": [
    {
      "type": "Section",
      "key": "release-notes-3.2#1",
      "name": "Sign-in fixes",
      "properties": {
        "text": "Sessions no longer expire right after login. People who were sent back to the login screen after the 3.1 upgrade can sign in again. This fixes AUTH-112."
      }
    },
    {
      "type": "Section",
      "key": "release-notes-3.2#2",
      "name": "Faster exports",
      "properties": {
        "text": "CSV exports now run on several threads. An export of 50,000 rows finishes in under ten seconds."
      }
    }
  ]
}
```

### Accounting

```md title="Accounting manual"
## 3.2 Low-value equipment

Book office equipment that costs less than 800 EUR per item to account 6815. Expense it in the month of purchase. Keep the receipt with the entry.

## 3.4 Software

Book software subscriptions to account 4400. Spread an annual plan over twelve months.
```

```json title="sections.json"
{
  "triplets": [
    {
      "source": {
        "type": "Section",
        "key": "accounting-manual#3.2",
        "name": "3.2 Low-value equipment"
      },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "accounting-manual", "name": "Accounting manual" }
    },
    {
      "source": { "type": "Section", "key": "accounting-manual#3.4", "name": "3.4 Software" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "accounting-manual", "name": "Accounting manual" }
    }
  ],
  "entity_properties": [
    {
      "type": "Section",
      "key": "accounting-manual#3.2",
      "name": "3.2 Low-value equipment",
      "properties": {
        "text": "Book office equipment that costs less than 800 EUR per item to account 6815. Expense it in the month of purchase. Keep the receipt with the entry."
      }
    },
    {
      "type": "Section",
      "key": "accounting-manual#3.4",
      "name": "3.4 Software",
      "properties": {
        "text": "Book software subscriptions to account 4400. Spread an annual plan over twelve months."
      }
    }
  ]
}
```

### Legal

```md title="MSA-204 signed copy"
## 14. Assignment

Neither party may assign this Master Services Agreement MSA-204 without the other party's written consent. A merger or a change of control counts as an assignment.

## 15. Term

This agreement runs for three years. It renews for one year at a time.
```

```json title="sections.json"
{
  "triplets": [
    {
      "source": { "type": "Section", "key": "MSA-204.pdf#14", "name": "14. Assignment" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "MSA-204.pdf", "name": "MSA-204 signed copy" }
    },
    {
      "source": { "type": "Section", "key": "MSA-204.pdf#15", "name": "15. Term" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "MSA-204.pdf", "name": "MSA-204 signed copy" }
    }
  ],
  "entity_properties": [
    {
      "type": "Section",
      "key": "MSA-204.pdf#14",
      "name": "14. Assignment",
      "properties": {
        "text": "Neither party may assign this Master Services Agreement MSA-204 without the other party's written consent. A merger or a change of control counts as an assignment."
      }
    },
    {
      "type": "Section",
      "key": "MSA-204.pdf#15",
      "name": "15. Term",
      "properties": {
        "text": "This agreement runs for three years. It renews for one year at a time."
      }
    }
  ]
}
```

### Life sciences

```md title="Deviation report DR-77"
## 2. Finding

Lot 73091 failed the impurity test on 2 September. The level was above the release limit. Hold every batch made from this lot.

## 3. Action

Quality assurance quarantined the remaining stock and informed the supplier.
```

```json title="sections.json"
{
  "triplets": [
    {
      "source": { "type": "Section", "key": "DR-77#2", "name": "2. Finding" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "DR-77", "name": "Deviation report DR-77" }
    },
    {
      "source": { "type": "Section", "key": "DR-77#3", "name": "3. Action" },
      "relation": "PART_OF",
      "target": { "type": "Document", "key": "DR-77", "name": "Deviation report DR-77" }
    }
  ],
  "entity_properties": [
    {
      "type": "Section",
      "key": "DR-77#2",
      "name": "2. Finding",
      "properties": {
        "text": "Lot 73091 failed the impurity test on 2 September. The level was above the release limit. Hold every batch made from this lot."
      }
    },
    {
      "type": "Section",
      "key": "DR-77#3",
      "name": "3. Action",
      "properties": {
        "text": "Quality assurance quarantined the remaining stock and informed the supplier."
      }
    }
  ]
}
```

### Security

```md title="Security advisory SA-2021-44"
## Affected versions

log4j-core versions 2.0 to 2.14.1 run lookups on logged text. An attacker can run code on the server with one crafted string.

## Fix

Upgrade to log4j-core 2.17.1 or later. Until then, remove the JndiLookup class from the classpath.
```

```json title="sections.json"
{
  "triplets": [
    {
      "source": { "type": "Section", "key": "SA-2021-44#1", "name": "Affected versions" },
      "relation": "PART_OF",
      "target": {
        "type": "Document",
        "key": "SA-2021-44",
        "name": "Security advisory SA-2021-44"
      }
    },
    {
      "source": { "type": "Section", "key": "SA-2021-44#2", "name": "Fix" },
      "relation": "PART_OF",
      "target": {
        "type": "Document",
        "key": "SA-2021-44",
        "name": "Security advisory SA-2021-44"
      }
    }
  ],
  "entity_properties": [
    {
      "type": "Section",
      "key": "SA-2021-44#1",
      "name": "Affected versions",
      "properties": {
        "text": "log4j-core versions 2.0 to 2.14.1 run lookups on logged text. An attacker can run code on the server with one crafted string."
      }
    },
    {
      "type": "Section",
      "key": "SA-2021-44#2",
      "name": "Fix",
      "properties": {
        "text": "Upgrade to log4j-core 2.17.1 or later. Until then, remove the JndiLookup class from the classpath."
      }
    }
  ]
}
```

Send the file with the same code as step 2 and a new idempotency key:

### TypeScript

```ts
const sections = await lbb.graph("kb").facts.create(read("sections.json"), {
  idempotencyKey: "kb-sections-1",
});
```

### Python

```python
sections = lbb.graph("kb").facts.create(read("sections.json"), idempotency_key="kb-sections-1")
```

### cURL

```sh
curl --fail-with-body -sS -X POST "$LBB_URL/v1/graph/commit?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: kb-sections-1" -d @sections.json
```

## 4. Turn on search

Declare an embedding on the `Section` type, from its name and its text. The
platform embeds each section and keeps the vectors current. `label` is the
record's `name`.

An embedding reads published data. So the code first waits for the sections to
publish. The first build then runs in the background, until the embedding
reports `serving`.

### TypeScript

```ts
await lbb.waitForPublished(sections.commit_seq);

await lbb.embeddings.declare({
  class: "https://littlebigbrain.com/class/section",
  from: ["label", "text"],
});
while (!(await lbb.embeddings.get("section")).serving) {
  await new Promise((resolve) => setTimeout(resolve, 2_000));
}

const found = await lbb.embeddings.search(read("search.json"));
for (const hit of found.hits) console.log(hit.label);
```

### Python

```python
lbb.wait_for_published(sections["commit_seq"])

lbb.embeddings.declare("https://littlebigbrain.com/class/section", from_=["label", "text"])
while not lbb.embeddings.get("section").get("serving"):
    time.sleep(2)

found = lbb.embeddings.search(**read("search.json"))
for hit in found["hits"]:
    print(hit["label"])
```

### cURL

```sh
until curl -sS "$LBB_URL/v1/graph/publication-status?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" | grep -q '"state": *"current"'; do sleep 1; done

curl --fail-with-body -sS -X PUT "$LBB_URL/v1/embeddings?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" \
  -d '{"class": "https://littlebigbrain.com/class/section", "from": ["label", "text"]}'

until curl -sS "$LBB_URL/v1/embeddings?graph=kb&name=section" \
  -H "Authorization: Bearer $LBB_API_KEY" | grep -q '"serving": *{'; do sleep 2; done

curl --fail-with-body -sS -X POST "$LBB_URL/v1/search?graph=kb" \
  -H "Authorization: Bearer $LBB_API_KEY" -H "Content-Type: application/json" \
  -d @search.json | jq -r '.hits[].label'
```

`search.json` asks the question in your own words:

### Support

```json title="search.json"
{ "text": "users cannot sign in after the upgrade", "top_k": 3 }
```

The first hit is `Sign-in fixes`.

### Accounting

```json title="search.json"
{ "text": "where do I book a cheap office chair", "top_k": 3 }
```

The first hit is `3.2 Low-value equipment`.

### Legal

```json title="search.json"
{ "text": "who must consent before a merger", "top_k": 3 }
```

The first hit is `14. Assignment`.

### Life sciences

```json title="search.json"
{ "text": "which lot failed quality testing", "top_k": 3 }
```

The first hit is `2. Finding`.

### Security

```json title="search.json"
{ "text": "an attacker runs code through log messages", "top_k": 3 }
```

The first hit is `Affected versions`.

A declaration spends model tokens. To preview the cost, see
[Search by meaning](https://docs.littlebigbrain.com/guides/search-by-meaning/). It also shows how to filter
hits by type or by link.

## 5. Connect the two sources

The records and the sections are in one graph now. No link joins them yet.
The `COVERS` link says which record a section is about.

An LLM can read each section and write those links for you. The guide
[Add links with an LLM](https://docs.littlebigbrain.com/guides/add-links-with-an-llm/) continues with the same
graph and files.

## What the schema holds

| Part | Example | Required |
| --- | --- | --- |
| Types | `Customer`, `Order` | At least one |
| Links, with the types they connect | `PLACED`, from `Customer` to `Order` | At least one |
| Fields, with a value type | `total` (number), `placed_at` (date and time) | No, but a field must be declared before a write uses it |
| Parent types | `VipCustomer` is a kind of `Customer` | No |
| Allowed values | `status` is `open` or `closed` | No |
| A reverse name for a link | `PLACED_BY` for `PLACED` | No |

Each field has a value type:

| Value | `value_type` |
| --- | --- |
| Free text | `text` |
| A code or label, such as a status | `keyword` |
| A whole number | `i64` |
| A decimal number | `f64` |
| True or false | `bool` |
| A date and time | `date_time` |

The API calls the schema the **ontology**. The
[HTTP API](https://docs.littlebigbrain.com/sdks/http-api/#evolve-the-schema) lists the other value types.

## Change the schema later

Your data will grow. The schema grows with it through `evolve`:

| To add | `op` |
| --- | --- |
| A type | `add_entity_type` |
| A link | `add_relation` |
| A field | `add_property` |
| A parent type | `add_super_types` |
| More types to an existing link | `widen_relation` |

Additions are always safe. A change that removes or narrows something is
blocked while stored data uses it. Add `?dry_run=true` to preview a change and
the records it affects. The [HTTP API](https://docs.littlebigbrain.com/sdks/http-api/#evolve-the-schema) lists
every operation.

You can also call `define` again with a larger document. It adds what is new.
It refuses a document that leaves out a type or link the graph has. That error
is `400 ontology_identity_breaking_change`.

## Start from an ontology you have

Skip this section if you have no schema file yet. Some teams already have their
schema as an OWL file. OWL is a standard format for schemas.

`define` reads such a file in Turtle, JSON-LD, RDF/XML or CSV. Set `format` to
`turtle`, `json_ld`, `rdf_xml` or `csv`, and pass the file as `source`.

```turtle
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <https://example.org/shop#> .

ex:Customer a owl:Class .
ex:Order a owl:Class .
ex:VipCustomer a owl:Class ; rdfs:subClassOf ex:Customer .
ex:placed a owl:ObjectProperty ; rdfs:domain ex:Customer ; rdfs:range ex:Order .
ex:total a owl:DatatypeProperty ; rdfs:domain ex:Order ; rdfs:range xsd:decimal .
```

| In the OWL file | Becomes |
| --- | --- |
| `owl:Class` | A type: `Customer`, `Order`, `VipCustomer` |
| `rdfs:subClassOf` | A parent type: a `VipCustomer` is a `Customer` |
| `owl:ObjectProperty` | A link: `PLACED` |
| `owl:DatatypeProperty` | A field: `total`, a decimal number |

Link names become upper case. Writes match type and link names in any case.
So `placed` and `PLACED` name the same link.

## Load RDF files

RDF files need no schema. An RDF import stores each statement as written, and
your queries use your own predicates. You can add rules and reasoning later.

The first write to a graph decides its write model:

- **RDF first.** The graph becomes RDF-native. It takes RDF import and SPARQL
  `INSERT DATA` from then on. A JSON write is refused with
  `400 rdf_native_graph`.
- **JSON records first.** The graph keeps JSON writes, and it also takes RDF
  import.

So to mix both formats in one graph, write the JSON records first.
[Load RDF data](https://docs.littlebigbrain.com/guides/load-rdf/) covers formats, large files and limits.

## Load a lot of data

- **JSON records.** Stream newline-delimited JSON through an
  [import job](https://docs.littlebigbrain.com/sdks/http-api/#bulk-ndjson-ingest). The same schema rules apply,
  so declare the schema first. A job that meets an unknown type fails with
  `invalid_import`.
- **RDF.** Send the file in chunks. See
  [Load a large dataset](https://docs.littlebigbrain.com/guides/load-rdf/#load-a-large-dataset).

## Common errors

| Error | What to do |
| --- | --- |
| `unknown entity type 'Customer'` | Declare the type, with `define` or with the `evolve` op `add_entity_type`. |
| `unknown relation 'RETURNED'` | Declare the link with the `evolve` op `add_relation`. |
| `unknown property field 'priority'; declare it first` | Declare the field with the `evolve` op `add_property`. |
| `property 'total' is declared F64 but its value is not a number` | Send a value of the declared type. |
| `graph_not_found` from `evolve` | Call `define` first. It creates the graph. |
| `ontology spec requires at least one named relation type` | Add at least one link to the `define` document. |
| `400 ontology_identity_breaking_change` | Keep the existing types and links in the document, or use `evolve`. |
| `400` with `no predicate named text` when you declare an embedding | The sections are not published yet. Wait for them, then declare again. |
| `409 embedding_not_ready` from a search | The first build is still running. Wait until the embedding reports `serving`. |
| `400 rdf_native_graph` | The graph was first written with RDF. Use RDF import, or load the JSON records into a new graph. |
| An import job fails with `invalid_import` | Read the job's `error.message`, fix the schema or the data, then submit the job again with a new idempotency key. |

## Related

- [Quickstart](https://docs.littlebigbrain.com/start/quickstart/): one worked example, from the first write to a cited answer.
- [Add links with an LLM](https://docs.littlebigbrain.com/guides/add-links-with-an-llm/): join your records and your documents.
- [Build with JSON records](https://docs.littlebigbrain.com/guides/without-rdf/): allowed values, parent types and rules.
- [Core concepts](https://docs.littlebigbrain.com/start/concepts/): records, types, links and commits.
