Current section
Files
Jump to
Current section
Files
lib/query.ex
defmodule AshAge.Query do
@moduledoc """
Query structure for AGE graph queries.
"""
defstruct [
:resource,
:graph,
:label,
:repo,
:expression,
:limit,
:offset,
filters: [],
sort: [],
params: %{}
]
@type t :: %__MODULE__{
resource: module(),
graph: atom(),
label: atom() | String.t(),
repo: module(),
expression: Ash.Filter.t() | nil,
limit: non_neg_integer() | nil,
offset: non_neg_integer() | nil,
filters: [String.t()],
sort: [{atom(), :asc | :desc}],
params: map()
}
@doc """
Converts a query to Cypher with parameters.
Returns `{cypher_string, params_map}`.
"""
@spec to_cypher(t()) :: {String.t(), map()}
def to_cypher(%__MODULE__{} = query) do
{where_parts, query} = build_where(query)
parts =
["MATCH (n:#{query.label})"] ++
build_where_clause(where_parts) ++
["RETURN n"] ++
build_order_by(query.sort) ++
build_skip(query.offset) ++
build_limit(query.limit)
{Enum.join(parts, " "), query.params}
end
@doc """
Adds a parameter to the query, returning the updated query and a `$paramN` reference.
"""
@spec add_param(t(), term()) :: {t(), String.t()}
def add_param(%__MODULE__{params: params} = query, value) do
key = "param#{map_size(params) + 1}"
{%{query | params: Map.put(params, key, value)}, "$#{key}"}
end
defp build_where(query) do
filter_clauses = query.filters
{expression_clauses, query} =
if query.expression do
case AshAge.Query.Filter.translate(query.expression, query) do
{:ok, query, ""} -> {[], query}
{:ok, query, clause} -> {[clause], query}
_ -> {[], query}
end
else
{[], query}
end
{filter_clauses ++ expression_clauses, query}
end
defp build_where_clause([]), do: []
defp build_where_clause(parts) do
["WHERE " <> Enum.join(parts, " AND ")]
end
defp build_order_by([]), do: []
defp build_order_by(sort_clauses) do
order =
Enum.map_join(sort_clauses, ", ", fn {field, direction} ->
dir = if direction == :desc, do: "DESC", else: "ASC"
"n.#{field} #{dir}"
end)
["ORDER BY " <> order]
end
defp build_skip(nil), do: []
defp build_skip(offset), do: ["SKIP #{offset}"]
defp build_limit(nil), do: []
defp build_limit(limit), do: ["LIMIT #{limit}"]
end