From 02ab89ffc34c128653583bcc6fc43e8a6d052735 Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Sat, 8 Aug 2026 16:55:39 +0200 Subject: [PATCH 1/4] Add MemGQL MongoDB connector --- pages/memgraph-zero/memgql/changelog.mdx | 25 +++ pages/memgraph-zero/memgql/connect.mdx | 1 + pages/memgraph-zero/memgql/connect/_meta.ts | 1 + .../memgraph-zero/memgql/connect/mongodb.mdx | 157 ++++++++++++++++++ pages/memgraph-zero/memgql/features.mdx | 2 + .../memgraph-zero/memgql/multiple-graphs.mdx | 4 +- pages/memgraph-zero/memgql/reference.mdx | 15 ++ 7 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 pages/memgraph-zero/memgql/connect/mongodb.mdx diff --git a/pages/memgraph-zero/memgql/changelog.mdx b/pages/memgraph-zero/memgql/changelog.mdx index 046d9f859..7710ffe1d 100644 --- a/pages/memgraph-zero/memgql/changelog.mdx +++ b/pages/memgraph-zero/memgql/changelog.mdx @@ -5,6 +5,31 @@ description: MemGQL release notes # MemGQL Changelog +## MemGQL v0.9.0 - TBD + +### 🍃 New features & Improvements + +- **MongoDB connector.** New `mongodb` connector type brings a document store + into the federation. It is the first backend that is neither SQL nor Cypher: + queries are translated to **MongoDB aggregation pipelines** and executed + server-side. Node labels map to collections and relationship types to their own + collections holding `from`/`to` reference fields, using the same + [mapping](/memgraph-zero/memgql/reference#mapping-schema) format as every other + backend — a mapping written for PostgreSQL works unchanged. A single hop + becomes `$lookup`; a **variable-length hop becomes `$graphLookup`**, MongoDB's + native recursive traversal, so `(a)-[:KNOWS*1..3]->(b)` runs on the server — + and unlike the SQL backends the **unbounded** `*` form is supported too. + Filters that compare a property to a literal emit the plain query form so + MongoDB can serve them from an index. **Both reads and writes** are supported, + including `DETACH DELETE`, which clears incident edge documents. Connect with a + standard connection string, so replica sets and MongoDB Atlas + (`mongodb+srv://`) work as-is. `WITH` boundaries, `OPTIONAL MATCH` (including a + predicate inside the optional pattern), `FOR x IN […]`, and the full aggregate + set all run server-side; `UNION` and scalar functions inside `RETURN` are the + notable gaps. MongoDB runs the same cross-backend parity corpus as every other + connector, so its answers are checked against the same golden rows. See the + [MongoDB connector page](/memgraph-zero/memgql/connect/mongodb) for setup. + ## MemGQL v0.8.0 - July 19th, 2026 ### ⚠️ Breaking changes diff --git a/pages/memgraph-zero/memgql/connect.mdx b/pages/memgraph-zero/memgql/connect.mdx index dd2db8732..9321050c2 100644 --- a/pages/memgraph-zero/memgql/connect.mdx +++ b/pages/memgraph-zero/memgql/connect.mdx @@ -9,6 +9,7 @@ description: MemGQL connector details and configuration. - [DuckDB](/memgraph-zero/memgql/connect/duckdb) - [Iceberg](/memgraph-zero/memgql/connect/iceberg) - [Memgraph](/memgraph-zero/memgql/connect/memgraph) +- [MongoDB](/memgraph-zero/memgql/connect/mongodb) - [MySQL](/memgraph-zero/memgql/connect/mysql) - [Neo4j](/memgraph-zero/memgql/connect/neo4j) - [Oracle](/memgraph-zero/memgql/connect/oracle) diff --git a/pages/memgraph-zero/memgql/connect/_meta.ts b/pages/memgraph-zero/memgql/connect/_meta.ts index 91cb92f32..c3f903b79 100644 --- a/pages/memgraph-zero/memgql/connect/_meta.ts +++ b/pages/memgraph-zero/memgql/connect/_meta.ts @@ -3,6 +3,7 @@ export default { "duckdb": "to DuckDB", "iceberg": "to Iceberg", "memgraph": "to Memgraph", + "mongodb": "to MongoDB", "neo4j": "to Neo4j", "oracle": "to Oracle", "postgres": "to PostgreSQL", diff --git a/pages/memgraph-zero/memgql/connect/mongodb.mdx b/pages/memgraph-zero/memgql/connect/mongodb.mdx new file mode 100644 index 000000000..9ce483094 --- /dev/null +++ b/pages/memgraph-zero/memgql/connect/mongodb.mdx @@ -0,0 +1,157 @@ +--- +title: MongoDB +description: Connect MemGQL to MongoDB. +--- + +# MongoDB + +The MongoDB connector (`CONNECTOR_TYPE=mongodb`) translates GQL queries into +MongoDB **aggregation pipelines** and executes them on the server. It requires a +[mapping file](../quick-start.mdx#mapping-file) that maps graph patterns to +MongoDB collections. + +Unlike the SQL connectors, nothing is translated to SQL. Node labels become +collections, relationship types become their own collections holding `from`/`to` +reference fields, and each GQL operator becomes a pipeline stage — a hop is +`$lookup`, and a variable-length hop is `$graphLookup`, MongoDB's native +recursive traversal. + +## 1. Start MongoDB + +```bash +docker network create memgql-net + +docker run -d --rm \ + --name mongodb-dev \ + --network memgql-net \ + -p 27017:27017 \ + mongo:8 +``` + +## 2. Seed data + +```bash +docker exec -i mongodb-dev mongosh --quiet test << 'JS' +db.persons.insertMany([ + { id: 1, name: 'Alice', age: 30 }, + { id: 2, name: 'Bob', age: 25 }, +]); +db.companies.insertOne({ id: 1, name: 'Acme Corp' }); +db.knows.insertOne({ id: 1, from_id: 1, to_id: 2 }); +db.works_at.insertOne({ id: 1, person_id: 1, company_id: 1 }); + +// Identity and endpoint indexes: `$lookup` and `$graphLookup` match on these, +// and without an index each hop is a collection scan. +db.persons.createIndex({ id: 1 }, { unique: true }); +db.companies.createIndex({ id: 1 }, { unique: true }); +db.knows.createIndex({ from_id: 1 }); +db.knows.createIndex({ to_id: 1 }); +db.works_at.createIndex({ person_id: 1 }); +db.works_at.createIndex({ company_id: 1 }); +JS +``` + +## 3. Start MemGQL + +```bash +docker run --rm \ + --name memgql \ + --network memgql-net \ + --stop-timeout 2 \ + -p 7688:7688 \ + --env CONNECTOR_TYPE=mongodb \ + --env MONGODB_URL=mongodb://mongodb-dev:27017 \ + --env MONGODB_DB=test \ + --env MAPPING_FILE=/data/mapping.json \ + --env BOLT_LISTEN_ADDR=0.0.0.0:7688 \ + -v ./mapping.json:/data/mapping.json \ + memgraph/memgql:latest +``` + +## 4. Connect + +```bash +mgconsole --port 7688 +``` + +## 5. Query + +```gql +MATCH (p:Person) RETURN p.name, p.age; +``` + +```gql +MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name; +``` + +```gql +MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name; +``` + +Variable-length traversal runs natively on MongoDB, and — unlike the SQL +connectors — the unbounded form works too: + +```gql +MATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a.name, b.name; +``` + +For environment variables, see [Reference](../reference.mdx#mongodb-mongodb). + +## Mapping a document model + +The mapping file is the same one every other connector uses; only the field +names read differently: + +| Mapping field | MongoDB meaning | +|----------------------------|------------------------------------------------| +| `mappedTableSource.schema` | database (defaults to `MONGODB_DB`) | +| `mappedTableSource.table` | collection | +| `metaFields.id` | identity field (often `_id`) | +| `metaFields.from` / `.to` | endpoint reference fields on an edge collection | +| `attributes[].column` | backing document field | + +Relationships must live in their own collections carrying the `from`/`to` +reference fields — that is the shape `$lookup` and `$graphLookup` consume. A +model that embeds neighbours as an array inside the node document is not +supported. + +Every collection in one graph must be in the same database: MongoDB's `$lookup` +resolves collections inside the aggregation's own database and cannot join +across databases. Spanning two databases needs two connectors. + +## Supported GQL features + +| Feature | MongoDB | +|------------------------------------------------|------------------------| +| `MATCH (n:Label) RETURN n.prop` | ✓ | +| Whole-node `RETURN n` / whole-rel `RETURN r` | ✓ | +| Pattern-level `WHERE` (`MATCH (n WHERE …)`) | ✓ | +| Typed edge `(a)-[r:R]->(b)` | ✓ (`$lookup`) | +| Variable-length `(a)-[:R*1..n]->(b)` | ✓ (`$graphLookup`) | +| Unbounded variable-length `(a)-[:R*]->(b)` | ✓ | +| `OPTIONAL MATCH` (incl. a predicate inside it) | ✓ | +| `WITH` pipeline boundaries | ✓ | +| `FOR x IN [...]` | ✓ | +| `ORDER BY` / `LIMIT` / `SKIP` | ✓ | +| `DISTINCT` | ✓ | +| Aggregation (`count`, `sum`, `avg`, `collect`) | ✓ (`$group`) | +| `INSERT (a {…})` | ✓ | +| `DELETE` / `DETACH DELETE` | ✓ | +| `SET` / `REMOVE` (properties) | ✓ | +| `SET` / `REMOVE` (labels) | ✗ — a label is a collection | +| `UNION` / `UNION ALL` | ✗ | +| Scalar functions in `RETURN` (`upper(x)`, …) | ✗ — see below | +| Untyped edge `(a)-[]->(b)` | ✗ — name the edge type | +| Undirected variable-length `(a)-[:R*]-(b)` | ✗ — give it a direction | +| Path binding on a quantified pattern | ✗ | + +Traversal is reachability-based: `$graphLookup` returns the set of nodes +reachable through the edge collection, so a node reachable by several routes +appears once. Queries that count distinct paths need a Cypher backend. + +Scalar functions inside `RETURN` (`upper(n.name)`, `abs(-7)`, `char_length(s)`) +are not available on MongoDB. The other backends receive such a call as raw +query text and evaluate it in their own dialect; an aggregation pipeline has no +expression string to run, so the function has to be applied client-side for now. +Aggregates (`count`, `sum`, `avg`, `min`, `max`, `collect`, and their `DISTINCT` +forms) are unaffected and run natively as `$group`. diff --git a/pages/memgraph-zero/memgql/features.mdx b/pages/memgraph-zero/memgql/features.mdx index b698fda85..c95032441 100644 --- a/pages/memgraph-zero/memgql/features.mdx +++ b/pages/memgraph-zero/memgql/features.mdx @@ -9,6 +9,7 @@ description: MemGQL Community and Enterprise feature comparison. |----------------------------------------------------------------------------------------------------------|-----------|-----------------------------------------------| | GQL to Cypher translation | Yes | Yes | | GQL to SQL translation | Yes | Yes | +| GQL to MongoDB aggregation pipeline translation | Yes | Yes | | Bolt protocol | Yes | Yes | | **Connectors** | | | | [Memgraph](/memgraph-zero/memgql/connect/memgraph) | Yes | Yes | @@ -23,6 +24,7 @@ description: MemGQL Community and Enterprise feature comparison. | [Oracle](/memgraph-zero/memgql/connect/oracle) | Yes | Yes | | [SQL Server](/memgraph-zero/memgql/connect/sqlserver) | Yes | Yes | | [Snowflake](/memgraph-zero/memgql/connect/snowflake) | Yes | Yes | +| [MongoDB](/memgraph-zero/memgql/connect/mongodb) | Yes | Yes | | **Multi-Connection Mode** | Yes | Yes | | Max connectors | 2 | Unlimited | | Max simultaneous connections | 2 | Unlimited | diff --git a/pages/memgraph-zero/memgql/multiple-graphs.mdx b/pages/memgraph-zero/memgql/multiple-graphs.mdx index e3e151394..5f86ca69f 100644 --- a/pages/memgraph-zero/memgql/multiple-graphs.mdx +++ b/pages/memgraph-zero/memgql/multiple-graphs.mdx @@ -11,7 +11,7 @@ If you want a running stack to try these queries against, the [Docker Compose - ## Where the catalog DSL works -The catalog statements (`ADD CONNECTOR`, `CREATE GRAPH`, `SHOW GRAPHS`, `SHOW CONNECTORS`, `DROP GRAPH`, `USE `, …) are available in **`CONNECTOR_TYPE=multi`** mode. Single-backend modes (`memgraph-gql`, `neo4j-gql`, `postgres`, `mysql`, `oracle`, `duckdb`, `clickhouse`, `iceberg`, `pinot`, `snowflake`) connect to one backend configured via env vars and don't expose the catalog. +The catalog statements (`ADD CONNECTOR`, `CREATE GRAPH`, `SHOW GRAPHS`, `SHOW CONNECTORS`, `DROP GRAPH`, `USE `, …) are available in **`CONNECTOR_TYPE=multi`** mode. Single-backend modes (`memgraph-gql`, `neo4j-gql`, `postgres`, `mysql`, `oracle`, `duckdb`, `clickhouse`, `iceberg`, `pinot`, `snowflake`, `mongodb`) connect to one backend configured via env vars and don't expose the catalog. | Statement | `multi` | Cypher single-backend | SQL single-backend | |---|---|---|---| @@ -84,7 +84,7 @@ MATCH (me:Person {id: 1})-[:FRIEND_OF]->(f:Person) RETURN f.id; The schema index is built from two sources: - **SQL-family connectors** (PostgreSQL, MySQL, Oracle, DuckDB, ClickHouse, - Iceberg, Pinot, Snowflake): taken from the registered **mapping** (labels, + Iceberg, Pinot, Snowflake, MongoDB): taken from the registered **mapping** (labels, rel-types, and properties are known exactly). - **Cypher-family connectors** (Memgraph, Neo4j): **introspected at `CONNECT` time** and cached. Memgraph uses `SHOW SCHEMA INFO` (the server must run with diff --git a/pages/memgraph-zero/memgql/reference.mdx b/pages/memgraph-zero/memgql/reference.mdx index 467f904bc..92d96cd95 100644 --- a/pages/memgraph-zero/memgql/reference.mdx +++ b/pages/memgraph-zero/memgql/reference.mdx @@ -217,6 +217,7 @@ connections. | `iceberg` | GQL -> SQL | Iceberg via Trino | | `iceberg-direct` | None (native in-process) | Iceberg (REST catalog + Arrow) | | `pinot` | GQL -> SQL | Apache Pinot | +| `mongodb` | GQL -> aggregation pipeline | MongoDB 5.0+ | | `multi` | Per-connector | Multiple backends simultaneously | @@ -308,6 +309,20 @@ type. See the [SQL Server connector page](/memgraph-zero/memgql/connect/sqlserve | `PINOT_QUERY_OPTIONS` | `useMultistageEngine=true` | Query options sent with broker SQL requests | | `MAPPING_FILE` | _(required)_ | Path to JSON mapping file | +#### MongoDB (`mongodb`) + +Translates to MongoDB aggregation pipelines rather than SQL. `mongo` is accepted +as an alias for the connector type. + +| Variable | Default | Description | +|----------------|-----------------------------|------------------------------------------| +| `MONGODB_URL` | `mongodb://localhost:27017` | Connection string (`mongodb+srv://` too) | +| `MONGODB_DB` | `test` | Default database | +| `MAPPING_FILE` | _(required)_ | Path to JSON mapping file | + +Every collection in one graph must live in the same database — MongoDB's +`$lookup` cannot join across databases. + #### Iceberg (`iceberg`) | Variable | Default | Description | From 4bb3d39c685bce44da8f98b7b6550f3b3cb02336 Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Sat, 8 Aug 2026 17:15:19 +0200 Subject: [PATCH 2/4] Update reference page --- pages/memgraph-zero/memgql/reference.mdx | 146 ++++++++++++++++------- 1 file changed, 100 insertions(+), 46 deletions(-) diff --git a/pages/memgraph-zero/memgql/reference.mdx b/pages/memgraph-zero/memgql/reference.mdx index 92d96cd95..708760b4a 100644 --- a/pages/memgraph-zero/memgql/reference.mdx +++ b/pages/memgraph-zero/memgql/reference.mdx @@ -7,55 +7,62 @@ description: All the details about syntax and various configs What works today, split by backend category. "Cypher backends" means Memgraph and Neo4j (translation is largely passthrough); "SQL backends" -means PostgreSQL, MySQL and DuckDB. +means PostgreSQL, MySQL and DuckDB. **MongoDB** is neither — it translates to +aggregation pipelines — so it gets its own column. SQL Server, ClickHouse, Apache Iceberg, and Apache Pinot are also supported as connectors but with a narrower verified surface. See each connector's page for the exact list of features each one supports. -| Feature | Cypher backends | SQL backends | -|--------------------------------------------------------------------|-----------------|--------------| -| `MATCH` / `WHERE` / `RETURN` | ✓ | ✓ | -| Pattern-level `WHERE` (`MATCH (n WHERE …)`) | ✓ | ✓ | -| Multiple `MATCH` clauses in one query | ✓ | ✓ | -| `OPTIONAL MATCH` (keep left side when no match found) | ✓ | ✓ | -| `WITH` clause (chain query steps) | ✓ | ✓ | -| `WITH DISTINCT` / `WITH … ORDER BY … LIMIT N` | ✓ | ✓ | -| Multiple chained `WITH` steps in one query | ✓ | ✓ | -| Pass a whole node through `WITH n` to a later step | ✓ | ✓ | -| `MATCH (n)-[r:R]->(m)` typed edge expansion | ✓ | ✓ | -| Untyped edge `()-[]->(b)` (union over types) | ✓ | ✗ | -| `UNION` / `UNION ALL` / `UNION DISTINCT` | ✓ | ✓ | -| `INTERSECT` / `EXCEPT` | ✓ | ✓ | -| Quantified path `(){m,n}` (bounded) | ✓ | ✓ | -| Quantified path `(){m,}` (unbounded) | ✓ | ✗ | -| Shortest-path (`ALL SHORTEST` / `ANY SHORTEST` / `SHORTEST k`) | ✓ | ✗ | -| Whole-node `RETURN n` / whole-relationship `RETURN r` | ✓ | ✓ | -| Map projections `RETURN n {.id, .title}` | ✓ | ✓ | -| Connection-less `RETURN 1` / `RETURN 1 + 2` (liveness) | ✓ | ✓ | -| `IN` list membership `WHERE x IN […]` | ✓ | ✓ | -| `STARTS WITH` / `ENDS WITH` / `CONTAINS` | ✓ | ✓ | -| `collect()` / `collect_list()` (aggregate) | ✓ | ✓ | -| `count`, `sum`, `avg`, `min`, `max` | ✓ | ✓ | -| `COUNT(DISTINCT …)` | ✓ | ✓ | -| Arithmetic `+ - * / %` | ✓ | ✓ | -| `CASE WHEN … THEN … ELSE … END` | ✓ | ✓ | -| `COALESCE`, `NULLIF` | ✓ | ✓ | -| Temporals (`date`, `datetime`, `localTime`, …) | ✓ | ✗ | -| `INSERT (a {…}) RETURN a.x` | ✓ | ✓ | -| `DELETE` | ✓ | ✓ | -| `DETACH DELETE` | ✓ | ✗ | -| `SET` (property update) | ✓ | ✗ | -| `REMOVE` (property delete) | ✓ | ✗ | +| Feature | Cypher backends | SQL backends | MongoDB | +|--------------------------------------------------------------------|-----------------|--------------|---------| +| `MATCH` / `WHERE` / `RETURN` | ✓ | ✓ | ✓ | +| Pattern-level `WHERE` (`MATCH (n WHERE …)`) | ✓ | ✓ | ✓ | +| Multiple `MATCH` clauses in one query | ✓ | ✓ | ✓ | +| `OPTIONAL MATCH` (keep left side when no match found) | ✓ | ✓ | ✓ | +| `WITH` clause (chain query steps) | ✓ | ✓ | ✓ | +| `WITH DISTINCT` / `WITH … ORDER BY … LIMIT N` | ✓ | ✓ | ✓ | +| Multiple chained `WITH` steps in one query | ✓ | ✓ | ✓ | +| Pass a whole node through `WITH n` to a later step | ✓ | ✓ | ✓ | +| `MATCH (n)-[r:R]->(m)` typed edge expansion | ✓ | ✓ | ✓ | +| Untyped edge `()-[]->(b)` (union over types) | ✓ | ✗ | ✗ | +| `UNION` / `UNION ALL` / `UNION DISTINCT` | ✓ | ✓ | ✗ | +| `INTERSECT` / `EXCEPT` | ✓ | ✓ | ✗ | +| Quantified path `(){m,n}` (bounded) | ✓ | ✓ | ✓ | +| Quantified path `(){m,}` (unbounded) | ✓ | ✗ | ✓ | +| Shortest-path (`ALL SHORTEST` / `ANY SHORTEST` / `SHORTEST k`) | ✓ | ✗ | ✗ | +| Whole-node `RETURN n` / whole-relationship `RETURN r` | ✓ | ✓ | ✓ | +| Map projections `RETURN n {.id, .title}` | ✓ | ✓ | ✓ | +| Connection-less `RETURN 1` / `RETURN 1 + 2` (liveness) | ✓ | ✓ | ✓ | +| `IN` list membership `WHERE x IN […]` | ✓ | ✓ | ✓ | +| `STARTS WITH` / `ENDS WITH` / `CONTAINS` | ✓ | ✓ | ✓ | +| `FOR x IN […]` (UNWIND-style loop) | ✓ | ✗ | ✓ | +| `collect()` / `collect_list()` (aggregate) | ✓ | ✓ | ✓ | +| `count`, `sum`, `avg`, `min`, `max` | ✓ | ✓ | ✓ | +| `COUNT(DISTINCT …)` | ✓ | ✓ | ✓ | +| Arithmetic `+ - * / %` | ✓ | ✓ | ✓ | +| `CASE WHEN … THEN … ELSE … END` | ✓ | ✓ | ✓ | +| `COALESCE`, `NULLIF` | ✓ | ✓ | ✓ | +| Scalar functions in `RETURN` (`upper(x)`, `abs(x)`, …) | ✓ | ✓ | ✗ | +| Temporals (`date`, `datetime`, `localTime`, …) | ✓ | ✗ | ✗ | +| `INSERT (a {…})` | ✓ | ✓ | ✓ | +| `INSERT (a {…}) RETURN a.x` (post-insert projection) | ✓ | ✓ | ✗ | +| `DELETE` | ✓ | ✓ | ✓ | +| `DETACH DELETE` | ✓ | ✗ | ✓ | +| `SET` (property update) | ✓ | ✗ | ✓ | +| `REMOVE` (property delete) | ✓ | ✗ | ✓ | +| `SET` / `REMOVE` of a **label** | ✓ | ✗ | ✗ | ### Known limitations - **Unbounded variable-length paths on SQL backends** (`()-[*]->()`) return an actionable error. -- **Untyped edge traversal on SQL backends** (`MATCH ()-[]->(b)` with - no rel-type) returns an actionable error pointing users at declaring - the edge type or running on a Cypher backend. The form is still - accepted natively on Cypher backends. +- **Untyped edge traversal on SQL backends and MongoDB** (`MATCH ()-[]->(b)` + with no rel-type) returns an actionable error pointing users at declaring + the edge type or running on a Cypher backend. Each relationship type is a + separate table (or collection), so an untyped hop would have to union across + every registered edge mapping. The form is still accepted natively on Cypher + backends. - **`FOR x IN [...]` (UNWIND-style) on SQL backends** returns an actionable error pointing users at running the query on a Cypher backend. The form is still accepted natively on Cypher backends. @@ -63,10 +70,43 @@ supports. relationship rows are not detached. It deletes a node that has no relationship rows and otherwise surfaces the backend's constraint error; don't rely on it. - **Path variables on variable-length patterns**: - `MATCH p = (a){1,3}(b) RETURN p` is not yet supported on SQL backends. - Drop the `p =` binding (or query a Cypher backend) and `RETURN` the + `MATCH p = (a){1,3}(b) RETURN p` is not yet supported on SQL backends or + MongoDB. Drop the `p =` binding (or query a Cypher backend) and `RETURN` the individual nodes / edges instead. +#### MongoDB-specific + +- **`UNION` / `UNION ALL` / `INTERSECT` / `EXCEPT`** return an actionable error. + Compose the arms client-side, or run the query on another backend. +- **Quantified paths are reachability, not path enumeration.** MongoDB runs + `(){m,n}` on `$graphLookup`, which never revisits an edge document. That + matches trail semantics (no edge repeats) and terminates on cycles, but it + deduplicates across the whole traversal: where several distinct paths reach + the same node, the SQL backends count each and MongoDB counts one. Use a + Cypher backend when the number of paths is the answer. +- **Undirected variable-length** (`(a)-[:R*]-(b)`) returns an actionable error: + `$graphLookup` follows a single connect-from/connect-to field pair. Give the + pattern a direction. +- **Scalar functions inside `RETURN`** (`upper(n.name)`, `abs(-7)`, + `char_length(s)`) return an actionable error. MemGQL passes such a call to + the other backends as raw query text, which happens to parse in their SQL or + Cypher dialect; an aggregation pipeline has no expression string to run one. + Aggregates (`count`, `sum`, `avg`, `min`, `max`, `collect`, and their + `DISTINCT` forms) are unaffected and run natively. +- **`INSERT … RETURN` drops the projection** and reports the affected count + instead. Re-read the inserted node with a follow-up `MATCH`. +- **Every collection in one graph must live in the same database.** MongoDB's + `$lookup` resolves collections inside the aggregation's own database and + cannot join across databases; a mapping that spans two is rejected at + translation time rather than silently reading the wrong collection. Use a + second connector instead. +- **A label is a collection**, so `SET n:Label` / `REMOVE n:Label` would mean + moving the document between collections and returns an actionable error. +- **`OPTIONAL MATCH` predicates may reference one variable.** A condition + inside the optional pattern is folded into the `$lookup` that binds the + variable it constrains; one spanning two variables returns an actionable + error rather than silently dropping rows. + ## Graph Management Query Syntax ``` @@ -74,7 +114,9 @@ supports. ADD CONNECTOR TYPE [URI ''] [PATH ''] [USER ''] [PASSWORD ''] - [CATALOG ''] [SCHEMA ''] [GRAPH '']; + [DATABASE ''] [CATALOG ''] [SCHEMA ''] [GRAPH ''] + [WAREHOUSE ''] [ROLE ''] + [PRIVATE_KEY_PATH ''] [TOKEN '']; DROP CONNECTOR ; PING ; @@ -91,10 +133,22 @@ ALTER GRAPH REMOVE CACHE; SHOW GRAPH CACHES; -- graph, cache_connector, ttl_secs, max_bytes, fragment count ``` -A connector is a **connection only**; it carries no graph shape. `GRAPH ` -selects the Cypher database on Memgraph / Neo4j (it is not a mapping). Re-adding -a connector replaces its config; `DROP CONNECTOR` is refused while a graph still -references it. +A connector is a **connection only**; it carries no graph shape. Which options +apply depends on the type: + +| Option | Read by | +|-----------------------------|----------------------------------------------------------------------| +| `GRAPH ''` | Memgraph, Neo4j — selects the Cypher database (it is not a mapping) | +| `DATABASE ''` | PostgreSQL, MySQL, SQL Server, Oracle (service name), ClickHouse, MongoDB, Snowflake | +| `CATALOG ''` | Iceberg (Trino catalog), Iceberg Direct (warehouse) | +| `SCHEMA ''` | Iceberg, Snowflake; MongoDB accepts it as a fallback for `DATABASE` | +| `WAREHOUSE` / `ROLE` | Snowflake session settings | +| `PRIVATE_KEY_PATH` / `TOKEN`| Snowflake auth (key-pair JWT / programmatic access token) | +| `PATH ''` | DuckDB (database file; `:memory:` by default) | + +An option a connector doesn't read is ignored, and one that is omitted falls +back to that connector's environment variable. Re-adding a connector replaces +its config; `DROP CONNECTOR` is refused while a graph still references it. `CREATE GRAPH … FROM` registers a graph from a `{ "vertices": …, "edges": … }` body and auto-connects the connectors it references. The mapping format is From 7326785fb2c0c81861ee5eb0be14aecb2001bf04 Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Sat, 8 Aug 2026 20:19:17 +0200 Subject: [PATCH 3/4] Update docs after adding the rest of the tests --- pages/memgraph-zero/memgql/connect/mongodb.mdx | 8 ++++---- pages/memgraph-zero/memgql/reference.mdx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/memgraph-zero/memgql/connect/mongodb.mdx b/pages/memgraph-zero/memgql/connect/mongodb.mdx index 9ce483094..e35057edd 100644 --- a/pages/memgraph-zero/memgql/connect/mongodb.mdx +++ b/pages/memgraph-zero/memgql/connect/mongodb.mdx @@ -92,7 +92,7 @@ Variable-length traversal runs natively on MongoDB, and — unlike the SQL connectors — the unbounded form works too: ```gql -MATCH (a:Person)-[:KNOWS*1..3]->(b:Person) RETURN a.name, b.name; +MATCH (a:Person) (-[:KNOWS]->()){1,3} (b:Person) RETURN a.name, b.name; ``` For environment variables, see [Reference](../reference.mdx#mongodb-mongodb). @@ -127,8 +127,8 @@ across databases. Spanning two databases needs two connectors. | Whole-node `RETURN n` / whole-rel `RETURN r` | ✓ | | Pattern-level `WHERE` (`MATCH (n WHERE …)`) | ✓ | | Typed edge `(a)-[r:R]->(b)` | ✓ (`$lookup`) | -| Variable-length `(a)-[:R*1..n]->(b)` | ✓ (`$graphLookup`) | -| Unbounded variable-length `(a)-[:R*]->(b)` | ✓ | +| Variable-length `(-[:R]->()){m,n}` | ✓ (`$graphLookup`) | +| Unbounded variable-length `(-[:R]->()){m,}` | ✓ | | `OPTIONAL MATCH` (incl. a predicate inside it) | ✓ | | `WITH` pipeline boundaries | ✓ | | `FOR x IN [...]` | ✓ | @@ -142,7 +142,7 @@ across databases. Spanning two databases needs two connectors. | `UNION` / `UNION ALL` | ✗ | | Scalar functions in `RETURN` (`upper(x)`, …) | ✗ — see below | | Untyped edge `(a)-[]->(b)` | ✗ — name the edge type | -| Undirected variable-length `(a)-[:R*]-(b)` | ✗ — give it a direction | +| Undirected variable-length `(-[:R]-()){m,n}` | ✗ — give it a direction | | Path binding on a quantified pattern | ✗ | Traversal is reachability-based: `$graphLookup` returns the set of nodes diff --git a/pages/memgraph-zero/memgql/reference.mdx b/pages/memgraph-zero/memgql/reference.mdx index 708760b4a..3fe28b768 100644 --- a/pages/memgraph-zero/memgql/reference.mdx +++ b/pages/memgraph-zero/memgql/reference.mdx @@ -84,7 +84,7 @@ supports. deduplicates across the whole traversal: where several distinct paths reach the same node, the SQL backends count each and MongoDB counts one. Use a Cypher backend when the number of paths is the answer. -- **Undirected variable-length** (`(a)-[:R*]-(b)`) returns an actionable error: +- **Undirected variable-length** (`(-[:R]-()){m,n}`) returns an actionable error: `$graphLookup` follows a single connect-from/connect-to field pair. Give the pattern a direction. - **Scalar functions inside `RETURN`** (`upper(n.name)`, `abs(-7)`, From 2300998f0e1c9d6dea2f2ff61457c2353dbfcab2 Mon Sep 17 00:00:00 2001 From: Marko Budiselic Date: Sun, 9 Aug 2026 09:11:59 +0200 Subject: [PATCH 4/4] Update the connect docs example --- .../memgraph-zero/memgql/connect/mongodb.mdx | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/pages/memgraph-zero/memgql/connect/mongodb.mdx b/pages/memgraph-zero/memgql/connect/mongodb.mdx index e35057edd..25c09f15c 100644 --- a/pages/memgraph-zero/memgql/connect/mongodb.mdx +++ b/pages/memgraph-zero/memgql/connect/mongodb.mdx @@ -51,8 +51,73 @@ db.works_at.createIndex({ company_id: 1 }); JS ``` +Check what landed, straight from `mongosh`: + +```bash +docker exec mongodb-dev mongosh --quiet test --eval 'db.getCollectionNames().sort().forEach(c => print(c, JSON.stringify(db[c].find({}, {_id:0}).toArray())))' +``` + +``` +companies [{"id":1,"name":"Acme Corp"}] +knows [{"id":1,"from_id":1,"to_id":2}] +persons [{"id":1,"name":"Alice","age":30},{"id":2,"name":"Bob","age":25}] +works_at [{"id":1,"person_id":1,"company_id":1}] +``` + +`persons` and `companies` are node collections; `knows` and `works_at` are edge +collections whose `from_id`/`to_id` and `person_id`/`company_id` point at node +`id`s. That reference shape is what the mapping below turns into a graph. + ## 3. Start MemGQL +Create the mapping file the connector reads — labels over collections +(`vertices`), relationship types over edge collections (`edges`): + +```bash +cat > mapping.json << 'EOF' +{ + "vertices": [ + { + "label": "Person", + "mappedTableSource": { + "table": "persons", + "metaFields": { "id": "id" } + }, + "attributes": [{ "name": "name" }, { "name": "age" }] + }, + { + "label": "Company", + "mappedTableSource": { + "table": "companies", + "metaFields": { "id": "id" } + }, + "attributes": [{ "name": "name" }] + } + ], + "edges": [ + { + "label": "KNOWS", + "from": "Person", + "to": "Person", + "mappedTableSource": { + "table": "knows", + "metaFields": { "id": "id", "from": "from_id", "to": "to_id" } + } + }, + { + "label": "WORKS_AT", + "from": "Person", + "to": "Company", + "mappedTableSource": { + "table": "works_at", + "metaFields": { "id": "id", "from": "person_id", "to": "company_id" } + } + } + ] +} +EOF +``` + ```bash docker run --rm \ --name memgql \ @@ -64,7 +129,7 @@ docker run --rm \ --env MONGODB_DB=test \ --env MAPPING_FILE=/data/mapping.json \ --env BOLT_LISTEN_ADDR=0.0.0.0:7688 \ - -v ./mapping.json:/data/mapping.json \ + --mount type=bind,source="$PWD/mapping.json",target=/data/mapping.json,readonly \ memgraph/memgql:latest ```