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 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
docker network create memgql-net
docker run -d --rm \
--name mongodb-dev \
--network memgql-net \
-p 27017:27017 \
mongo:82. Seed data
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 });
JSCheck what landed, straight from mongosh:
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
ids. 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):
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" }
}
}
]
}
EOFdocker 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 \
--mount type=bind,source="$PWD/mapping.json",target=/data/mapping.json,readonly \
memgraph/memgql:latest4. Connect
mgconsole --port 76885. Query
MATCH (p:Person) RETURN p.name, p.age;MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name;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:
MATCH (a:Person) (-[:KNOWS]->()){1,3} (b:Person) RETURN a.name, b.name;For environment variables, see Reference.
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 (-[:R]->()){m,n} | ✓ ($graphLookup) |
Unbounded variable-length (-[:R]->()){m,} | ✓ |
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 (-[:R]-()){m,n} | ✗ — 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.