Skip to content
Merged
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
25 changes: 25 additions & 0 deletions pages/memgraph-zero/memgql/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ description: MemGQL release notes

## MemGQL v0.15.0 - unreleased

### 🍃 New features & Improvements

- **One vertex in several connectors.** A label declared once per connector,
every declaration naming the same `linkKey`, is one entity: a path crosses
from one database into another at that node, with no edge to declare and no
connector named in the query. `MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book)<-[:REVIEWED]-(r:Reader)`
reads the books in one Memgraph, their reviews in another, and joins them on
the key. Whole-node returns come back as one node with the properties of
every side, connected to the relationships on both. See
[One vertex in several connectors](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors).
- **Faster cross-backend joins between connectors no graph maps.** Two
`MATCH` clauses that route to two different native connectors now send the
join keys of the filtered side to the other one as an `IN` list, instead of
reading both sides in full.
- **A clearer error when a label lives in several connectors of one graph.**
The error now names the connectors to choose from instead of the graph.

### 🐞 Bug fixes

- **Content in another connector is read wherever the vertex sits in the
path.** A vertex with a `mappedContentSource` that was not the first node of
its pattern, as `b` in `MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book) RETURN
b.blurb`, had its content properties come back `null`; the content
connector was never asked.

## MemGQL v0.14.0 - October 25th, 2026

### ⚠️ Behavior and breaking changes
Expand Down
25 changes: 25 additions & 0 deletions pages/memgraph-zero/memgql/connect/memgraph.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ CYPHER MATCH (a:Person)-[:KNOWS*1..2]->(b:Person) RETURN a.name, b.name;

For environment variables, see [Reference](/memgraph-zero/memgql/reference#memgraph-memgraph-memgraph-gql).

## Several Memgraph instances

Each instance is its own connector, and one MemGQL endpoint queries them all.
With a `catalog` instance holding `(:Book)-[:WRITTEN_BY]->(:Author)` and a
`reviews` instance holding `(:Reader)-[:REVIEWED]->(:Book)`, both carry `Book`,
so the relationship type picks the instance:

```gql
MATCH (b:Book)<-[:REVIEWED]-(r:Reader) RETURN b.title, r.name; -- runs on reviews
```

A query over both joins two `MATCH` clauses on a property. The filtered side
runs first and its keys go to the other instance as one `IN` list:

```gql
MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book)
MATCH (b2:Book)<-[:REVIEWED]-(r:Reader)
WHERE a.name = 'Frank Herbert' AND b.isbn = b2.isbn
RETURN b.title, r.name;
```

Declare `Book` in both with a `linkKey` and the same question is one path,
`MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book)<-[:REVIEWED]-(r:Reader)`; see
[One vertex in several connectors](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors).

## Supported GQL features

GQL-specific syntax (INSERT, LOCAL_DATETIME, etc.) only works on `memgraph-gql`; the plain `memgraph` connector is Cypher passthrough.
Expand Down
34 changes: 34 additions & 0 deletions pages/memgraph-zero/memgql/connect/redshift.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,40 @@ MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name;
MATCH (a:Person)((-[:KNOWS]->()){1,3})(b:Person) RETURN DISTINCT b.name;
```

## Joining Redshift with a graph

A Redshift table can be one home of an entity that also lives in a graph
database. Declare it with the same label and `linkKey` as the graph's
declaration, and a path crosses from the graph into Redshift at that node.
Next to the graph's own vertices and edges (here `Author` and `WRITTEN_BY` in
a `catalog` Memgraph), the Redshift side of the graph is:

```json
{ "label": "Book", "mappedGraphSource": { "connector": "catalog" }, "linkKey": "isbn" },
{ "label": "Book",
"mappedTableSource": { "connector": "warehouse", "table": "books", "metaFields": { "id": "isbn" } },
"attributes": [ { "name": "isbn" }, { "name": "title" } ],
"linkKey": "isbn" },
{ "label": "Sale",
"mappedTableSource": { "connector": "warehouse", "table": "sales", "metaFields": { "id": "sale_id" } },
"attributes": [ { "name": "isbn" }, { "name": "region" }, { "name": "units", "type": "Int" } ] },
{ "label": "SOLD_IN",
"from": "Book",
"to": "Sale",
"mappedJoinSource": { "fromKey": "isbn", "toKey": "isbn" } }
```

```gql
MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book)-[:SOLD_IN]->(s:Sale)
WHERE a.name = 'Frank Herbert'
RETURN b.title, s.region, s.units;
```

The books are found in the graph, and Redshift is asked about their `isbn`
values only, as bind parameters, with `books` joined to `sales` inside
Redshift. See
[One vertex in several connectors](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors).

## Authentication

The URI scheme selects the method:
Expand Down
6 changes: 6 additions & 0 deletions pages/memgraph-zero/memgql/multiple-graphs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ path pattern, and carries no properties of its own. See
[Schema File → Cross-connector edges](/memgraph-zero/memgql/schema-file#cross-connector-edges)
for the full rules.

When the two sides are the **same** entity kept in two databases (a book in a
catalog graph and in a reviews graph), declare it once per connector with a
`linkKey` instead, and a path crosses between them at that node with no edge to
write; see
[Schema File → One vertex in several connectors](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors).

## Composite Queries Across Graphs

The GQL standard defines composite expressions combining query branches with `UNION`, `INTERSECT`, and `EXCEPT`. Each branch can target a different graph.
Expand Down
25 changes: 22 additions & 3 deletions pages/memgraph-zero/memgql/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ supports.
| Pass a whole node through `WITH n` to a later step | ✓ | ✓ | ✓ |
| `MATCH (n)-[r:R]->(m)` typed edge expansion | ✓ | ✓ | ✓ |
| Multi-label pattern `(n:A&B)`; shared label over several mappings | ✓ | ✓ | ✗ |
| Path crossing connectors at a [linked vertex](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors) (`linkKey`) | ✓ | ✓ | ✓ |
| [Cross-connector edge](/memgraph-zero/memgql/schema-file#cross-connector-edges) (`mappedJoinSource`) | ✓ | ✓ | ✓ |
| Holding the [content of a vertex](/memgraph-zero/memgql/schema-file#content-in-another-connector) in another connector (`mappedContentSource`) | ✗ | ✓ | ✓ |
| Untyped edge `()-[]->(b)` (union over types) | ✓ | ✗ | ✗ |
| `UNION` / `UNION ALL` / `UNION DISTINCT` | ✓ | ✓ | ✗ |
| `INTERSECT` / `EXCEPT` | ✓ | ✓ | ✓ |
Expand Down Expand Up @@ -59,6 +62,11 @@ supports.
| `REMOVE` (property delete) | ✓ | ✗ | ✓ |
| `SET` / `REMOVE` of a **label** | ✓ | ✗ | ✗ |

The three cross-connector mapping rows hold for every connector tested against
Memgraph on the other side: PostgreSQL, MySQL, DuckDB, SQL Server, ClickHouse,
Amazon Redshift, MongoDB and Neo4j. A native graph (Memgraph, Neo4j) cannot
hold a vertex's content, which lives in a table or collection.

### Known limitations

- **Unbounded variable-length paths on SQL backends** (`()-[*]->()`)
Expand Down Expand Up @@ -91,8 +99,12 @@ supports.
individual nodes / edges instead.
- **Shared labels** (a label listed by several vertex mappings, see
[Vertices](/memgraph-zero/memgql/schema-file#vertices)) are unioned only
within one connector; a label shared across connectors is refused by routing
as ambiguous. A pattern matched through a shared label can be returned,
within one connector. A label several connectors carry is placed by the
relationship types next to it in the pattern (`(b:Book)<-[:REVIEWED]-()`
with `REVIEWED` stored in one connector); with nothing to place it, routing
refuses it as ambiguous and names the candidates. Declared with a
[`linkKey`](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors),
such a label is one entity, and a path may cross connectors at it. A pattern matched through a shared label can be returned,
filtered, sorted and aggregated, but not traversed from (`(a:Animal)-[:R]->()`),
not `INSERT`ed, and JSON keys (`a.props.key`) cannot be read through it —
each is refused with a pointer to the identifying label to use instead.
Expand Down Expand Up @@ -472,7 +484,10 @@ USE <graph2> <query>;

In `multi` mode, a query with no `USE` clause routes automatically when its
schema signals (labels, relationship types, properties) match exactly one
source; zero or multiple matches hard-error. Identifier matching is **exact**
source; zero or multiple matches hard-error. A label several sources carry is
narrowed by the relationship types and properties around it, and when the
candidates are connectors of one graph the error names those connectors.
Identifier matching is **exact**
(`:person` ≠ `Person`). Memgraph sources must run with `--schema-info-enabled`
for property-level introspection. See
[Multiple Graphs → Routing](/memgraph-zero/memgql/multiple-graphs#routing).
Expand Down Expand Up @@ -782,6 +797,10 @@ The same mapping body is used everywhere a graph is defined:
- **`MAPPING_FILE`** in single-connector mode: a bare `{ "vertices": …, "edges": … }`
body (the connector comes from the environment).

A label is unique within a graph, except that the same entity may be declared
once per connector when every declaration names the same `linkKey`; see
[One vertex in several connectors](/memgraph-zero/memgql/schema-file#one-vertex-in-several-connectors).

A mapping with a required field missing (for example a relational edge without
`metaFields.from` / `metaFields.to`) is rejected before the server serves
queries against it. The legacy `nodes` / `id_column` / `rel_type` format is no
Expand Down
97 changes: 92 additions & 5 deletions pages/memgraph-zero/memgql/schema-file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ A vertex maps a **label** to a backend source. Exactly one source kind is set:

A vertex may also add a **`mappedContentSource`** on top of either one, to read
some of its properties from a table in another connector (see
[Content in another connector](#content-in-another-connector)).
[Content in another connector](#content-in-another-connector)), or a
**`linkKey`**, to declare the same entity in several connectors (see
[One vertex in several connectors](#one-vertex-in-several-connectors)).

```json
{
Expand Down Expand Up @@ -343,7 +345,7 @@ spelling [schema discovery](#discovered-native-schema) produces.

| Field | Source kind | Description |
|-------|-------------|-------------|
| `label` / `labels` | both | The node label, or a list of labels whose first entry identifies the mapping (unique per graph) and whose others may be shared with other mappings. |
| `label` / `labels` | both | The node label, or a list of labels whose first entry identifies the mapping (unique per graph, unless every declaration names a `linkKey`) and whose others may be shared with other mappings. |
| `mappedTableSource.connector` | relational | Connector name (alias `catalog`). |
| `mappedTableSource.schema` | relational | Optional middle namespace (PostgreSQL schema, etc.). |
| `mappedTableSource.database` | relational | Optional outer namespace for backends with three-part names; on SQL Server it renders `database.schema.table`, so one connection can reach views in other databases. |
Expand All @@ -352,6 +354,7 @@ spelling [schema discovery](#discovered-native-schema) produces.
| `mappedGraphSource.connector` | native | Connector name (Memgraph / Neo4j); the query passes through as Cypher. |
| `mappedGraphSource.label` | native | Optional backend label (on an edge, backend relationship type) exposed under this element's `label`. Queries use the exposed name, MemGQL rewrites it for the backend, and returned nodes and relationships carry the exposed name. Two elements cannot share a backend name. Not renamed: strings a query computes from names (`labels(n)`, `type(r)`), the extra labels of a multi-label vertex, and property keys. |
| `mappedContentSource` | either | Optional second source for some of the vertex's properties; see [Content in another connector](#content-in-another-connector). |
| `linkKey` | either | Optional property identifying the same entity in every connector that declares this label; see [One vertex in several connectors](#one-vertex-in-several-connectors). |
| `attributes` | either | The properties the vertex exposes; see [Attributes](#attributes). |

#### Content in another connector
Expand Down Expand Up @@ -397,7 +400,7 @@ content connector.

| `mappedContentSource` key | Required | Description |
|---------------------------|----------|-------------|
| `connector` | yes | A **relational** connector, different from the vertex's own. |
| `connector` | yes | A **relational** or **MongoDB** connector, different from the vertex's own. |
| `schema` | no | Middle namespace of the content table. |
| `table` | yes | The content table. |
| `key` | yes | A **property** declared in the vertex's own `attributes`; its value finds the content row. |
Expand All @@ -410,12 +413,95 @@ instead):
- Each property has exactly one home: a name may not appear both in the
vertex's `attributes` and in the content `attributes`, and the `key` is
declared only on the vertex.
- The content connector must be relational and differ from the vertex's
- The content connector must be relational or MongoDB and differ from the vertex's
connector. Content in the same connector is just more attributes.
- A query that reads content properties must be a single `MATCH … RETURN`: no
`WITH`, `UNWIND`, `CALL`, `UNION` or `RETURN *`, and `OPTIONAL MATCH` may not
bind the vertex whose content is read.

#### One vertex in several connectors

The same entity often lives in more than one database: a book is in the
catalog graph, in the reviews graph, and in the sales warehouse, each time
with its own properties and relationships. Declare the label **once per
connector**, and give every declaration the same **`linkKey`**: the property
whose value says "this is the same book everywhere".

```
Memgraph "catalog" Memgraph "reviews" Redshift "warehouse"
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
│ (:Book │ │ (:Book │ │ books │
│ isbn: "978-0441…" │ │ isbn: "978-0441…" │ │ isbn: '978-0441…' │
│ title: "Dune") │ │ avgRating: 4.6) │ │ units_sold: 120000 │
└───────────┬────────────┘ └───────────┬────────────┘ └───────────┬────────────┘
└──────────── "linkKey": "isbn" ⇒ one Book ─────────────┘
```

```json
{ "label": "Book", "mappedGraphSource": { "connector": "catalog" }, "linkKey": "isbn" },
{ "label": "Book", "mappedGraphSource": { "connector": "reviews" }, "linkKey": "isbn" },
{ "label": "Book",
"mappedTableSource": { "connector": "warehouse", "table": "books", "metaFields": { "id": "isbn" } },
"attributes": [ { "name": "isbn" }, { "name": "unitsSold", "column": "units_sold" } ],
"linkKey": "isbn" }
```

Each declaration is an ordinary vertex of its connector. An edge that names
`Book` reads the declaration in its own connector: `WRITTEN_BY` in `catalog`,
`REVIEWED` in `reviews`. A path can therefore cross from one database into
another **at a book**, and the query never says where anything lives:

```gql
MATCH (a:Author)<-[:WRITTEN_BY]-(b:Book)<-[:REVIEWED]-(r:Reader)
WHERE a.name = 'Frank Herbert'
RETURN a, b, r;
```

`WRITTEN_BY` lives in `catalog` and `REVIEWED` in `reviews`, so MemGQL reads
the author's books in `catalog`, sends their `isbn` values to `reviews` as one
`IN` list, and joins the two halves on `isbn`:

```
catalog reviews
(a Frank Herbert)──[:WRITTEN_BY]──(b ?) (b' ?)──[:REVIEWED]──(r Reader)
└── b.isbn = b'.isbn ──┘
```

`RETURN b` comes back as **one** node carrying the properties of both
halves (`title` from `catalog`, `avgRating` from `reviews`), and the
`WRITTEN_BY` and `REVIEWED` relationships both point at it, so a graph client
draws one connected path. An equality filter on either side runs first in its
own database and narrows what the other side is asked for; a `LIMIT` applies to
the joined result.

**Same label, different names in the backends.** Linking groups declarations
by their exposed `label`, but the backends may name things differently. A
table's name never matters, and a native label can be renamed with
`mappedGraphSource.label`
(`{ "label": "Book", "mappedGraphSource": { "connector": "reviews", "label": "Title" }, "linkKey": "isbn" }`).
The key is one property name on every declaration: a table can map it from any
column (`{ "name": "isbn", "column": "isbn13" }`), while on Memgraph and Neo4j
the stored property must itself be called `isbn`. Two different entities, such
as `Book` and `Author`, are not linked: connect them with a
[cross-connector edge](#cross-connector-edges).

**Rules and limits** (each is refused with a message saying what to write
instead):

- Every declaration names the same `linkKey`, at most one declaration per
connector, and a label declared twice without `linkKey` is a duplicate.
- On a table declaration, `linkKey` must be one of its declared attributes, as
a plain column (not a JSON `path`).
- `linkKey` does not combine with `mappedContentSource`, and a
`mappedJoinSource` edge between two declarations of the same linked label is
refused: the key already joins them.
- A `Book` with no relationship to place it
(`MATCH (b:Book {isbn: '…'}) RETURN b`) is refused, naming the connectors to
choose from. Add a relationship that only one of them stores.
- A path crosses connectors in a single `MATCH`, without `OPTIONAL MATCH`,
path variables, shortest-path search or variable-length hops across the
crossing.

### Edges

An edge maps a **relationship type** between two labels. Relational edges name
Expand Down Expand Up @@ -854,10 +940,11 @@ Every name a schema file can contain, grouped by where it appears.

| Name | Meaning |
|------|---------|
| `label` | The exposed node label; unique within a graph. |
| `label` | The exposed node label; unique within a graph, except that a linked label is declared once per connector. |
| `labels` | Several labels; the first identifies the mapping, the rest may be shared. |
| `mappedTableSource` / `mappedGraphSource` | Where the vertex lives; exactly one is set. |
| `mappedContentSource` | Optional table in another connector for some of its properties. |
| `linkKey` | The **property** identifying the same entity in every connector that declares the label. |
| `attributes` | The properties read from the vertex's own source. |

### Edge
Expand Down
Loading