query_memory_graph
Run a graph query, in a Cypher subset, over the caller's own memory graph.
Use this when the user wants a structured traversal — shared-entity counts,
paths between events, tag membership — that a text query cannot express. Do
not use it for relational table facts; use `query_memories_with_sql` for that.
Do not use it when the starting point is a sentence and no pattern is stated;
use `find_memory_connections` for that.
Read-only.
The subset: a query opens with `START a = events(text: "..." | entity: "name
or id" | ids: [...], k: n)`, so the anchor set is always events. `k` defaults
to 5 and by default goes up to 20, or 200 with `entity:` alone. Events reach
events through `SHARES` (shared entities), `SIMILAR` and `FAR` (vector),
`NEXT`, and `RESOLVED_BY`, which runs `(plan)-[:RESOLVED_BY]->(evidence)`
from a plan or expectation to the memory that realized it and carries
`r.relation` and `r.asserted_at`; revoked links never appear. Events reach
entities through `PARTICIPATED_IN` and tags through `MEMBER_OF`; entities
reach entities through `CONNECTED` (stored co-occurrence) and `SAME_AS`
(stored alias). There is no `WITH` — conditions go in relation parameters
such as `{min: 3}`. The rest is ordinary Cypher: `MATCH`, `WHERE`,
`RETURN [DISTINCT]`, `ORDER BY`, `LIMIT`, aggregates in `RETURN`, `$name`
parameters, and `cos(a, b)`. Without `LIMIT`, rows stop at 20; `LIMIT` goes
up to 200.
Name match: `WHERE e.name CONTAINS $part` keeps the rows whose `Entity.name`
or `Tag.name` holds that piece, ignoring letter case and full-width forms. A
piece that is all ASCII needs two characters; one Hangul or Han character is
enough. It runs on the rows the walk already returned, so when
`truncated` is non-empty an empty result means the piece was not seen in what
was reached, not that no such name exists.
Scoped start: `events(entity: $name, text: $q, k: n)` ranks, by closeness to
`$q`, just the events that entity takes part in; `tag: $t, text: $q` does the
same inside a tag, and `tag:` alone gives its newest events. A list,
`entity: ["A", "B"]`, keeps events with any of the names; `entity_all:` or
`tag_all:` keeps events with all of them; `entity:` with `tag:` keeps events
matching both. Every part must hold and any name holds a part; there are no
brackets, no negation and no entity-or-tag choice. Up to 10 names in all;
`start.unresolved` lists the `{kind, name}` not found, and a part with no
name found gives no rows. The combined starts take `k` up to 20 and `tag:`
alone up to 200 by default; a server can set other caps, and `budget.caps`
in the response shows the ones in force. Names match exactly, up to 20
entities or tags; `start.entities_truncated` or `tags_truncated` says more
matched. `a.score` is the start event's cosine to
the start text, null when there is no text and on events reached by a hop;
nulls sort last, so use `ORDER BY a.score DESC, a.id` for a stable order.
`start.scoped_mode` is `exact` or `filtered_ann` (a large scope searched
through the vector index). `exact` measures every candidate's distance but
returns only the top `k`; `start.has_more: true` says the scope holds more
candidates than came back. A scope not fully covered (`filled: false` or a
names cap) adds a `stage: start` entry to `truncated` and sets `partial`
whatever the row count; 0 rows then means not confirmed, not absent.
`MATCH` can be left out when the start itself is the answer: `START a =
events(entity: $name, text: $q, k: 20) RETURN a, a.score ORDER BY a.score
DESC, a.id`. An arrow left off a directed relation is filled in, with a
line in `notes`, when the end labels allow one way only, as
`(a)-[:MEMBER_OF]-(t)` from an event reads `->`; `NEXT` and `RESOLVED_BY`
join two events and still need their arrow.
Example: `START a = events(text: $q, k: 3) MATCH (a)-[:PARTICIPATED_IN]-(e)
-[:PARTICIPATED_IN]-(b) RETURN b, count(DISTINCT e) AS shared ORDER BY shared
DESC LIMIT 20`
Args:
query: The graph query text.
params: Values for `$name` references (strings, numbers, lists).
Returns:
`{columns, rows, row_count, start, partial, truncated, plan, budget,
notes}`. Rows are projected: an Event to `{id, summary, timestamp,
order_index}`, an Entity to `{id, name, type}`, a Tag to `{id, name}`.
Budget caps, including a START cut short (`stage: start`), are
reported in `truncated`, never applied silently.
Rejections come back as `{error, blocked_by: syntax|grammar|params,
hint?, allowed_*}`.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The graph query text. | |
| params | No | Values for `$name` references (strings, numbers, lists). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||