Quick Start

MemGQL is a federated GQL (ISO/IEC 39075) query engine that translates GQL queries into backend-native languages and executes them across graph databases. Clients connect via the Bolt protocol (port 7688) using any Bolt-compatible driver.

Prerequisites

Quick Start: Memgraph + MemGQL

1. Create a Docker network

docker network create memgql-net

2. Start Memgraph

docker run -d --rm \
    --name memgraph-dev \
    --network memgql-net \
    -p 7687:7687 \
    memgraph/memgraph-mage:3.12.0 \
    --log-level=TRACE --also-log-to-stderr

3. Start MemGQL

docker run --rm \
    --name memgql \
    --network memgql-net \
    --stop-timeout 2 \
    -p 7688:7688 \
    --env CONNECTOR_TYPE=memgraph-gql \
    --env MEMGRAPH_URI=memgraph-dev:7687 \
    --env BOLT_LISTEN_ADDR=0.0.0.0:7688 \
    memgraph/memgql:latest

4. Connect with mgconsole

mgconsole --port 7688

5. Seed some data

INSERT
  (lana:Developer {name: "Lana", level: "senior", yoe: 12}),
  (marco:Developer {name: "Marco", level: "mid", yoe: 5}),
  (priya:Developer {name: "Priya", level: "senior", yoe: 9}),
  (rs:Language {name: "Rust", releaseYear: 2010}),
  (go:Language {name: "Go", releaseYear: 2009}),
  (ts:Language {name: "TypeScript", releaseYear: 2012}),
  (acme:Startup {name: "Acme Labs", funding: 4200000}),
  (nova:Startup {name: "Nova AI", funding: 18500000}),
  (lana)-[:WRITES {since: 2018}]->(rs),
  (lana)-[:WRITES {since: 2021}]->(go),
  (marco)-[:WRITES {since: 2022}]->(ts),
  (priya)-[:WRITES {since: 2019}]->(rs),
  (priya)-[:WRITES {since: 2020}]->(ts),
  (lana)-[:MENTORS]->(marco),
  (priya)-[:MENTORS]->(marco),
  (lana)-[:EMPLOYED_AT {role: "CTO"}]->(acme),
  (marco)-[:EMPLOYED_AT {role: "Backend Engineer"}]->(nova),
  (priya)-[:EMPLOYED_AT {role: "Tech Lead"}]->(nova);

6. Run GQL queries

Count all nodes in the graph:

MATCH () RETURN count(*);

Return developers and their experience:

MATCH (d:Developer) RETURN d.name, d.yoe;

Filter with WHERE inside the pattern (GQL syntax):

MATCH (d:Developer WHERE d.yoe > 8) RETURN d.name, d.level;

Label expression with IS keyword:

MATCH (s IS Startup) RETURN s.name, s.funding;

OR label expression — match multiple labels at once:

MATCH (n:Language|Startup) RETURN n;

Edge pattern with direction and type:

MATCH (:Developer)-[w:WRITES]->(lang:Language) RETURN lang.name, w.since;

Edge with WHERE clause:

MATCH (:Developer)-[w:WRITES WHERE w.since < 2020]->(lang:Language) RETURN lang.name;

Path variable binding:

MATCH p = (:Developer)-[:MENTORS]->(:Developer) RETURN p;

Two-hop traversal — who mentors someone employed at a startup:

MATCH (senior:Developer)-[:MENTORS]->(junior:Developer)-[:EMPLOYED_AT]->(s:Startup)
RETURN senior.name, junior.name, s.name;

Variable-length path (quantified path pattern):

MATCH (d:Developer {name: "Lana"})-[:MENTORS]->{1,3}(mentee:Developer) RETURN mentee.name;

Environment Variables

See Reference for the full list of environment variables and connector-specific settings.

Mapping File

A mapping describes how a relational backend’s tables appear as graph labels and relationship types: node labels over tables (vertices), edge types over association tables (edges). The full field reference is on the Schema File page. The example below is a standalone mapping body, the shape MAPPING_FILE expects.

Create a mapping file:

cat > mapping.json << 'EOF'
{
  "vertices": [
    {
      "label": "Person",
      "mappedTableSource": {
        "table": "persons",
        "metaFields": {
          "id": "id"
        }
      },
      "attributes": [
        {
          "name": "name"
        },
        {
          "name": "age",
          "type": "Int"
        }
      ]
    },
    {
      "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

Connecting your data with a schema file

Declare your backends and the graphs over them in one file, boot from it, and query straight away, then add more whenever you need.

1. Write a schema file

A schema file declares the backends (connectors) and the graphs mapped over them. Here is a minimal schema.json with one Memgraph connector and an engineering graph over the data you seeded above:

{
  "connectors": [
    {
      "name": "mg",
      "type": "memgraph",
      "connection": {
        "uri": "memgraph-dev:7687"
      }
    }
  ],
  "graphs": [
    {
      "name": "engineering",
      "vertices": [
        {
          "label": "Developer",
          "mappedGraphSource": {
            "connector": "mg"
          }
        },
        {
          "label": "Language",
          "mappedGraphSource": {
            "connector": "mg"
          }
        }
      ],
      "edges": [
        {
          "label": "WRITES",
          "from": "Developer",
          "to": "Language",
          "mappedGraphSource": {
            "connector": "mg"
          }
        }
      ]
    }
  ]
}

See the Schema File page for every field.

2. Boot from it

Point MemGQL at the file. Every connector connects and every graph registers at startup, with no further setup. This reuses the Memgraph you started above, so stop the MemGQL container you started earlier (Ctrl-C) to free port 7688:

docker run --rm -p 7688:7688 \
    --network memgql-net \
    -e BOLT_LISTEN_ADDR=0.0.0.0:7688 \
    -v "$(pwd)/schema.json:/data/schema.json" \
    memgraph/memgql:latest --schema=/data/schema.json

3. Query

mgconsole --port 7688

No USE clause is needed; the query routes to engineering by its labels.

MATCH (d:Developer)-[:WRITES]->(l:Language) RETURN d.name, l.name LIMIT 5;

4. Add more backends at runtime

Register more connectors and graphs live, without a restart. Changes persist back to schema.json. For example, with a PostgreSQL reachable on the same network, add it and map a store graph over one of its tables:

ADD CONNECTOR pg TYPE postgres URI 'postgresql://postgres:postgres@postgres-dev:5432/postgres';
 
CREATE GRAPH store FROM '{
  "vertices": [
    {
      "label": "Customer",
      "mappedTableSource": {
        "connector": "pg",
        "table": "customers",
        "metaFields": { "id": "id" }
      },
      "attributes": [ { "name": "name" } ]
    }
  ]
}';
 
MATCH (c:Customer) RETURN c.name LIMIT 5;

CREATE GRAPH … FROM also accepts FROM FILE '<path>' (the mapping file format). Pinot can additionally run as a single backend via the CONNECTION_TYPE alias (CONNECTION_TYPE=pinot PINOT_URL=…).

5. Introspection

SHOW CONNECTORS;
SHOW GRAPHS;
SHOW MAPPINGS;
SHOW SCHEMA;
PING mg;

6. Cleanup

DROP GRAPH store;
DROP CONNECTOR pg;

MemGQL Statements Reference

StatementDescription
ADD CONNECTOR <name> TYPE <type> [options...]Register a backend connector (connection)
DROP CONNECTOR <name>Remove a connector
CREATE GRAPH <name> FROM '<json>' | FROM FILE '<path>'Define a graph (mapping) over connectors
DROP GRAPH [IF EXISTS] <name>Remove a graph
ALTER GRAPH <name> SET READ ONLY | READ WRITEToggle a graph’s access mode
ALTER GRAPH <name> SET CACHE CONNECTOR <mg> [...]Cache a graph in Memgraph
USE <graph> <query>Route a query to a named graph
PING <connector>Test a connector is alive
SHOW CONNECTORS / SHOW CONNECTIONSList connectors / live connections
SHOW GRAPHS / SHOW GRAPH <name>List graphs / one graph’s details
SHOW MAPPINGSList per-graph mappings
SHOW SCHEMA / REFRESH SCHEMARouting index / re-introspect
EXPORT SCHEMA [TO '<path>']Export the catalog as JSON