AWS ę°ę®åŗčŖåØåčæē§»ļ¼neo4j-to-neptune å®ēØęå
Source: AWS - Databases
When migrating from Neo4j to Amazon Neptune, you should ensure Cypher query compatibility is maintained. The Amazon Neptune openCypher implementation generally supports the clauses, operators, expressions, and functions defined in the openCypher specification, but with important limitations and differences that require careful attention. The migration process requires you to systematically analyze existing queries to identify Neo4j-specific syntax, functions, and patterns that you need to transform to comply with Neptuneās specifications so that your graph database applications function correctly in the Amazon Web Services (AWS) environment.
For detailed guidance on migrating a Neo4j graph database to a Neptune database, see Automate your Neo4j to Amazon Neptune migration using the neo4j-to-neptune utility, which helps you streamline you migration process by providing flexible options for both fully automated and step-by-step approaches.
In this post, we show you how to validate Neo4j Cypher queries before migrating to Neptune using the openCypher Compatibility Checker tool. You can use this tool to identify compatibility issues early in your migration process, reducing migration time and effort.
Solution overview
The openCypher Compatibility Checker tool provides detailed reports highlighting specific areas of concern, such as unsupported functions, syntax variations, and Neo4j-specific features that need alternative implementations in Neptune. The tool can detect common conversion challenges, including differences in string matching functions, aggregation methods, and pattern matching syntax, offering suggestions alternatives that are compatible with Neptune.
When you encounter Neo4j-specific functions such as APOCĀ procedures or custom plugins, the tool flags these instances and can recommend equivalent Neptune functions or alternative approaches to achieve the same functionality. It generates comprehensive reports that detail which parts of your queries can be directly converted, which require modification, and which might need complete restructuring. Through this systematic approach, you can prioritize your conversion efforts and estimate the work required for a successful migration.The solution architecture is straightforward. It doesnāt require an AWS environment; the tool can be downloaded from GitHub and used on your client machine or desktop. The following diagram shows the workflow of the process of using the openCypher Compatibility Checker tool.

The openCypher Compatibility Checker tool processes a JSON file of Cypher queries, evaluates their compatibility with the openCypher specification, and generates a JSON output containing either automatically converted openCypher queries or recommended alternatives for queries that require manual adjustment.
Prerequisites
To implement the solution, you need to have the following prerequisites in place:
- Java version 17 (jdk-17.0.12) or later. For information on how to install, see the JDK Installation Guide.
- Your Neo4j Cypher queries exported in JSON format.
- Download the openCypher Compatibility Checker tool.
How to use the tool
The openCypher Compatibility Checker expects a JSON input file with a specific structure:
{
"targetSystem": "na|ndb",
"queries": [
{
"id": 1,
"query": "_query_text_"
}
]
}Ā
The JSON object has two main fields. TheĀ targetSystemĀ field specifies the target database system na(Neptune Analytics) or ndb (Neptune database), while theĀ queriesĀ array contains multiple query objects to be processed in a single batch. Each query object includes anĀ id field, a unique numeric identifier that distinguishes each query, and aĀ queryĀ field containing the actual Cypher query text to be analyzed and converted. You can include as many queries as needed in the array, with each requiring a unique ID to track results and identify which queries need attention. The tool processes all queries in the batch and returns results mapped to their corresponding IDs, so you can analyze multiple queries efficiently in a single execution.
The following example JSON structure defines a batch of Cypher queries for compatibility checking. The tool processes two queries: one retrieving Person nodes (id: 1) and another finding User-FOLLOWS relationships (id: 2), returning compatibility analysis and any necessary transformations for each.
{
"targetSystem": "na",
"queries": [
{
"id": 1,
"query": "MATCH (n:Person) RETURN n"
},
{
"id": 2,
"query": "MATCH (n:User)-[:FOLLOWS]->(m) RETURN n,m"
}
]
}
Example input: Sample Cypher queries for compatibility
To demonstrate the working of the openCypher Compatibility Checker tool, Iāll use the following example file, which contains a few cypher queries:
{
"targetSystem": "NA",
"queries": [
{
"id": 1,
"query": "match p=(a:Start)-[:HOP*1..]->(z:End) where none(node IN nodes(p) where node.class ='D') return p"
},
{
"id": 2,
"query": "MATCH p=(a:airport {code: 'ANC'})-[r:route*1..3]->(z:airport {code: 'AUS'}) RETURN p, reduce(totalDist=0, r in relationships(p) | totalDist + r.dist) AS totalDist ORDER BY totalDist LIMIT 5"
},
{
"id": 3,
"query": "MATCH p=(:airport {code: 'ANC'})-[*1..2]->({code: 'AUS'}) FOREACH (n IN nodes(p) | SET n.visited = true)"
},
{
"id": 4,
"query": "MATCH p=(start)-[*]->(finish) WHERE start.name = 'A' AND finish.name = 'D' FOREACH (n IN nodes(p) | SET n.marked = true)"
},
{
"id": 5,
"query": "MATCH (n:airport {region: 'US-AK'}) CALL apoc.do.when( n.runways>=3, 'SET n.is_large_airport=true RETURN n','SET n.is_large_airport=false RETURN n', {n:n} ) YIELD value WITH collect(value.n) as airports RETURN size([a in airports where a.is_large_airport]) as large_airport_count, size([a in airports where NOT a.is_large_airport]) as small_airport_count"
},
{
"id": 6,
"query": "MATCH (n:airport {region: 'US-AK'}) CALL apoc.case([ n.runways=1, 'RETURN \\\"Has one runway\\\" as b', n.runways=2, 'RETURN \\\"Has two runways\\\" as b'], 'RETURN \\\"Has more than 2 runways\\\" as b') YIELD value RETURN {type: value.b,airport: n}"
}
]
}
Ā
The example contains six queries with varying compatibility levels for the Neptune openCypher format.
Query ID 1Ā uses theĀ none()Ā predicate function to filter paths. This query is supported but requires minor modification as explained in the Neptune documentation on rewriting None, All, and Any predicate functions.
# Neo4J Cypher code
match p=(a:Start)-[:HOP*1..]->(z:End)
where none(node IN nodes(p) where node.class ='D')
return p
Query ID 2Ā uses theĀ reduce()Ā function to calculate total distance across relationships. This query is supported but needs minor modification as detailed in the Neptune documentation on rewriting the CypherĀ reduce()Ā function in openCypher.
# Neo4J Example
match p=(a:airport {code: 'ANC'})-[r:route*1..3]->(z:airport {code: 'AUS'})
return p, reduce(totalDist=0, r in relationships(p) | totalDist + r.dist) AS totalDist
ORDER BY totalDist LIMIT 5
Query IDs 3 and 4 use theĀ FOREACHĀ clause to update node properties along a path. These queries arenāt supported and require a complete rewrite using theĀ UNWINDĀ clause combined with appropriate pattern matching, as explained in the Neptune documentation on rewriting the CypherĀ FOREACHĀ clause.
# Neo4J Example
MATCH p=(:airport {code: 'ANC'})-[*1..2]->({code: 'AUS'})
FOREACH (n IN nodes(p) | SET n.visited = true)
MATCH p=(start)-[*]->(finish) WHERE start.name = 'A' AND finish.name = 'D' FOREACH (n IN nodes(p) | SET n.marked = true)"
Query ID 5Ā uses the Neo4j APOC procedureĀ apoc.do.when()Ā for conditional logic. This query isnāt supported because APOC procedures are Neo4j-specific. It requires a rewrite using the alternatives built into Neptune, such as list comprehension capabilities with theĀ UNWIND clause, as explained in the Neptune documentation on rewriting Neo4j APOC procedures.
# Neo4J Example
MATCH (n:airport {region: 'US-AK'})
CALL apoc.do.when(
n.runways>=3,
'SET n.is_large_airport=true RETURN n',
'SET n.is_large_airport=false RETURN n',
{n:n}
) YIELD value
WITH collect(value.n) as airports
RETURN size([a in airports where a.is_large_airport]) as large_airport_count,
size([a in airports where NOT a.is_large_airport]) as small_airport_count
Query ID 6Ā uses the APOC procedureĀ apoc.case()Ā for case-based logic. This query isnāt supported in the Neptune openCypher implementation and requires rewriting using the native capabilities Neptune. While the tool identifies this incompatibility, it doesnāt provide a specific replacement suggestion for this APOC function. See the Neptune compatibility documentation with Neo4j to find guidance on converting unsupported APOC functions into Neptune-supported openCypher queries or equivalent Neptune functions.
Executing the compatibility check and interpreting results
This section demonstrates how to use the openCypher Compatibility Checker to analyze the input file. The tool accepts two parameters:Ā --input to specify the source file containing queries to process (for example,Ā --input queries.json), andĀ --outputĀ to define where the migration results should be saved (for example,Ā --output results.json). When executed, the application reads queries from the input file, applies compatibility analysis and transformation logic, and writes the results to the output file. This migration helper streamlines the process of converting queries from Neo4jās Cypher dialect to openCypher-compliant syntax compatible with Neptune.
java -jar NeptuneNeo4jMigrationHelper-1.0.jar --input queries.json --output results.json
After running the preceding command, the compatibility tool analysis results are written to theĀ results.jsonfile. You can view the output by opening this file in any text editor or JSON viewer. The results file contains a structured JSON response with aĀ resultsĀ array, where each element corresponds to one of your input queries, matched by its uniqueĀ id. For each query, the output indicates whether itāsĀ supportedĀ (true or false) and provides anĀ errorDefinitionsĀ array that details any compatibility issues found. When a query is unsupported, the error definitions include the exactĀ positionĀ of the problematic syntax, theĀ nameĀ of the unsupported feature, a potentialĀ replacementĀ suggestion (if available), and aĀ descriptionĀ explaining why the feature isnāt supported and how you might address it. This structured format simplifies identifying which queries need modification and understanding exactly what changes are required for Neptune compatibility. The following JSON shows the results.json file:
{
"results" : [ {
"id" : 1,
"supported" : true,
"errorDefinitions" : [ ]
}, {
"id" : 2,
"supported" : true,
"errorDefinitions" : [ ]
}, {
"id" : 3,
"supported" : false,
"errorDefinitions" : [ {
"position" : "line 1, column 59 (offset: 58)",
"name" : "FOREACH",
"replacement" : "",
"description" : "FOREACH is not supported in this release"
} ]
}, {
"id" : 4,
"supported" : false,
"errorDefinitions" : [ {
"position" : "line 1, column 76 (offset: 75)",
"name" : "FOREACH",
"replacement" : "",
"description" : "FOREACH is not supported in this release"
} ]
}, {
"id" : 5,
"supported" : false,
"errorDefinitions" : [ {
"position" : "line 1, column 37 (offset: 36)",
"name" : "CALL",
"replacement" : "",
"description" : "CALL clause inputs of type GreaterThanOrEqual(Property(Variable(n),PropertyKeyName(runways,None)),SignedDecimalIntegerLiteral(3))"
}, {
"position" : "line 1, column 37 (offset: 36)",
"name" : "apoc.do",
"replacement" : "List Comprehension capabilities with the UNWIND clause",
"description" : "apoc.do is not supported in this release but try replacing with List Comprehension capabilities with the UNWIND clause"
} ]
}, {
"id" : 6,
"supported" : false,
"errorDefinitions" : [ {
"position" : "line 1, column 37 (offset: 36)",
"name" : "apoc.case",
"replacement" : "",
"description" : "apoc.case is not supported in this release"
} ]
} ]
}
The tool generates a detailed report showing:
- Whether each query is supported
- The position of unsupported elements in the cypher query
- Names of unsupported functions and clauses
- Suggested replacements (if available)
- Detailed error descriptions
Common migration scenarios
This guide outlines key adaptations and technical considerations for a successful migration.
Predicate functions
NONE(): Can be implemented using list comprehensionALL(): Can be rewritten using list comprehensionANY(): Achievable through list comprehension patterns
Aggregation and processing
reduce(): Can be implemented using a combination of:- List comprehension
UNWINDĀ clause- Appropriate aggregation functions
Control flow modifications
FOREACHĀ clause:- Replace withĀ
UNWINDĀ operations - Combine with appropriate pattern matching
- Use multipleĀ
MATCHĀ orĀCREATEĀ statements as needed
- Replace withĀ
Procedure adaptations
- Neo4j APOC procedures:
- Use the built-in Neptune alternatives where available
- Implement custom solutions for unsupported functionalities
- Use the built-in graph operations available in Neptune
For more information, see Rewriting Cypher queries to run in openCypher on Neptune.
Conclusion
The openCypher Compatibility Checker streamlines Neo4j to Neptune migrations by identifying compatibility issues early and providing clear guidance on necessary changes. While the tool automates incompatibility detection, you should carefully evaluate each case and thoroughly test rewritten queries to verify that they maintain the intended functionality in a Neptune openCypher implementation.