> ## Documentation Index
> Fetch the complete documentation index at: https://ahvn.top/llms.txt
> Use this file to discover all available pages before exploring further.

# Entities

> Logical schemas with typed fields, stable identities, defaults, compute Hooks, and JSON definitions.

<Note>
  *Entities say what the data is before anyone asks where it lives.*
</Note>

<br />

## 1. Define a Logical Shape

Entities are Python classes that extend `hb.Entity`. Use `hb.field(...)` when you want metadata, defaults, storage placement, or compute Hooks; use annotations for the shortest plain-field form.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import heavenbase as hb


class Document(hb.Entity):
    title = hb.field(hb.ShortText).desc("Display title")
    body = hb.field(hb.LongText)
    tags = hb.field(hb.Array[hb.ShortText]).default([])
    embedding = hb.field(hb.Vector[2])


print(Document.schema().entity_id)
```

The class above creates a logical entity named `document`. HeavenBase also injects `object_id` and `name` when you do not define your own `object_id`.

<br />

## 2. Choose Logical Types

Logical types describe meaning. Backends decide how those meanings are physically stored.

| Type                                  | Use it for                                         |
| ------------------------------------- | -------------------------------------------------- |
| `Identifier`                          | Stable IDs and slugs.                              |
| `ShortText`, `MediumText`, `LongText` | Labels, descriptions, and full documents.          |
| `Integer`, `Float`, `Boolean`         | Scalar values.                                     |
| `Categorical([...])`                  | String or integer values limited to known choices. |
| `Timestamp(unit="ms")`, `Date()`      | Instants and calendar dates.                       |
| `Array[...]`                          | Lists with an optional item type.                  |
| `Vector[dim]`                         | Embeddings and other numeric vectors.              |
| `Json`                                | JSON-compatible objects.                           |
| `HyperG`                              | Repeatable relation-like records.                  |
| `Artifact`                            | Binary payloads.                                   |

<Note>
  HeavenBase does not expose separate `Datetime` or `Interval` logical types yet. Use `Timestamp` for instants, `Date` for calendar dates, and a numeric field with a unit in its description for durations.
</Note>

<br />

## 3. Understand Identity

Every entity row has exactly one user-facing `object_id`. If you omit it, HeavenBase uses the row's `name` to derive a deterministic ID.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
ws = hb.HeavenBase("core-entities", preset="debug")
ws.register(Document)

first_id = ws.upsert(
    Document,
    {
        "name": "Agent guide",
        "title": "Agent guide",
        "body": "Use Catalog for objects and MetaSchema for structure.",
        "tags": ["docs"],
        "embedding": [1.0, 0.0],
    },
)

second_id = ws.upsert(
    Document,
    {
        "name": "Agent guide",
        "title": "Agent guide",
        "body": "Updated body.",
        "tags": ["docs"],
        "embedding": [1.0, 0.0],
    },
)

print(first_id == second_id)
```

Define `object_id` yourself only when a different natural key should drive identity.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from heavenbase.utils import hash_id


def stock_id(sku: str, warehouse: str) -> str:
    return hash_id("StockItem", sku, warehouse)


class StockItem(hb.Entity):
    object_id = hb.field(hb.Identifier).compute(stock_id, inputs=["sku", "warehouse"])
    sku = hb.field(hb.ShortText)
    warehouse = hb.field(hb.ShortText)
    quantity = hb.field(hb.Integer).default(0)


ws.register(StockItem)
stock_id_value = ws.upsert(StockItem, {"sku": "SKU-001", "warehouse": "east"})
print(ws.get(stock_id_value, entity=StockItem)["quantity"])
```

<br />

## 4. Add Compute Hooks

Computed fields run when rows are written. Query-compute Hooks run when a query value needs to be normalized before dispatch. The common case is accepting a text query for a vector field.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def embed_text(text: str) -> list[float]:
    return [1.0, 0.0] if "agent" in text.lower() else [0.0, 1.0]


class Issue(hb.Entity):
    title = hb.field(hb.ShortText)
    summary = hb.field(hb.LongText)
    emb = (
        hb.field(hb.Vector[2])
        .compute(embed_text, inputs=["summary"])
        .query_compute(embed_text)
    )


ws.register(Issue)
ws.upsert(Issue, {"object_id": "i1", "title": "Agent memory", "summary": "Design agent memory around search."})

frame = ws.query(Issue).near(Issue.emb, "agent search", top_k=1).select("title", "score").execute()
print(frame.rows()[0]["title"])
```

<br />

## 5. Compile from JSON

Agents and external clients can define entities with JSON-compatible schema dictionaries.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
Product = hb.Entity.from_schema(
    {
        "entity_id": "product",
        "name": "Product",
        "fields": {
            "name": "ShortText",
            "price": "Float",
            "status": {
                "type": {
                    "type": "categorical",
                    "values": ["draft", "active"],
                },
            },
            "created_at": {"type": {"type": "timestamp", "unit": "ms"}},
            "available_on": "Date",
        },
    }
)

print(Product.schema().entity_id)
```

The generated class behaves like a normal `hb.Entity` subclass and can be passed to `ws.register(...)`, `ws.upsert(...)`, and `ws.query(...)`.

<br />

## Further Exploration

<Tip>
  **Related resources:**

  * [Workspace](/features/workspace) - Register entities in an application boundary
  * [Routing](/features/routing) - Store fields on selected backends
  * [Query](/features/query) - Filter, search, and inspect entity rows
  * [Catalog](/features/catalog) - Discover concrete objects after writes
</Tip>

<br />
