AshNeo4j Cypher Functions for converting Elixir data structures to Cypher query components and running Cypher queries against a Neo4j database. Ideally has no specific knowledge of Ash
Summary
Functions
Converts a node variable, label, predicates and operator to cypher expression
Converts a node variable and labels to basic cypher node expression.
Converts a node variable and optional property map to cypher WHERE conditions and variable prefixed parameters map.
Converts a node variable, labels and optional property map to cypher properties string and variable prefixed parameters map.
Converts a node variable and optional property map to cypher properties string and variable prefixed parameters map.
Backtick-quotes a property name if it contains a dot, so that Neo4j
parses it as a single property reference rather than a nested-property
access. e.g. "location.point" → "`location.point`".
Converts a relationship variable, label and optional direction to cypher relationship.
Converts a list of property names into a remove properties string.
The list is converted to a string in the format n.key1, n.key2.
Renders a %Cypher.Query{} to a {cypher_string, params} tuple.
Raises AshNeo4j.Error.RequiresCypher25 when the connected server does not
support Cypher 25 (negotiated server version < 2025.06). Call at the top of
any function that emits Cypher 25-only syntax.
Runs some cypher
Rewrites dots in a name as underscores so it is safe to use as a
Cypher parameter key. Neo4j parses $foo.bar as the parameter $foo
followed by a .bar property access, so the dot has to go.
Bare scalar Cypher for a vector function, e.g. for use in ORDER BY.
Functions
Converts a node variable, label, predicates and operator to cypher expression
Examples
iex> AshNeo4j.Cypher.expression(:s, "name", "IN", "[$s_name_0]")
"s.name IN [$s_name_0]"
iex> AshNeo4j.Cypher.expression(:s, "name", "is_nil", true)
"s.name IS NULL"
iex> AshNeo4j.Cypher.expression(:s, "name", "is_nil", false)
"s.name IS NOT NULL"
iex> AshNeo4j.Cypher.expression(:s, "name", "contains", "$s_name_0")
"s.name CONTAINS $s_name_0"
iex> AshNeo4j.Cypher.expression(:s, "name", "contains", "$s_name_0", case_insensitive?: true)
"toLower(s.name) CONTAINS toLower($s_name_0)"
iex> AshNeo4j.Cypher.expression(:s, "name", "=", "$s_name_0", case_insensitive?: true)
"toLower(s.name) = toLower($s_name_0)"
iex> AshNeo4j.Cypher.expression(:n, "bounds", "within_bbox", "$test_point")
"point.withinBBox(n.`bounds.bbSW`, point({longitude: -180, latitude: -90}), $test_point) AND point.withinBBox(n.`bounds.bbNE`, $test_point, point({longitude: 180, latitude: 90}))"
iex> AshNeo4j.Cypher.expression(:n, "bounds", "within_bbox_box", {"$inner_sw", "$inner_ne"})
"point.withinBBox(n.`bounds.bbSW`, point({longitude: -180, latitude: -90}), $inner_sw) AND point.withinBBox(n.`bounds.bbNE`, $inner_ne, point({longitude: 180, latitude: 90}))"
iex> AshNeo4j.Cypher.expression(:n, "location", "st_distance", {"<", "$test_point", "$threshold"})
"point.distance(n.location, $test_point) < $threshold"
iex> AshNeo4j.Cypher.expression(:n, "location", "dwithin", {"$test_point", "$threshold"})
"point.distance(n.location, $test_point) <= $threshold"
iex> AshNeo4j.Cypher.expression(:s, "embedding", "vector_similarity", {">", "$s_embedding_0_vec", "$s_embedding_0_t"})
"vector.similarity.cosine(s.embedding, $s_embedding_0_vec) > $s_embedding_0_t"
iex> AshNeo4j.Cypher.expression(:s, "embedding", "vector_cosine_distance", {"<", "$s_embedding_0_vec", "$s_embedding_0_t"})
"(2.0 * (1.0 - vector.similarity.cosine(s.embedding, $s_embedding_0_vec))) < $s_embedding_0_t"
Converts a node variable and labels to basic cypher node expression.
Examples
iex> AshNeo4j.Cypher.node(:s, [:Actor])
"(s:Actor)"
Converts a node variable and optional property map to cypher WHERE conditions and variable prefixed parameters map.
Examples
iex> AshNeo4j.Cypher.parameterized_conditions(:n, %{name: "Bill Nighy"})
{"n.name = $n_name", %{"n_name" => "Bill Nighy"}}
iex> AshNeo4j.Cypher.parameterized_conditions(:n, %{name: "Bill Nighy", age: 72})
{"n.name = $n_name AND n.age = $n_age", %{"n_name" => "Bill Nighy", "n_age" => 72}}
Converts a node variable, labels and optional property map to cypher properties string and variable prefixed parameters map.
Examples
iex> AshNeo4j.Cypher.parameterized_node(:s, [:Actor])
{"(s:Actor)", %{}}
iex> AshNeo4j.Cypher.parameterized_node(:s, [:Cinema, :Actor], %{name: "Bill Nighy"})
{"(s:Cinema:Actor {name: $s_name})", %{"s_name" =>"Bill Nighy"}} Note: the properties map is converted to parameter names by prefixing the keys with $<variable>, and the original values are returned in a separate map for use as query parameters.
Converts a node variable and optional property map to cypher properties string and variable prefixed parameters map.
Examples
iex> AshNeo4j.Cypher.parameterized_properties(:s)
{"{}", %{}}
iex> AshNeo4j.Cypher.parameterized_properties(:s, %{name: "Bill Nighy"})
{"{name: $s_name}", %{"s_name" =>"Bill Nighy"}}
Backtick-quotes a property name if it contains a dot, so that Neo4j
parses it as a single property reference rather than a nested-property
access. e.g. "location.point" → "`location.point`".
Converts a relationship variable, label and optional direction to cypher relationship.
Examples
iex> AshNeo4j.Cypher.relationship(:r, :ACTED_IN, :outgoing)
"-[r:ACTED_IN]->"
iex> AshNeo4j.Cypher.relationship(:r, :ACTED_IN, :incoming)
"<-[r:ACTED_IN]-"
iex> AshNeo4j.Cypher.relationship(:r, :KNOWS)
"-[r:KNOWS]-"
@spec remove_properties(atom(), maybe_improper_list()) :: binary()
Converts a list of property names into a remove properties string.
The list is converted to a string in the format n.key1, n.key2.
Examples
iex> AshNeo4j.Cypher.remove_properties(:n, [:born, :bafta_winner])
"n.born, n.bafta_winner"
Renders a %Cypher.Query{} to a {cypher_string, params} tuple.
Examples
iex> query = %AshNeo4j.Cypher.Query{
...> clauses: [
...> %AshNeo4j.Cypher.Match{pattern: "(s:Actor)"},
...> %AshNeo4j.Cypher.Return{items: ["s"]},
...> %AshNeo4j.Cypher.Limit{value: 5}
...> ],
...> params: %{}
...> }
iex> AshNeo4j.Cypher.render(query)
{"MATCH (s:Actor) RETURN s LIMIT 5", %{}}
iex> query = %AshNeo4j.Cypher.Query{
...> clauses: [
...> %AshNeo4j.Cypher.Call{
...> branches: [
...> "MATCH (s:Place) WHERE s.uuid = $b0_s_uuid_0 RETURN s",
...> "MATCH (s:Place) WHERE s.uuid = $b1_s_uuid_0 RETURN s"
...> ],
...> union_type: :union_all
...> },
...> %AshNeo4j.Cypher.OptionalMatch{pattern: "(s)-[r]-(d)"},
...> %AshNeo4j.Cypher.Return{items: ["s", "r", "d"]}
...> ],
...> params: %{"b0_s_uuid_0" => "x", "b1_s_uuid_0" => "y"}
...> }
iex> {cypher, _params} = AshNeo4j.Cypher.render(query)
iex> cypher
"CALL { MATCH (s:Place) WHERE s.uuid = $b0_s_uuid_0 RETURN s UNION ALL MATCH (s:Place) WHERE s.uuid = $b1_s_uuid_0 RETURN s } OPTIONAL MATCH (s)-[r]-(d) RETURN s, r, d"
Raises AshNeo4j.Error.RequiresCypher25 when the connected server does not
support Cypher 25 (negotiated server version < 2025.06). Call at the top of
any function that emits Cypher 25-only syntax.
Runs some cypher
Examples
iex> cypher = "CREATE (n:Actor {name: 'Bill Nighy', born: 1949, bafta_winner: true}) RETURN n"
iex> {result, _} = AshNeo4j.Cypher.run(cypher)
iex> result
:ok
iex> cypher = "MATCH (n:Actor {name: $name}) RETURN n"
iex> params = %{name: "Bill Nighy"}
iex> {result, _} = AshNeo4j.Cypher.run(cypher, params)
iex> result
:ok
Rewrites dots in a name as underscores so it is safe to use as a
Cypher parameter key. Neo4j parses $foo.bar as the parameter $foo
followed by a .bar property access, so the dot has to go.
Bare scalar Cypher for a vector function, e.g. for use in ORDER BY.
vec_ref is the parameter reference holding the query embedding ("$q").
vector_similarity is Neo4j's normalised cosine similarity in [0, 1]
(higher = closer); vector_cosine_distance rescales it to pgvector-style
distance in [0, 2] (lower = closer) via 2 * (1 - similarity).
Examples
iex> AshNeo4j.Cypher.vector_scalar(:vector_similarity, :s, "embedding", "$q")
"vector.similarity.cosine(s.embedding, $q)"
iex> AshNeo4j.Cypher.vector_scalar(:vector_cosine_distance, :s, "embedding", "$q")
"(2.0 * (1.0 - vector.similarity.cosine(s.embedding, $q)))"