# `AshNeo4j.Cypher`
[🔗](https://github.com/diffo-dev/ash_neo4j/blob/v0.9.0/lib/cypher.ex#L5)

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

# `expression`

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"
```

# `node`

Converts a node variable and labels to basic cypher node expression.

## Examples
```
iex> AshNeo4j.Cypher.node(:s, [:Actor])
"(s:Actor)"
```

# `parameterized_conditions`

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}}
```

# `parameterized_node`

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.

# `parameterized_properties`

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"}}
```

# `quote_if_dotted`

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`" ``.

# `relationship`

# `relationship`

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]-"
```

# `remove_properties`

```elixir
@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"
```

# `render`

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"
```

# `require_cypher25!`

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.

# `run`

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
```

# `run`

# `run_expecting_deletions`

# `run_expecting_deletions`

# `sanitize_param`

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.

# `vector_scalar`

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)))"
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
