Introduction
You’ve been using Cypher projections throughout this course.
MATCH (source:Movie)-[r:IN_GENRE]->(target:Genre)
WITH gds.graph.project('movies-genres', source, target) AS g
RETURN g.graphName, g.nodeCountIntroduction
The Python client offers the same capabilities through gds.graph.project()—using Python dictionaries instead of Cypher syntax.
This lesson shows how to translate your Cypher projection knowledge into Python.
What You’ll Learn
By the end of this lesson, you’ll be able to:
-
Translate Cypher projections to Python syntax
-
Configure node and relationship properties in projections
-
Set relationship orientation (undirected, reverse)
-
Handle missing properties with default values
-
Choose between
gds.graph.project()andgds.graph.cypher.project()
The Same Concepts, Different Syntax
Both approaches do the same thing:
-
Select nodes by label
-
Select relationships by type
-
Configure properties and orientation
-
Create an in-memory projection
The Python client translates your dictionary configuration into the equivalent Cypher projection.
Basic Projection: Cypher
MATCH (source:Movie)-[r:IN_GENRE]->(target:Genre)
WITH gds.graph.project('movies-genres', source, target) AS g
RETURN g.graphName, g.nodeCountBasic Projection: Python
G, result = gds.graph.project( # (1)
"movies-genres", # Graph name
["Movie", "Genre"], # Node labels # (2)
"IN_GENRE" # Relationship type
)-
Returns a Graph object (
G) and metadata (result) — useGfor all subsequent operations -
Labels from the MATCH pattern become a list; the relationship type becomes a string argument
Basic Projection: The Pattern
The MATCH pattern (source:Movie)-[r:IN_GENRE]→(target:Genre) becomes two arguments:
-
The labels
["Movie", "Genre"] -
The type
"IN_GENRE"
What gds.graph.project() Returns
The method returns two values: a Graph object and projection metadata.
G, result = gds.graph.project(
"movies-genres",
["Movie", "Genre"],
"IN_GENRE"
)The Result Metadata
print(result["graphName"]) # 'movies-genres'
print(result["nodeCount"]) # Number of nodes
print(result["relationshipCount"]) # Number of relationshipsDiscarding Metadata
If you only need the Graph object use _ to discard the results metadata:
G, _ = gds.graph.project(
"movies-genres",
["Movie", "Genre"],
"IN_GENRE"
)Multiple Labels and Types: Cypher
MATCH (source)-[r:ACTED_IN|RATED]->(target:Movie)
WHERE source:Actor OR source:User
WITH gds.graph.project('movie-interactions', source, target) AS g
RETURN gMultiple Labels and Types: Python
G, _ = gds.graph.project(
"movie-interactions",
["Actor", "User", "Movie"], # (1)
["ACTED_IN", "RATED"] # (2)
)-
All node labels from the Cypher
WHERE source:Actor OR source:Userpattern become a simple list -
Multiple relationship types from
r:ACTED_IN|RATEDalso become a list
Adding Node Properties: Cypher
MATCH (source:Actor)-[r:ACTED_IN]->(target:Movie)
WITH gds.graph.project(
'actors-movies-props',
source, target,
{ sourceNodeProperties: source { .born },
targetNodeProperties: target { .year, .imdbRating } }
) AS g
RETURN gAdding Node Properties: Python
G, _ = gds.graph.project(
"actors-movies-props",
{
"Actor": {}, # (1)
"Movie": {"properties": ["year"]} # (2)
},
"ACTED_IN"
)-
Labels without properties use an empty dict
{}— the label is still included in the projection -
When you need properties, the label becomes a dict key with a
propertieslist
When you need properties, labels become dictionary keys with a properties list.
Relationship Properties: Cypher
MATCH (source:User)-[r:RATED]->(target:Movie)
WITH gds.graph.project(
'user-ratings', source, target,
{ relationshipProperties: r { .rating } }
) AS g
RETURN gRelationship Properties: Python
G, _ = gds.graph.project(
"user-ratings",
["User", "Movie"],
{"RATED": {"properties": ["rating"]}} # (1)
)-
Same pattern as nodes: when you need properties or configuration, the type string becomes a dictionary key with a
propertieslist
Same pattern: when you need configuration, the type becomes a dictionary key.
Undirected Relationships: Cypher
MATCH (source:Actor)-[r:ACTED_IN]->(target:Movie)
WITH gds.graph.project(
'actors-movies-undirected',
source, target, {},
{ undirectedRelationshipTypes: ['ACTED_IN'] }
) AS g
RETURN gUndirected Relationships: Python
G, _ = gds.graph.project(
"actors-movies-undirected",
["Actor", "Movie"],
{"ACTED_IN": {"orientation": "UNDIRECTED"}} # (1)
)-
orientationreplaces the CypherundirectedRelationshipTypesconfig — options areNATURAL,REVERSE, orUNDIRECTED
Orientation Options
-
NATURAL— Keep original direction (default) -
REVERSE— Flip all directions -
UNDIRECTED— Treat as bidirectional
Default Values for Missing Properties
Some nodes might be missing properties. Use defaultValue to handle nulls:
G, _ = gds.graph.project(
"with-defaults",
{
"Actor": {
"properties": {
"born": {
"property": "born", # (1)
"defaultValue": 1900
}
}
},
"Movie": {
"properties": {
"year": {"defaultValue": 1900}, # (2)
"imdbRating": {"defaultValue": 0.0},
"runtime": {"defaultValue": 90}
}
}
},
{"ACTED_IN": {"orientation": "UNDIRECTED"}}
)-
Long form:
propertyspecifies the source property name,defaultValueprovides a fallback for nulls -
Short form: when the projected property name matches the source, you can omit the
propertykey
Default Values
Nodes missing the property use the default value instead of causing an error.
This mirrors the coalesce() function you used in Cypher projections.
Monopartite Transformations: Cypher
Creating an Actor-to-Actor network through shared Movies requires a complex MATCH pattern.
MATCH (source:Actor)-[:ACTED_IN]->(:Movie)<-[:ACTED_IN]-(target:Actor)
WITH gds.graph.project('actor-collab', source, target) AS g
RETURN gMonopartite Transformations: Python
The gds.graph.project() method only handles direct label/type projections.
For pattern-based transformations, use gds.graph.cypher.project():
G, result = gds.graph.cypher.project( # (1)
"""
MATCH (source:Actor)-[:ACTED_IN]->(:Movie)<-[:ACTED_IN]-(target:Actor)
RETURN gds.graph.project($graph_name, source, target) # (2)
""",
graph_name='actor-collab'
)-
gds.graph.cypher.project()accepts a Cypher query string — use this when native projection can’t express your pattern -
$graph_nameis a parameterized variable passed as a keyword argument — avoids string injection
Alternative: run_cypher + graph.get()
You can also run Cypher projection directly and retrieve the Graph object afterwards:
result = gds.run_cypher(""" # (1)
MATCH (source:Actor)-[:ACTED_IN]->(:Movie)<-[:ACTED_IN]-(target:Actor)
WITH gds.graph.project('actor-collab-v2', source, target) AS g
RETURN g.graphName, g.nodeCount, g.relationshipCount
""")-
Uses the exact same Cypher syntax from Module 2 — useful if you already have working Cypher projections you want to reuse
Retrieving the Graph Object
G = gds.graph.get("actor-collab-v2")
print(f"Retrieved graph: {G.name()}")
print(f"Node count: {G.node_count()}")This approach uses the exact Cypher syntax you already know.
Full Movies Graph Projection
G, result = gds.graph.project(
"movies-full",
{
"Actor": {"properties": {"born": {"defaultValue": 1900}}},
"User": {},
"Movie": {"properties": {
"imdbRating": {"defaultValue": 0.0},
"year": {"defaultValue": 1900}
}},
"Genre": {} # (1)
},
{
"ACTED_IN": {},
"RATED": {"properties": {"rating": {"defaultValue": 0.0}}}, # (2)
"IN_GENRE": {} # (3)
}
)-
Labels without properties still need an entry —
{}means "include all nodes with this label, no properties" -
Relationship properties follow the same
defaultValuepattern as node properties -
Relationship types without special config use an empty dict — same pattern as labels
When to Use Each Method
| Method | Use when |
|---|---|
|
Projecting by labels and types directly |
|
Complex patterns, aggregation, filtering |
|
You prefer writing raw Cypher |
When to Use Each Method
Most projections work with gds.graph.project().
Use the Cypher-based methods when you need pattern transformations like monopartite projections.
Translation Reference
| Cypher | Python |
|---|---|
|
|
|
|
|
|
|
|
Complex MATCH patterns |
|
Summary
Translating Cypher projections to Python:
-
Labels and types become strings or lists for simple cases
-
Use dictionaries when you need properties or configuration
-
gds.graph.project()handles direct label/type projections -
gds.graph.cypher.project()handles complex MATCH patterns -
Use
defaultValueto handle missing properties
Next: Running algorithms with the Python client.