rdf-ex/test/support/query/query_planner_helper.ex

129 lines
3.8 KiB
Elixir

defmodule RDF.QueryPlannerHelper do
import RDF.Guards
def better?(left, right) do
left = simplify_triple(left)
right = simplify_triple(right)
less_variables?(left, right) or
(same_count_of_variables?(left, right) and better_positioning?(left, right)) or
false
end
defp simplify_triple({s, p, o}), do: {simplify_term(s), simplify_term(p), simplify_term(o)}
defp simplify_term(triple) when is_triple(triple) do
case variable_count(triple) do
0 -> 0
variable_count -> {variable_count}
end
end
defp simplify_term(variable) when is_atom(variable), do: 1
defp simplify_term(_), do: 0
defp variable_count({count}), do: count
defp variable_count(variable) when is_atom(variable), do: 1
defp variable_count(triple) when is_tuple(triple) do
triple
|> Tuple.to_list()
|> Enum.count(&variable?/1)
end
defp variable_count(_), do: 0
defp variable?(variable) when is_atom(variable), do: true
defp variable?(%type{}) when type in [RDF.IRI, RDF.Literal, RDF.BlankNode], do: false
defp variable?(count) when is_integer(count), do: count > 0
defp less_variables?(left, right) do
valued_variable_count(left) < valued_variable_count(right)
end
defp same_count_of_variables?(left, right) do
valued_variable_count(left) == valued_variable_count(right)
end
defp valued_variable_count(triple) when is_triple(triple) do
triple
|> Tuple.to_list()
|> Enum.map(&valued_variable_count/1)
|> Enum.sum()
end
defp valued_variable_count({3}), do: 1
defp valued_variable_count({_}), do: 0
defp valued_variable_count(count), do: count
defp better_positioning?(left, right) do
Enum.reduce_while(0..2, nil, fn i, _ ->
case better_term?(elem(left, i), elem(right, i)) do
nil -> {:cont, true}
result -> {:halt, result}
end
end)
end
defp better_term?(term, term), do: nil
defp better_term?(1, 0), do: false
defp better_term?(0, 1), do: true
defp better_term?({_}, 0), do: false
defp better_term?({_}, 1), do: true
defp better_term?(0, {_}), do: true
defp better_term?(1, {_}), do: false
defp better_term?({left_count}, {right_count}), do: left_count < right_count
#############################################################################
# functions for generating all possible combinations of triple patterns
# with quoted triples (but NOT nested quoted triples)
def all_combinations do
all_tps = all_tps()
for left <- all_tps, right <- all_tps, do: {left, right}
end
defp all_tps do
elements = [:var, :term, :quoted_triple]
for s <- elements, p <- elements, o <- elements do
{s, p, o}
end
|> Enum.reject(&match?({_, :quoted_triple, _}, &1))
|> Enum.map(fn {s, p, o} ->
{tp_term(s, :subject), tp_term(p, :predicate), tp_term(o, :object)}
end)
|> Enum.flat_map(&expand_quoted_triples/1)
end
defp expand_quoted_triples({{}, p, o}) do
quoted_triples("s")
|> Enum.map(&{&1, p, o})
|> Enum.flat_map(&expand_quoted_triples(&1))
end
defp expand_quoted_triples({s, p, {}}) do
quoted_triples("o")
|> Enum.map(&{s, p, &1})
|> Enum.flat_map(&expand_quoted_triples(&1))
end
defp expand_quoted_triples(triple), do: [triple]
defp quoted_triples(pos) do
[
{RDF.iri("urn:QS#{pos}"), RDF.iri("urn:QP#{pos}"), RDF.iri("urn:QO#{pos}")},
{:"qs_#{pos}?", RDF.iri("urn:QP#{pos}"), RDF.iri("urn:QO#{pos}")},
{:"qs_#{pos}?", :"qp_#{pos}?", RDF.iri("urn:QO#{pos}")},
{:"qs_#{pos}?", :"qp_#{pos}?", :"qo_#{pos}?"}
]
end
defp tp_term(:var, :subject), do: :s?
defp tp_term(:var, :predicate), do: :p?
defp tp_term(:var, :object), do: :o?
defp tp_term(:term, :subject), do: RDF.iri("urn:S")
defp tp_term(:term, :predicate), do: RDF.iri("urn:P")
defp tp_term(:term, :object), do: RDF.iri("urn:O")
defp tp_term(:quoted_triple, _), do: {}
end