Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions pages/memgraph-zero/memgql/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,47 @@ description: MemGQL release notes

# MemGQL Changelog

## Unreleased

### ⚠️ Behavior changes

- **A vertex's or edge's `metaFields.id` is now exposed as a property.**
`RETURN n` / `RETURN r` include it, and `n.<id_column>` reads it by name —
including after a `WITH n` boundary, which previously resolved only when the
id column happened to be called `id`. This aligns relational sources with native-graph ones and
with a graph's Memgraph cache, which stores the id as its merge key.
- **The cache now answers only for properties it holds.** A fragment holds a
label's id and its declared `attributes`; a query reading anything else is a
cache miss and reads the source, so results never differ from the uncached
run. A label declaring no `attributes` still caches its ids and topology, so
traversals and graph algorithms work against it. `SHOW GRAPH CACHES` gained
`hits`, `misses` and `cached_properties` columns — enough to tell a cache that
is serving from one that never engages, and a property-driven miss from a cold
one. See
[Caching a graph in Memgraph](/memgraph-zero/memgql/multiple-graphs#caching-a-graph-in-memgraph).

### Fixed

- **ClickHouse returned result columns in alphabetical order** rather than the
order the query projected them. Column names travelled with their values, so
most clients were unaffected, but anything reading positionally was not: a
`RETURN n` whose mapping had a property sorting before `id` came back with that
property's value as the node's element id.
- **The Memgraph cache returned different rows than its source.** Cached edges
pointed at the wrong endpoints or were missing entirely, edge properties were
never copied at all, and a cache holding only a label's id answered property
reads with `NULL` instead of declining them — so a query silently changed its
answer once the cache was warm.
- **`count(DISTINCT <node>)` and `count(DISTINCT <relationship>)` produced invalid
SQL** on relational backends. Counting distinct properties was unaffected.
- **Grouping by a whole element produced invalid SQL** — `WITH n, count(…)` and
`WITH r, count(…)` on relational backends.
- **A warm table was re-scanned from the source** every time a newly-touched
edge type pulled it in as an endpoint, repeating the full scan the cache
exists to avoid.
- **The cache's warm-up log counted rows read from the source**, so it reported
success even when no rows reached the cache.

## MemGQL v0.8.0 - July 19th, 2026

### ⚠️ Breaking changes
Expand Down
27 changes: 23 additions & 4 deletions pages/memgraph-zero/memgql/multiple-graphs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -443,12 +443,26 @@ stale result.
`TTL` is in seconds; `MAX_BYTES` accepts a plain byte count or a `K`/`M`/`G`/`T`
binary suffix (`8G` = 8 × 1024³ bytes).

**The cache holds a label's id and its declared
[`attributes`](/memgraph-zero/memgql/schema-file#attributes)** — the columns the
mapping names. A query reading any other property is a cache miss and reads the
source, so results never differ from the uncached run; you lose the speed-up for
that query, not correctness. This is the same rule
[routing](#routing) applies: a property has to be declared to carry a signal.

`SHOW GRAPH CACHES` reports `hits` / `misses` per graph and a
`cached_properties` column listing what each fragment holds — between them you
can tell a cache that is serving from one that never engages, and a
property-driven miss from a cold one.

The cache engages per routed part, whether the part is pinned with `USE` or
routed by its labels:

- **First read (cache MISS)**: the query runs on the source and the engine
*tees* the scanned labels and edge types into the Memgraph cache, recording
which scopes it cached.
which scopes it cached. Scopes already resident are left alone — teeing an
edge type pulls in its endpoint labels, and those are not re-scanned if the
cache already holds them.
- **Later covered read (cache HIT)**: a query whose scans are covered by
existing cached scopes is served entirely from Memgraph, with no source I/O.
- **Capability upgrade**: because cached data is a real graph in Memgraph,
Expand All @@ -465,15 +479,20 @@ routed by its labels:

In a cross-backend query (split by explicit `USE`, or routed by label), each
cache-enabled part is served from its cache independently while the other parts
run on their own sources.
run on their own sources. `UNION` / `UNION ALL` queries are the exception: they
dispatch as a whole and never consult the cache, even when every scope they
touch is resident.

Inspect what is cached with `SHOW GRAPH CACHES` (returns each cache-enabled
graph, its cache connector, `TTL`, `MAX_BYTES`, and the number of cached
fragments). Cache activity is logged at `DEBUG` as `[CACHE] MISS … teeing …`
and `[CACHE] HIT … serving from cache connector …`.

> **Note:** The query-driven cache is currently validated with Iceberg sources;
> the mechanism generalizes to the other warehouse connectors.
> **Note:** The query-driven cache is exercised in CI against PostgreSQL, MySQL,
> DuckDB, SQL Server, ClickHouse and the native Iceberg connector, each
> replaying the same query corpus from its cache that it runs against the
> source, and asserting identical rows. Graph-native sources (Memgraph, Neo4j) cannot be cached —
> there is no relational mapping to copy from.

## Cross-Graph Query Rules

Expand Down
2 changes: 1 addition & 1 deletion pages/memgraph-zero/memgql/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ ALTER GRAPH <name> SET READ WRITE;
-- Query-driven cache
ALTER GRAPH <name> SET CACHE CONNECTOR <cache_connector> [TTL <secs>] [MAX_BYTES <n>[K|M|G|T]];
ALTER GRAPH <name> REMOVE CACHE;
SHOW GRAPH CACHES; -- graph, cache_connector, ttl_secs, max_bytes, fragment count
SHOW GRAPH CACHES; -- graph, cache_connector, ttl_secs, max_bytes, fragments, hits, misses, cached_properties
```

A connector is a **connection only**; it carries no graph shape. `GRAPH <db>`
Expand Down
11 changes: 9 additions & 2 deletions pages/memgraph-zero/memgql/schema-file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ A vertex maps a **label** to a backend source. Exactly one source kind is set:
| `mappedTableSource.connector` | relational | Connector name (alias `catalog`). |
| `mappedTableSource.schema` | relational | Optional middle namespace (PostgreSQL schema, etc.). |
| `mappedTableSource.table` | relational | The backing table. |
| `mappedTableSource.metaFields.id` | relational | Primary-key column; the node's identity and the join key for edges. |
| `mappedTableSource.metaFields.id` | relational | Primary-key column; the node's identity and the join key for edges. Also exposed as a property, so `RETURN n` includes it and `n.<id_column>` reads it by name. |
| `mappedGraphSource.connector` | native | Connector name (Memgraph / Neo4j); the query passes through as Cypher. |

### Edges
Expand Down Expand Up @@ -271,7 +271,7 @@ A relational edge adds two more `metaFields` on top of `id`:

| `metaFields` key | Description |
|------------------|-------------|
| `id` | The edge row's own key. |
| `id` | The edge row's own key. Exposed as a property, like a vertex's. |
| `from` | Foreign-key column pointing at the **source** vertex's id column. |
| `to` | Foreign-key column pointing at the **target** vertex's id column. |

Expand Down Expand Up @@ -303,6 +303,13 @@ queries by.
> type, or add an explicit `USE`, when a property isn't declared. See
> [routing](/memgraph-zero/memgql/multiple-graphs#routing).

> **Note:** [caching a graph](/memgraph-zero/memgql/multiple-graphs#caching-a-graph-in-memgraph)
> follows the same rule. The cache holds a label's id and its declared
> attributes, so a query reading an undeclared property is served from the
> source rather than from the cache. A label declaring no `attributes` still
> caches its ids and topology, which is what traversals and graph algorithms
> run on.

## Complete example

One engine over **Memgraph** (a social graph) and **PostgreSQL** (a store),
Expand Down