Add support for RDF.PropertyMaps in BGP and path queries
This commit is contained in:
parent
d3c6336782
commit
cafba9f61f
4 changed files with 74 additions and 24 deletions
|
@ -20,8 +20,8 @@ are specified.
|
|||
|
||||
- `RDF.PropertyMap` which allow definition of atoms for RDF properties.
|
||||
Such property maps can be provided to all RDF data structure functions
|
||||
accepting input data with the `:context` opt, allowing the use of the atoms
|
||||
from the property map in the input data.
|
||||
accepting input data and BGP query patterns with the `:context` opt,
|
||||
allowing the use of the atoms from the property map in the input data.
|
||||
- to `RDF.Description`
|
||||
- `RDF.Description.subject/1`
|
||||
- `RDF.Description.change_subject/2`
|
||||
|
|
|
@ -73,7 +73,7 @@ defmodule RDF.Query do
|
|||
end
|
||||
|
||||
def execute(query, graph, opts) do
|
||||
with {:ok, bgp} <- Builder.bgp(query) do
|
||||
with {:ok, bgp} <- Builder.bgp(query, opts) do
|
||||
execute(bgp, graph, opts)
|
||||
end
|
||||
end
|
||||
|
@ -141,7 +141,7 @@ defmodule RDF.Query do
|
|||
end
|
||||
|
||||
def stream(query, graph, opts) do
|
||||
with {:ok, bgp} <- Builder.bgp(query) do
|
||||
with {:ok, bgp} <- Builder.bgp(query, opts) do
|
||||
stream(bgp, graph, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,51 +2,53 @@ defmodule RDF.Query.Builder do
|
|||
@moduledoc false
|
||||
|
||||
alias RDF.Query.BGP
|
||||
alias RDF.{IRI, BlankNode, Literal, Namespace}
|
||||
alias RDF.{IRI, BlankNode, Literal, Namespace, PropertyMap}
|
||||
import RDF.Utils.Guards
|
||||
import RDF.Utils
|
||||
|
||||
def bgp(query) do
|
||||
with {:ok, triple_patterns} <- triple_patterns(query) do
|
||||
def bgp(query, opts \\ []) do
|
||||
property_map = if context = Keyword.get(opts, :context), do: PropertyMap.new(context)
|
||||
|
||||
with {:ok, triple_patterns} <- triple_patterns(query, property_map) do
|
||||
{:ok, %BGP{triple_patterns: triple_patterns}}
|
||||
end
|
||||
end
|
||||
|
||||
def bgp!(query) do
|
||||
case bgp(query) do
|
||||
def bgp!(query, opts \\ []) do
|
||||
case bgp(query, opts) do
|
||||
{:ok, bgp} -> bgp
|
||||
{:error, error} -> raise error
|
||||
end
|
||||
end
|
||||
|
||||
defp triple_patterns(query) when is_list(query) or is_map(query) do
|
||||
defp triple_patterns(query, property_map) when is_list(query) or is_map(query) do
|
||||
flat_map_while_ok(query, fn triple ->
|
||||
with {:ok, triple_pattern} <- triple_patterns(triple) do
|
||||
with {:ok, triple_pattern} <- triple_patterns(triple, property_map) do
|
||||
{:ok, List.wrap(triple_pattern)}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp triple_patterns({subject, predicate, objects}) do
|
||||
defp triple_patterns({subject, predicate, objects}, property_map) do
|
||||
with {:ok, subject_pattern} <- subject_pattern(subject) do
|
||||
do_triple_patterns(subject_pattern, {predicate, objects})
|
||||
do_triple_patterns(subject_pattern, {predicate, objects}, property_map)
|
||||
end
|
||||
end
|
||||
|
||||
defp triple_patterns({subject, predications}) when is_map(predications) do
|
||||
triple_patterns({subject, Map.to_list(predications)})
|
||||
defp triple_patterns({subject, predications}, property_map) when is_map(predications) do
|
||||
triple_patterns({subject, Map.to_list(predications)}, property_map)
|
||||
end
|
||||
|
||||
defp triple_patterns({subject, predications}) do
|
||||
defp triple_patterns({subject, predications}, property_map) do
|
||||
with {:ok, subject_pattern} <- subject_pattern(subject) do
|
||||
predications
|
||||
|> List.wrap()
|
||||
|> flat_map_while_ok(&do_triple_patterns(subject_pattern, &1))
|
||||
|> flat_map_while_ok(&do_triple_patterns(subject_pattern, &1, property_map))
|
||||
end
|
||||
end
|
||||
|
||||
defp do_triple_patterns(subject_pattern, {predicate, objects}) do
|
||||
with {:ok, predicate_pattern} <- predicate_pattern(predicate) do
|
||||
defp do_triple_patterns(subject_pattern, {predicate, objects}, property_map) do
|
||||
with {:ok, predicate_pattern} <- predicate_pattern(predicate, property_map) do
|
||||
objects
|
||||
|> List.wrap()
|
||||
|> map_while_ok(fn object ->
|
||||
|
@ -70,8 +72,8 @@ defmodule RDF.Query.Builder do
|
|||
end
|
||||
end
|
||||
|
||||
defp predicate_pattern(predicate) do
|
||||
value = variable(predicate) || resource(predicate) || property(predicate)
|
||||
defp predicate_pattern(predicate, property_map) do
|
||||
value = variable(predicate) || resource(predicate) || property(predicate, property_map)
|
||||
|
||||
if value do
|
||||
{:ok, value}
|
||||
|
@ -127,8 +129,13 @@ defmodule RDF.Query.Builder do
|
|||
|
||||
defp resource(_), do: nil
|
||||
|
||||
defp property(:a), do: RDF.type()
|
||||
defp property(_), do: nil
|
||||
defp property(:a, _), do: RDF.type()
|
||||
|
||||
defp property(term, property_map) when is_atom(term) and not is_nil(property_map) do
|
||||
PropertyMap.iri(property_map, term)
|
||||
end
|
||||
|
||||
defp property(_, _), do: nil
|
||||
|
||||
defp literal(%Literal{} = literal), do: literal
|
||||
defp literal(value), do: Literal.coerce(value)
|
||||
|
@ -144,7 +151,7 @@ defmodule RDF.Query.Builder do
|
|||
|
||||
def path([subject | rest], opts) do
|
||||
path_pattern(subject, rest, [], 0, Keyword.get(opts, :with_elements, false))
|
||||
|> bgp()
|
||||
|> bgp(opts)
|
||||
end
|
||||
|
||||
def path!(query, opts \\ []) do
|
||||
|
|
|
@ -212,6 +212,30 @@ defmodule RDF.Query.BuilderTest do
|
|||
{~B"s", :p, :o}
|
||||
])
|
||||
end
|
||||
|
||||
test "with contexts" do
|
||||
assert Builder.bgp(
|
||||
%{
|
||||
s?: %{
|
||||
p1: :o?,
|
||||
p2: [42, true]
|
||||
},
|
||||
o?: [p3: ["foo", "bar"]]
|
||||
},
|
||||
context: %{
|
||||
p1: EX.p1(),
|
||||
p2: EX.p2(),
|
||||
p3: EX.p3()
|
||||
}
|
||||
) ==
|
||||
ok_bgp_struct([
|
||||
{:o, EX.p3(), ~L"foo"},
|
||||
{:o, EX.p3(), ~L"bar"},
|
||||
{:s, EX.p1(), :o},
|
||||
{:s, EX.p2(), XSD.integer(42)},
|
||||
{:s, EX.p2(), XSD.true()}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
describe "path/2" do
|
||||
|
@ -247,5 +271,24 @@ defmodule RDF.Query.BuilderTest do
|
|||
{:el0, EX.p2(), :o}
|
||||
])
|
||||
end
|
||||
|
||||
test "with contexts" do
|
||||
property_map = %{
|
||||
p1: EX.p1(),
|
||||
p2: EX.p2()
|
||||
}
|
||||
|
||||
assert Builder.path([EX.s(), :p1, :p2, EX.o()], context: property_map) ==
|
||||
ok_bgp_struct([
|
||||
{EX.s(), EX.p1(), RDF.bnode("0")},
|
||||
{RDF.bnode("0"), EX.p2(), EX.o()}
|
||||
])
|
||||
|
||||
assert Builder.path([EX.s(), :p1, :p2, :o?], context: property_map, with_elements: true) ==
|
||||
ok_bgp_struct([
|
||||
{EX.s(), EX.p1(), :el0},
|
||||
{:el0, EX.p2(), :o}
|
||||
])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue