Supported GQL Query Features

What works today, split by backend category. “Cypher backends” means Memgraph and Neo4j (translation is largely passthrough); “SQL backends” means PostgreSQL, MySQL and DuckDB. MongoDB is neither — it translates to aggregation pipelines — so it gets its own column.

SQL Server, SAP HANA, 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.

FeatureCypher backendsSQL backendsMongoDB
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 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.
  • DETACH DELETE on SQL backends currently executes as a plain DELETE; 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 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 ((-[: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), 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

-- Connectors (connections only)
ADD CONNECTOR <name> TYPE <type>
    [URI '<uri>'] [PATH '<path>']
    [USER '<user>'] [PASSWORD '<pass>']
    [DATABASE '<db>'] [CATALOG '<catalog>'] [SCHEMA '<schema>'] [GRAPH '<db>']
    [WAREHOUSE '<wh>'] [ROLE '<role>']
    [PRIVATE_KEY_PATH '<path>'] [TOKEN '<token>']
    [TENANT_ID '<tid>'] [CLIENT_ID '<cid>'] [CLIENT_SECRET '<secret>'];
DROP CONNECTOR <name>;
PING <connector>;

-- Graphs (mappings over connectors)
CREATE GRAPH <name> FROM '<json>';        -- inline { "vertices": …, "edges": … } body
CREATE GRAPH <name> FROM FILE '<path>';   -- same body from a file
DROP GRAPH [IF EXISTS] <name>;
ALTER GRAPH <name> SET READ ONLY;
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, fragments, hits, misses, cached_properties

A connector is a connection only; it carries no graph shape. Which options apply depends on the type:

OptionRead by
GRAPH '<db>'Memgraph, Neo4j — selects the Cypher database (it is not a mapping)
DATABASE '<db>'PostgreSQL, MySQL, SQL Server, Oracle (service name), ClickHouse, MongoDB, Snowflake, Fabric (warehouse/lakehouse item), SAP HANA (tenant database of an MDC system)
CATALOG '<catalog>'Iceberg (Trino catalog), Iceberg Direct (warehouse)
SCHEMA '<schema>'Iceberg, Snowflake, Fabric (default dbo), SAP HANA (defaults to the connection user’s own schema); MongoDB accepts it as a fallback for DATABASE
WAREHOUSE / ROLESnowflake session settings
PRIVATE_KEY_PATH / TOKENSnowflake auth (key-pair JWT / programmatic access token); TOKEN is also a Fabric Entra ID access token
TENANT_ID / CLIENT_ID / CLIENT_SECRETFabric service-principal auth (Microsoft Entra ID)
PATH '<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 documented on the Schema File page; the same body loads at boot via --schema.

SET CACHE CONNECTOR makes a graph cache-enabled: touched labels and edge types are copied into the Memgraph cache connector on first read, and later covered queries are served from that cache. See Multiple Graphs → Caching. TTL is in seconds; MAX_BYTES accepts a plain byte count or a K/M/G/T binary suffix (e.g. 8G).

-- Introspection
SHOW CONNECTORS;                 -- registered connectors
SHOW CONNECTIONS;                -- live connections
SHOW GRAPHS [ON CONNECTOR <c>];  -- registered graphs (name, connector(s), type, access, counts)
SHOW GRAPH <name>;               -- one graph's details
SHOW MAPPINGS;                   -- per-graph mappings
SHOW SCHEMA [FOR <graph>];       -- unified routing index: labels, rel-types, properties
EXPORT SCHEMA [TO '<path>'];     -- merged catalog as canonical schema JSON (round-trippable)
REFRESH SCHEMA;                  -- re-introspect live Cypher connections (Memgraph/Neo4j)

-- Query load per source
SHOW STATS;                      -- source, queries, rows, errors, avg_latency_ms, max_latency_ms
RESET STATS;                     -- zero the counters

SHOW STATS reports what each source saw — statements MemGQL dispatched to it and rows it returned — so the load federation puts on a production backend can be measured before rollout:

+--------+---------+------+--------+----------------+----------------+
| source | queries | rows | errors | avg_latency_ms | max_latency_ms |
+--------+---------+------+--------+----------------+----------------+
| mg     |       3 |    6 |      0 |           6.42 |          11.03 |
| pg     |       3 |   12 |      0 |           9.18 |          18.55 |
+--------+---------+------+--------+----------------+----------------+

Latency covers a whole query — the statement and the fetch of its rows — and is reported in fractional milliseconds, since a healthy local source answers in hundreds of microseconds. max_latency_ms sits next to the average because an average hides the tail that shows up as a load problem. RESET STATS zeroes the counters, so a single query’s cost can be measured in isolation.

Counters are keyed by connector. Cache efficacy is keyed by graph and lives in SHOW GRAPH CACHES (hits, misses, resident fragments).

-- Single graph
USE <graph> <query>;

-- Composite
USE <graph1> <query>
UNION | UNION ALL | INTERSECT | INTERSECT ALL | EXCEPT | EXCEPT ALL
USE <graph2> <query>;

-- Routing: routes automatically when the query's labels / rel-types /
-- properties match exactly one registered source (see Multiple Graphs).
<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 (:personPerson). Memgraph sources must run with --schema-info-enabled for property-level introspection. See Multiple Graphs → Routing.

Configuration Reference

General

VariableDefaultDescription
CONNECTOR_TYPEmemgraphConnector to use (see table below)
CONNECTION_TYPE(none)Alias for CONNECTOR_TYPE
BOLT_LISTEN_ADDR127.0.0.1:7688Address the Bolt server binds to

Logging

MemGQL writes log lines to the console (stdout for most levels, stderr for ERROR and CRITICAL) and, in parallel, to a log file. Both destinations receive the same stream, filtered by --log-level. Both are configured via CLI flags on the Bolt server binary.

CLI Flags

FlagDefaultDescription
--log-level=<LEVEL>INFOLogging verbosity for console and file (see below)
--log-file=<PATH>bolt_server.logFile to mirror the (level-filtered) log output to

Log Levels

Levels form a severity ladder; picking a level also emits everything more severe. Values are case-insensitive (--log-level=debug and --log-level=DEBUG are the same; WARN is accepted for WARNING).

LevelWhat it adds
CRITICALCritical failures only
ERROR+ errors
WARNING+ warnings
INFO+ connections, state changes, lifecycle events (default)
DEBUG+ incoming queries and their transpiled Cypher / SQL
TRACE+ plan and per-row execution detail

An unknown level fails at startup with an actionable error listing the valid values. The RUST_LOG environment variable is not consulted.

To see query traffic and what MemGQL translates it into, run with --log-level=DEBUG. The info-queries-only value from earlier releases was removed in v0.7.0; see the changelog.

Enterprise License

VariableDefaultDescription
MEMGQL_ENTERPRISE_LICENSE(none)License key (mglk-...)
MEMGQL_ORGANIZATION_NAME(none)Organization name to verify against license

When set, the license is decoded and verified against the organization name at startup. A valid enterprise license removes connector and connection limits. Without a license, community mode allows up to 2 connectors and 2 simultaneous connections.

Connector Types

ConnectorTranslationBackend
memgraphNone (passthrough)Memgraph
memgraph-gqlGQL -> CypherMemgraph
neo4jNone (passthrough)Neo4j
neo4j-gqlGQL -> CypherNeo4j
postgresGQL -> SQLPostgreSQL
mysqlGQL -> SQLMySQL 8.0+
oracleGQL -> SQLOracle 19c+ (incl. Free 23ai)
sqlserverGQL -> SQLMicrosoft SQL Server (multi mode only)
duckdbGQL -> SQLDuckDB (embedded)
clickhouseGQL -> SQLClickHouse
icebergGQL -> SQLIceberg via Trino
iceberg-directNone (native in-process)Iceberg (REST catalog + Arrow)
pinotGQL -> SQLApache Pinot
mongodbGQL -> aggregation pipelineMongoDB 5.0+
hanaGQL -> SQLSAP HANA 2.0 SPS05+, HANA Cloud, HANA Express
multiPer-connectorMultiple backends simultaneously

Memgraph (memgraph, memgraph-gql)

VariableDefaultDescription
MEMGRAPH_URI127.0.0.1:7687Connection URI
MEMGRAPH_USERuserUsername
MEMGRAPH_PASSpassPassword
MEMGRAPH_DBmemgraphDatabase name

Neo4j (neo4j, neo4j-gql)

VariableDefaultDescription
NEO4J_URI127.0.0.1:7687Connection URI
NEO4J_USERneo4jUsername
NEO4J_PASSpasswordPassword
NEO4J_DBneo4jDatabase name

PostgreSQL (postgres)

VariableDefaultDescription
POSTGRES_URLhost=localhost user=postgres password=postgres dbname=postgreslibpq connection string
MAPPING_FILE(required)Path to JSON mapping file

MySQL (mysql)

VariableDefaultDescription
MYSQL_URLmysql://root:mysql@localhost:3306/testMySQL connection URL (mysql://user:pass@host:port/database)
MAPPING_FILE(required)Path to JSON mapping file

Oracle (oracle)

VariableDefaultDescription
ORACLE_URLoracle://system:oracle@localhost:1521/FREEPDB1Easy Connect URL (oracle://user:pass@host:port/service_name). The default targets Oracle Database Free 23ai (service FREEPDB1).
MAPPING_FILE(required)Path to JSON mapping file (same format as Postgres)

The Oracle connector uses oracle-rs, a pure-Rust implementation of Oracle’s TNS wire protocol, pooled via deadpool-oracle. No OCI / ODPI-C / Instant Client is required at build or runtime: the bundled Docker image ships only the bolt server binary plus TLS roots, and local builds work on macOS, Linux, and Windows without any extra system packages.

SQL Server (sqlserver)

SQL Server has no standalone environment-variable mode; CONNECTOR_TYPE=sqlserver is not supported. Register it at runtime in multi mode with an ADO-style connection string:

ADD CONNECTOR mssql TYPE sqlserver
    URI 'Server=localhost,1433;Database=test;User Id=sa;Password=YourPassword;TrustServerCertificate=true';
-- then map it into a graph
CREATE GRAPH sales FROM FILE '/data/sales.graph.json';

mssql, sql_server, and sql-server are accepted aliases for the sqlserver type. See the SQL Server connector page.

DuckDB (duckdb)

VariableDefaultDescription
DUCKDB_PATH:memory:Path to DuckDB file
MAPPING_FILE(required)Path to JSON mapping file

ClickHouse (clickhouse)

VariableDefaultDescription
CLICKHOUSE_URLhttp://localhost:8123ClickHouse HTTP API URL
CLICKHOUSE_USERdefaultClickHouse user
CLICKHOUSE_PASS(none)ClickHouse password
CLICKHOUSE_DBdefaultClickHouse database
MAPPING_FILE(required)Path to JSON mapping file

Apache Pinot (pinot)

VariableDefaultDescription
PINOT_URLhttp://localhost:8099Pinot broker base URL or full SQL endpoint
PINOT_QUERY_OPTIONSuseMultistageEngine=trueQuery 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.

VariableDefaultDescription
MONGODB_URLmongodb://localhost:27017Connection string (mongodb+srv:// too)
MONGODB_DBtestDefault 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.

SAP HANA (hana)

VariableDefaultDescription
HANA_URLhdbsql://SYSTEM:HXEHana1@localhost:39017Connection URL. The scheme selects the transport: hdbsql:// plaintext, hdbsqls:// TLS (required by SAP HANA Cloud)
HANA_USER(from the URL)DB user, when not written into the URL
HANA_PASSWORD(from the URL)Password, when not written into the URL
HANA_DATABASE(none)Tenant database of a multitenant (MDC) system
MAPPING_FILE(required)Path to JSON mapping file (same format as Postgres)

sap-hana, sap_hana, and saphana are accepted aliases for the hana type.

Prefer HANA_USER / HANA_PASSWORD (or the USER / PASSWORD connector options) over embedding credentials in the URL: a HANA password often contains @ or :, which cannot be expressed inside a URL.

The connector uses hdbconnect, a pure-Rust implementation of HANA’s SQL Command Network Protocol. No ODBC, no JDBC, and no SAP HANA client installation is required at build or runtime.

There is no CATALOG level for HANA: the tenant database is chosen at login, not spelled into a qualified table name, so a HANA table reference is at most schema.table. See the SAP HANA connector page.

Iceberg (iceberg)

VariableDefaultDescription
TRINO_URLhttp://localhost:8080Trino REST API URL
TRINO_USERtrinoTrino user
TRINO_CATALOGicebergTrino catalog
TRINO_SCHEMAdefaultTrino schema
MAPPING_FILE(required)Path to JSON mapping file

Iceberg Direct (iceberg-direct)

Native, in-process execution over Iceberg: reads the REST catalog and object storage (S3/MinIO) directly, no Trino. Read-only.

VariableDefaultDescription
ICEBERG_REST_URIhttp://localhost:8181Iceberg REST Catalog URI
ICEBERG_WAREHOUSEicebergWarehouse / catalog name
ICEBERG_SCHEMAdefaultDefault namespace (schema)
ICEBERG_DIRECT_S3_ENDPOINThttp://localhost:9000S3/MinIO endpoint
ICEBERG_DIRECT_S3_REGIONus-east-1S3 region
ICEBERG_DIRECT_S3_ACCESS_KEY_IDadminS3/MinIO access key
ICEBERG_DIRECT_S3_SECRET_ACCESS_KEYpasswordS3/MinIO secret key
MAPPING_FILE(required)Path to JSON mapping file

S3 path-style access is always enabled (s3.path-style-access=true).

Mapping Schema

A graph’s mapping declares its vertices and edges: labels and relationship types mapped to backend tables (via metaFields and attributes) or to native graph sources. The full format, with field tables and worked examples, lives on the Schema File page.

The same mapping body is used everywhere a graph is defined:

  • --schema=<path> at boot: connectors + graphs in one file.
  • CREATE GRAPH <name> FROM '<json>' / FROM FILE '<path>' at runtime.
  • MAPPING_FILE in single-connector mode: a bare { "vertices": …, "edges": … } body (the connector comes from the environment).

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 longer accepted; loading it raises an actionable error pointing at the new format.