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.
|
- `RDF.PropertyMap` which allow definition of atoms for RDF properties.
|
||||||
Such property maps can be provided to all RDF data structure functions
|
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
|
accepting input data and BGP query patterns with the `:context` opt,
|
||||||
from the property map in the input data.
|
allowing the use of the atoms from the property map in the input data.
|
||||||
- to `RDF.Description`
|
- to `RDF.Description`
|
||||||
- `RDF.Description.subject/1`
|
- `RDF.Description.subject/1`
|
||||||
- `RDF.Description.change_subject/2`
|
- `RDF.Description.change_subject/2`
|
||||||
|
|
|
@ -73,7 +73,7 @@ defmodule RDF.Query do
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(query, graph, opts) do
|
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)
|
execute(bgp, graph, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -141,7 +141,7 @@ defmodule RDF.Query do
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream(query, graph, opts) do
|
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)
|
stream(bgp, graph, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,51 +2,53 @@ defmodule RDF.Query.Builder do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
alias RDF.Query.BGP
|
alias RDF.Query.BGP
|
||||||
alias RDF.{IRI, BlankNode, Literal, Namespace}
|
alias RDF.{IRI, BlankNode, Literal, Namespace, PropertyMap}
|
||||||
import RDF.Utils.Guards
|
import RDF.Utils.Guards
|
||||||
import RDF.Utils
|
import RDF.Utils
|
||||||
|
|
||||||
def bgp(query) do
|
def bgp(query, opts \\ []) do
|
||||||
with {:ok, triple_patterns} <- triple_patterns(query) 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}}
|
{:ok, %BGP{triple_patterns: triple_patterns}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def bgp!(query) do
|
def bgp!(query, opts \\ []) do
|
||||||
case bgp(query) do
|
case bgp(query, opts) do
|
||||||
{:ok, bgp} -> bgp
|
{:ok, bgp} -> bgp
|
||||||
{:error, error} -> raise error
|
{:error, error} -> raise error
|
||||||
end
|
end
|
||||||
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 ->
|
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)}
|
{:ok, List.wrap(triple_pattern)}
|
||||||
end
|
end
|
||||||
end)
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
defp triple_patterns({subject, predications}) when is_map(predications) do
|
defp triple_patterns({subject, predications}, property_map) when is_map(predications) do
|
||||||
triple_patterns({subject, Map.to_list(predications)})
|
triple_patterns({subject, Map.to_list(predications)}, property_map)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp triple_patterns({subject, predications}) do
|
defp triple_patterns({subject, predications}, property_map) do
|
||||||
with {:ok, subject_pattern} <- subject_pattern(subject) do
|
with {:ok, subject_pattern} <- subject_pattern(subject) do
|
||||||
predications
|
predications
|
||||||
|> List.wrap()
|
|> 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
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_triple_patterns(subject_pattern, {predicate, objects}) do
|
defp do_triple_patterns(subject_pattern, {predicate, objects}, property_map) do
|
||||||
with {:ok, predicate_pattern} <- predicate_pattern(predicate) do
|
with {:ok, predicate_pattern} <- predicate_pattern(predicate, property_map) do
|
||||||
objects
|
objects
|
||||||
|> List.wrap()
|
|> List.wrap()
|
||||||
|> map_while_ok(fn object ->
|
|> map_while_ok(fn object ->
|
||||||
|
@ -70,8 +72,8 @@ defmodule RDF.Query.Builder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp predicate_pattern(predicate) do
|
defp predicate_pattern(predicate, property_map) do
|
||||||
value = variable(predicate) || resource(predicate) || property(predicate)
|
value = variable(predicate) || resource(predicate) || property(predicate, property_map)
|
||||||
|
|
||||||
if value do
|
if value do
|
||||||
{:ok, value}
|
{:ok, value}
|
||||||
|
@ -127,8 +129,13 @@ defmodule RDF.Query.Builder do
|
||||||
|
|
||||||
defp resource(_), do: nil
|
defp resource(_), do: nil
|
||||||
|
|
||||||
defp property(:a), do: RDF.type()
|
defp property(:a, _), do: RDF.type()
|
||||||
defp property(_), do: nil
|
|
||||||
|
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(%Literal{} = literal), do: literal
|
||||||
defp literal(value), do: Literal.coerce(value)
|
defp literal(value), do: Literal.coerce(value)
|
||||||
|
@ -144,7 +151,7 @@ defmodule RDF.Query.Builder do
|
||||||
|
|
||||||
def path([subject | rest], opts) do
|
def path([subject | rest], opts) do
|
||||||
path_pattern(subject, rest, [], 0, Keyword.get(opts, :with_elements, false))
|
path_pattern(subject, rest, [], 0, Keyword.get(opts, :with_elements, false))
|
||||||
|> bgp()
|
|> bgp(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def path!(query, opts \\ []) do
|
def path!(query, opts \\ []) do
|
||||||
|
|
|
@ -212,6 +212,30 @@ defmodule RDF.Query.BuilderTest do
|
||||||
{~B"s", :p, :o}
|
{~B"s", :p, :o}
|
||||||
])
|
])
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe "path/2" do
|
describe "path/2" do
|
||||||
|
@ -247,5 +271,24 @@ defmodule RDF.Query.BuilderTest do
|
||||||
{:el0, EX.p2(), :o}
|
{:el0, EX.p2(), :o}
|
||||||
])
|
])
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue