-
Notifications
You must be signed in to change notification settings - Fork 8
/
query.ex
232 lines (179 loc) · 6.65 KB
/
query.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
defmodule RDF.Query do
@moduledoc """
The RDF Graph query API.
"""
alias RDF.Graph
alias RDF.Query.{BGP, Builder}
@default_matcher RDF.Query.BGP.Stream
@doc """
Execute the given `query` against the given `graph`.
The `query` can be given directly as `RDF.Query.BGP` struct created with one
of the builder functions in this module or as basic graph pattern expression
accepted by `bgp/1`.
The result is a list of maps with the solutions for the variables in the graph
pattern query and will be returned in a `:ok` tuple. In case of an error a
`:error` tuple is returned.
## Example
Let's assume we have an `example_graph` with these triples:
```turtle
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix ex: <http://example.com/> .
ex:Outlaw
foaf:name "Johnny Lee Outlaw" ;
foaf:mbox <mailto:jlow@example.com> .
ex:Goodguy
foaf:name "Peter Goodguy" ;
foaf:mbox <mailto:peter@example.org> ;
foaf:friend ex:Outlaw .
```
iex> {:_, FOAF.name, :name?} |> RDF.Query.execute(example_graph())
{:ok, [%{name: ~L"Peter Goodguy"}, %{name: ~L"Johnny Lee Outlaw"}]}
iex> [
...> {:_, FOAF.name, :name?},
...> {:_, FOAF.mbox, :mbox?},
...> ] |> RDF.Query.execute(example_graph())
{:ok, [
%{name: ~L"Peter Goodguy", mbox: ~I<mailto:peter@example.org>},
%{name: ~L"Johnny Lee Outlaw", mbox: ~I<mailto:jlow@example.com>}
]}
iex> query = [
...> {:_, FOAF.name, :name?},
...> {:_, FOAF.mbox, :mbox?},
...> ] |> RDF.Query.bgp()
...> RDF.Query.execute(query, example_graph())
{:ok, [
%{name: ~L"Peter Goodguy", mbox: ~I<mailto:peter@example.org>},
%{name: ~L"Johnny Lee Outlaw", mbox: ~I<mailto:jlow@example.com>}
]}
iex> [
...> EX.Goodguy, FOAF.friend, FOAF.name, :name?
...> ] |> RDF.Query.path() |> RDF.Query.execute(example_graph())
{:ok, [%{name: ~L"Johnny Lee Outlaw"}]}
"""
def execute(query, graph, opts \\ [])
def execute(%BGP{} = query, %Graph{} = graph, opts) do
matcher = Keyword.get(opts, :matcher, @default_matcher)
{:ok, matcher.execute(query, graph, opts)}
end
def execute(query, graph, opts) do
with {:ok, bgp} <- Builder.bgp(query, opts) do
execute(bgp, graph, opts)
end
end
@doc """
Execute the given `query` against the given `graph`.
As opposed to `execute/3` this function returns the results directly or fails
with an exception.
"""
def execute!(query, graph, opts \\ []) do
case execute(query, graph, opts) do
{:ok, results} -> results
{:error, error} -> raise error
end
end
@doc """
Returns a `Stream` for the execution of the given `query` against the given `graph`.
Just like on `execute/3` the `query` can be given directly as `RDF.Query.BGP` struct
created with one of the builder functions in this module or as basic graph pattern
expression accepted by `bgp/1`.
The stream of solutions for variable bindings will be returned in a `:ok` tuple.
In case of an error a `:error` tuple is returned.
## Example
Let's assume we have an `example_graph` with these triples:
```turtle
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix ex: <http://example.com/> .
ex:Outlaw
foaf:name "Johnny Lee Outlaw" ;
foaf:mbox <mailto:jlow@example.com> .
ex:Goodguy
foaf:name "Peter Goodguy" ;
foaf:mbox <mailto:peter@example.org> ;
foaf:friend ex:Outlaw .
```
iex> {:ok, stream} = {:_, FOAF.name, :name?} |> RDF.Query.stream(example_graph())
...> Enum.to_list(stream)
[%{name: ~L"Peter Goodguy"}, %{name: ~L"Johnny Lee Outlaw"}]
iex> {:ok, stream} = [
...> {:_, FOAF.name, :name?},
...> {:_, FOAF.mbox, :mbox?},
...> ] |> RDF.Query.stream(example_graph())
...> Enum.take(stream, 1)
[
%{name: ~L"Peter Goodguy", mbox: ~I<mailto:peter@example.org>},
]
"""
def stream(query, graph, opts \\ [])
def stream(%BGP{} = query, %Graph{} = graph, opts) do
matcher = Keyword.get(opts, :matcher, @default_matcher)
{:ok, matcher.stream(query, graph, opts)}
end
def stream(query, graph, opts) do
with {:ok, bgp} <- Builder.bgp(query, opts) do
stream(bgp, graph, opts)
end
end
@doc """
Returns a `Stream` for the execution of the given `query` against the given `graph`.
As opposed to `stream/3` this function returns the stream directly or fails
with an exception.
"""
def stream!(query, graph, opts \\ []) do
case stream(query, graph, opts) do
{:ok, results} -> results
{:error, error} -> raise error
end
end
@doc """
Creates a `RDF.Query.BGP` struct.
A basic graph pattern consist of single or list of triple patterns.
A triple pattern is a tuple which consists of RDF terms or variables for
the subject, predicate and object of an RDF triple.
As RDF terms `RDF.IRI`s, `RDF.BlankNode`s, `RDF.Literal`s or all Elixir
values which can be coerced to any of those are allowed, i.e.
`RDF.Vocabulary.Namespace` atoms or Elixir values which can be coerced to RDF
literals with `RDF.Literal.coerce/1` (only on object position). On predicate
position the `:a` atom can be used for the `rdf:type` property.
Variables are written as atoms ending with a question mark. Blank nodes which
in a graph query patterns act like a variable which doesn't show up in the
results can be written as atoms starting with an underscore.
Here's a basic graph pattern example:
```elixir
[
{:s?, :a, EX.Foo},
{:s?, :a, EX.Bar},
{:s?, RDFS.label, "foo"},
{:s?, :p?, :o?}
]
```
Multiple triple patterns sharing the same subject and/or predicate can be grouped:
- Multiple objects to the same subject-predicate pair can be written by just
writing them one by one on the same triple pattern.
- Multiple predicate-objects pair on the same subject can be written by
grouping them with square brackets.
With these, the previous example can be shortened to:
```elixir
{
:s?,
[:a, EX.Foo, EX.Bar],
[RDFS.label, "foo"],
[:p?, :o?]
}
```
"""
defdelegate bgp(query), to: Builder, as: :bgp!
@doc """
Creates a `RDF.Query.BGP` struct for a path through a graph.
The elements of the path can consist of the same RDF terms and variable
expressions allowed in `bgp/1` expressions.
## Example
The `RDF.Query.BGP` struct build with this:
RDF.Query.path [EX.S, EX.p, RDFS.label, :name?]
is the same as the one build by this `bgp/1` call:
RDF.Query.bgp [
{EX.S, EX.p, :_o},
{:_o, RDFS.label, :name?},
]
"""
defdelegate path(query, opts \\ []), to: Builder, as: :path!
end