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

# Query

> Typed and JSON query surfaces with backend routing, ResultFrame output, and explain diagnostics.

<Note>
  *Ask once. HeavenBase figures out who should answer.*
</Note>

<br />

## 1. Build a Query

Start from `ws.query(Entity)`, then add filters, vector search, projection, ordering, pagination, and execution.

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


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


ws = hb.HeavenBase("core-query", preset="debug")
ws.register(Article)
ws.upsert_many(
    Article,
    [
        {"object_id": "a1", "name": "Agent memory", "title": "Agent memory", "body": "Agents need searchable memory.", "tags": ["agent"], "embedding": [1.0, 0.0]},
        {"object_id": "a2", "name": "Backend plan", "title": "Backend plan", "body": "Backends store physical fields.", "tags": ["storage"], "embedding": [0.0, 1.0]},
    ],
)

frame = (
    ws.query(Article)
    .where(Article.body.match("agent"))
    .near(Article.embedding, [1.0, 0.0], top_k=3)
    .select("title", "score")
    .limit(2)
    .execute()
)

print(frame.rows())
```

`execute()` returns a `ResultFrame`, not a backend cursor.

<br />

## 2. Read a ResultFrame

`ResultFrame` keeps `object_id` even when you project a small column set.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
rows = frame.rows()
print(rows[0]["object_id"])
print(frame.ids)
print(frame.col_names())
```

This is intentional for agents. They can display compact rows, then call `get`, `set`, `delete`, `query`, or `explain` later with the same identity.

<br />

## 3. Use Filters

Field references build typed filter expressions. You can combine them with `&`, `|`, and `~`.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
not_storage = ws.query(Article).where(Article.title != "Backend plan").execute()
tagged = ws.query(Article).where(Article.tags.array_contains("agent")).execute()
combined = ws.query(Article).where((Article.body.match("agent")) & (Article.title != "Draft")).execute()

print(len(not_storage), len(tagged), len(combined))
```

Use `query_json` when an agent or external client needs a serializable query spec.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
json_frame = ws.query_json(
    Article,
    {
        "filter": {
            "body": {"$match": "agent"},
            "tags": {"$array_contains": "agent"},
        },
        "select": ["title"],
        "limit": 5,
    },
).execute()

print(json_frame.rows())
```

<br />

## 4. Mongo-Style JSON Queries

`where()` also accepts a Mongo-style filter dictionary. Operator keys use a `$` prefix, a bare value means `$eq`, and `$and`, `$or`, and `$not` combine clauses.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
mongo_frame = (
    ws.query(Article)
    .where(
        {
            "$and": [
                {"body": {"$match": "agent"}},
                {"title": {"$in": ["Agent memory", "Backend plan"]}},
            ]
        }
    )
    .execute()
)

print(mongo_frame.rows())
```

Supported `$` operators map to the registered logical operations: `$eq`, `$ne`, `$lt`, `$lte`, `$gt`, `$gte`, `$in`, `$match`, `$like`, `$ilike`, `$wildcard`, `$regex`, `$contains`, `$array_contains`, `$exists`, and `$all`. On Array fields, `$contains` normalizes to `array_contains` automatically. The same filter shape works as the `filter` key of a `query_json` spec.

<br />

## 5. Inspect an Explain Plan

Call `explain()` before execution when you need routing diagnostics.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
plan = ws.query(Article).where(Article.body.match("agent")).explain()
print(plan["steps"][0]["backend"])
print(plan["steps"][0]["handler_mode"])
```

The plan reports storage placement, selected backend, strategy, handler kind, handler mode, and unsupported reasons for fallback paths.

<br />

## 6. Mutate Rows Outside Query

Query reads rows. Use CRUD methods for writes, updates, and deletes.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
object_id = ws.upsert(
    Article,
    {
        "name": "Query note",
        "title": "Query note",
        "body": "Keep the returned object_id.",
        "tags": ["docs"],
        "embedding": [1.0, 0.0],
    },
)

ws.set(object_id, {"title": "Updated query note"}, entity=Article)
print(ws.get(object_id, entity=Article)["title"])
print(ws.delete((Article, object_id)))
```

Pass `entity=...` when an `object_id` could exist under more than one entity type.

<br />

## Further Exploration

<Tip>
  **Related resources:**

  * [Entities](/features/entities) - Define queryable fields
  * [Routing](/features/routing) - See how query fragments dispatch
  * [Catalog](/features/catalog) - Discover objects before typed queries
  * [HeavenBase MCP](/quickstart/heavenbase-mcp) - Expose queries to agents
</Tip>

<br />
