diff --git a/pages/memgraph-zero/memgql/changelog.mdx b/pages/memgraph-zero/memgql/changelog.mdx index 046d9f859..079ea0433 100644 --- a/pages/memgraph-zero/memgql/changelog.mdx +++ b/pages/memgraph-zero/memgql/changelog.mdx @@ -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.` 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 )` and `count(DISTINCT )` 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 diff --git a/pages/memgraph-zero/memgql/multiple-graphs.mdx b/pages/memgraph-zero/memgql/multiple-graphs.mdx index e3e151394..0c7669798 100644 --- a/pages/memgraph-zero/memgql/multiple-graphs.mdx +++ b/pages/memgraph-zero/memgql/multiple-graphs.mdx @@ -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, @@ -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 diff --git a/pages/memgraph-zero/memgql/reference.mdx b/pages/memgraph-zero/memgql/reference.mdx index 467f904bc..dab19e998 100644 --- a/pages/memgraph-zero/memgql/reference.mdx +++ b/pages/memgraph-zero/memgql/reference.mdx @@ -88,7 +88,7 @@ ALTER GRAPH SET READ WRITE; -- Query-driven cache ALTER GRAPH SET CACHE CONNECTOR [TTL ] [MAX_BYTES [K|M|G|T]]; ALTER GRAPH 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 ` diff --git a/pages/memgraph-zero/memgql/schema-file.mdx b/pages/memgraph-zero/memgql/schema-file.mdx index 8f7239bca..387c651eb 100644 --- a/pages/memgraph-zero/memgql/schema-file.mdx +++ b/pages/memgraph-zero/memgql/schema-file.mdx @@ -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.` reads it by name. | | `mappedGraphSource.connector` | native | Connector name (Memgraph / Neo4j); the query passes through as Cypher. | ### Edges @@ -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. | @@ -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),