The Three-Step Workflow
Every GDS analysis follows the same basic pattern: Project → Run → Write
This workflow separates your source data from analysis, enabling fast, iterative experimentation on an in-memory projection of your graph without modifying your original graph.
You can choose to run an algorithm and write its results in a single operation instead of two. Or, you can chain multiple algorithm results to feed a final algorithm before writing back to the graph.
What You’ll Learn
By the end of this lesson, you’ll be able to:
-
Apply the Project → Run → Write workflow to any GDS analysis
-
Create graph projections using Cypher projection syntax
-
Run algorithms in different execution modes (stats, stream, mutate, write)
-
Manage in-memory projections by listing and dropping graphs
Why This Workflow?
-
Speed: In-memory projections are faster than querying the database directly.
-
Safety: Your source graph remains unchanged until you explicitly write results back.
-
Flexibility: Run multiple algorithms on the same projection to compare and combine results.
Step 1: Project
The graph projected by GDS is an in-memory structure containing nodes and relationships — just like your main graph.
However, the in-memory graph you create is optimized for topology and property lookup operations.
Projecting graphs
You can create a projection of your graph using Cypher or Native projection.
MATCH (source:Actor)-[r:ACTED_IN]->(target:Movie)
WITH gds.graph.project(
'actors-graph',
source,
target
) AS g
RETURN g.graphName AS graph,
g.nodeCount AS nodes,
g.relationshipCount AS relsCALL gds.graph.project(
'actors-graph-native',
['Actor', 'Movie'],
'ACTED_IN'
)
YIELD graphName AS graph,
nodeCount AS nodes,
relationshipCount AS relsCypher vs Native context
Cypher projection is more flexible and more readily available across Neo4j products. It is primarily used in the Neo4j Browser but you can also use it via the Python driver.
Native projection is ostensibly simpler, and mimics the syntax of Python environments. It is primarily used in the Python driver, but you can also use it via the Neo4j Browser.
Generally, you will use Cypher projection in the Neo4j Browser and Native projection in the Python driver.
In this workshop, we will focus on Cypher projection first, and then introduce Native projection later.
Cypher projection
A Cypher projection follows the same pattern as any other Cypher query you are used to running. However, instead of returning results, the gds.graph.project() procedure creates an in-memory representation based on the MATCH clause.
MATCH (source:Actor)-[r:ACTED_IN]->(target:Movie) // (1)
WITH gds.graph.project( // (2)
'actors-graph',
source,
target
) AS g
RETURN g.graphName AS graph, // (3)
g.nodeCount AS nodes,
g.relationshipCount AS relsThe Cypher projection above has three components:
-
Cypher query - The
MATCHclause defines which nodes and relationships to include. -
Projection call - The
gds.graph.project()procedure creates the projection with a unique name, and defines the source and target nodes. -
Return statement - The
RETURNclause returns metadata about the created projection, aliased asg.
Using Native projections
The native projection below produces the same result, and is more concise, but is less flexible.
To create a native production, use the CALL keyword to invoke the gds.graph.project() procedure and YIELD the metadata.
CALL gds.graph.project(
'actor-graph-native',
['Actor', 'Movie'], // (1)
'ACTED_IN' // (2)
)
YIELD graphName AS graph, // (3)
nodeCount AS nodes,
relationshipCount AS relsThe native projection:
-
Loads the provided labels into the project.
-
Connects those nodes with the provided relationship types.
-
Provides the same metadata, which are accessed using the
YIELDclause.
Cypher vs Native
In the early stages of this workshop, we will focus on Cypher projection.
Later, when you become familiar with the Python driver, we will switch to using Native projection.
Running algorithms
Once you have projected your graph into memory, you can run algorithms on it using the CALL gds.<algorithm>.<mode> command.
Ignore the mode for now. We will cover it in more detail in a moment.
CALL gds.degree.stream( // (1)
'actors-graph', // (2)
{} // (3)
)
YIELD nodeId, score // (4)
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC-
The degree centrality algorithm is called in stream mode.
-
The algorithm is run on the
actors-graphprojection. -
A map of configuration options can be provided as the third argument.
-
Each algorithm yields a unique set of results, which can be processed with Cypher.
Execution modes
There are five execution modes for algorithm commands:
-
Stats: Get summary statistics without viewing individual results
-
Stream: View results directly without storing
-
Mutate: Store results in the projection
-
Write: Persist results to your database
-
Estimate: Check memory requirements before running
You can invoke each of these modes by appending the mode to the algorithm name.
CALL gds.degree.stats('actor-graph-native')Estimate mode
The estimate mode is used to estimate the memory requirements for an algorithm.
CALL gds.degree.stats.estimate('actors-graph', {}) // (1)
YIELD nodeCount, relationshipCount, // (2)
bytesMin, bytesMax, requiredMemory-
Append
.estimateto any execution mode -
Returns graph size and memory requirements
You will learn how and when to use each of these modes throughout this workshop.
Write from algorithm
Ultimately, you will end most GDS sessions by writing your results back to the graph. The CALL gds.<algorithm>.write will run the algorithm directly, and then write results.
CALL gds.degree.write(
'actors-graph',
{
writeProperty: 'degree' // (1)
}
)
YIELD centralityDistribution, nodePropertiesWritten // (2)
RETURN centralityDistribution.min AS minimumScore,
centralityDistribution.mean AS meanScore,
nodePropertiesWritten-
Specify the property name for storing results in the database
-
Write mode yields summary statistics about what was written
Write from projection
You can also write results to your graph projection first, using mutate mode:
CALL gds.degree.mutate(
'actors-graph',
{
mutateProperty: 'degree' // (1)
}
)
YIELD centralityDistribution, nodePropertiesWritten // (2)-
Store results in the projection under this property name
-
Results stay in the projection only—not persisted to the database
Write from graph
Then write from the graph projection back to your main graph.
Write from graph
CALL gds.graph.nodeProperties.write(
'actors-graph', // (1)
['degree'] // (2)
)
YIELD propertiesWritten-
Name of the projection to write from
-
List of properties to write back to the database
List graphs
Even when you’ve finished working on a projection, it will continue to hang around in memory until you either stop the server, or explicitly drop it.
You can see which graphs you have in memory with the gds.graph.list() command:
CALL gds.graph.list() // (1)
YIELD graphName
RETURN graphName-
Returns all in-memory projections and their metadata
Dropping graphs
Once you’ve finished working on your projection, you can drop it from memory.
CALL gds.graph.drop('actors-graph')Dropping graphs
The entire graph will disappear, including any data within it — always make sure to write important information back to your main graph before dropping.
Drop all
Sometimes, you might end up with a bunch of graphs in memory. You can drop them all at once with this pattern.
CALL gds.graph.list()
YIELD graphName // (1)
CALL gds.graph.drop(graphName) // (2)
YIELD graphName AS droppedGraphs
RETURN droppedGraphs-
List all projection names
-
Drop each projection by name
Lesson Summary
In this lesson, you got to grips with the general GDS workflow: project → Run → Write.
In the next lesson, you’ll learn how to project a graph and run your first graph projection.