Current section
Files
Jump to
Current section
Files
lib/cypher/parameterized.ex
defmodule AshAge.Cypher.Parameterized do
@moduledoc """
Parameterized Cypher query builder for Apache AGE.
AGE requires queries in the form:
SELECT * FROM ag_catalog.cypher('graph_name', $$ CYPHER $$, $1) AS (v agtype)
Where `$1` is a JSON-encoded map of parameters.
"""
@doc """
Builds a parameterized Cypher query wrapped for AGE execution.
Returns `{sql_string, [json_params]}` suitable for `Ecto.Adapters.SQL.query/3`.
"""
@spec build(atom() | String.t(), String.t(), map()) :: {String.t(), list()}
def build(graph, cypher, params) do
build(graph, cypher, params, [{:v, :agtype}])
end
@doc """
Builds a parameterized Cypher query with custom return type columns.
`return_types` is a keyword list like `[{:v, :agtype}, {:e, :agtype}]`.
"""
@spec build(atom() | String.t(), String.t(), map(), keyword()) :: {String.t(), list()}
def build(graph, cypher, params, return_types) do
graph_name = validate_and_stringify_graph!(graph)
columns = format_return_columns(return_types)
json_params = Jason.encode!(params)
sql = "SELECT * FROM ag_catalog.cypher('#{graph_name}', $$ #{cypher} $$, $1) AS (#{columns})"
{sql, [json_params]}
end
@doc """
Builds a static Cypher query (no parameters).
Omits the params argument entirely — AGE rejects `NULL` as the third argument.
"""
@spec build_static(atom() | String.t(), String.t()) :: {String.t(), list()}
def build_static(graph, cypher) do
build_static(graph, cypher, [{:v, :agtype}])
end
@doc """
Builds a static Cypher query with custom return type columns.
"""
@spec build_static(atom() | String.t(), String.t(), keyword()) :: {String.t(), list()}
def build_static(graph, cypher, return_types) do
graph_name = validate_and_stringify_graph!(graph)
columns = format_return_columns(return_types)
sql =
"SELECT * FROM ag_catalog.cypher('#{graph_name}', $$ #{cypher} $$) AS (#{columns})"
{sql, []}
end
defp validate_and_stringify_graph!(graph) when is_atom(graph) do
validate_and_stringify_graph!(Atom.to_string(graph))
end
defp validate_and_stringify_graph!(graph) when is_binary(graph) do
AshAge.Migration.validate_identifier!(graph)
end
defp format_return_columns(return_types) do
Enum.map_join(return_types, ", ", fn {name, type} ->
"#{name} #{type}"
end)
end
end