solis 0.125.0 → 0.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/solis/query.rb +58 -6
- data/lib/solis/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce5b23eb71b6b7189377d4981fa8e6dc076128d60aec207e58310fbd162b8a16
|
|
4
|
+
data.tar.gz: 57c514df94596d89d76ea012cc946b2b521869f585f1d495609f8e0ec5cbdf71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 912773a18a5b44da59e1bbb6b7e3472c5d8be9a8044df0b64acbb7da501a3156ead5615363bd23fbd595d42db1b862f3d776235758d92acf8d0942f42f2fde00
|
|
7
|
+
data.tar.gz: 90c434c26f367159078555cf9bfbc715d4179ad8f3ebffba12ba379000e7696bf25020cd9a033d90b1b1e95375574bd4600eb3492d67e703a07aaff901d6b251
|
data/lib/solis/query.rb
CHANGED
|
@@ -11,6 +11,15 @@ module Solis
|
|
|
11
11
|
include Enumerable
|
|
12
12
|
include Solis::QueryFilter
|
|
13
13
|
|
|
14
|
+
# XSD numeric datatypes must be ordered by value; wrapping them in STR() would sort
|
|
15
|
+
# them lexically ("100" < "9"). Every other datatype is STR()-wrapped in the outer
|
|
16
|
+
# ORDER BY (see #sort_key_expression) to dodge a Virtuoso collation bug.
|
|
17
|
+
XSD_NUMERIC_DATATYPES = %w[
|
|
18
|
+
integer decimal float double long int short byte
|
|
19
|
+
nonNegativeInteger nonPositiveInteger negativeInteger positiveInteger
|
|
20
|
+
unsignedLong unsignedInt unsignedShort unsignedByte
|
|
21
|
+
].map { |t| "http://www.w3.org/2001/XMLSchema##{t}" }.freeze
|
|
22
|
+
|
|
14
23
|
def self.run(entity, query, options = {})
|
|
15
24
|
Solis::Query::Runner.run(entity, query, options)
|
|
16
25
|
end
|
|
@@ -96,6 +105,13 @@ module Solis
|
|
|
96
105
|
@filter = {values: ["VALUES ?type {#{target_class}}"], concepts: ['?concept a ?type .'] }
|
|
97
106
|
@sort = 'ORDER BY ?concept'
|
|
98
107
|
@sort_select = ''
|
|
108
|
+
# @sort_project carries the sort key(s) out of the inner (paginated) subquery so the
|
|
109
|
+
# outer query can re-sort on them; @sort_outer is that outer ORDER BY. A subquery's
|
|
110
|
+
# ORDER BY only decides which rows survive LIMIT/OFFSET — it does NOT propagate
|
|
111
|
+
# through the enclosing join — so the outer query must sort too. See #sort for the
|
|
112
|
+
# datatype-aware STR() handling that the outer ORDER BY needs.
|
|
113
|
+
@sort_project = ''
|
|
114
|
+
@sort_outer = ''
|
|
99
115
|
@language = Graphiti.context[:object]&.language || Solis::Options.instance.get[:language] || 'en'
|
|
100
116
|
@query_cache = self.class.shared_query_cache
|
|
101
117
|
end
|
|
@@ -113,19 +129,31 @@ module Solis
|
|
|
113
129
|
def sort(params)
|
|
114
130
|
@sort = ''
|
|
115
131
|
@sort_select = ''
|
|
132
|
+
@sort_project = ''
|
|
133
|
+
@sort_outer = ''
|
|
116
134
|
if params.key?(:sort)
|
|
117
135
|
i = 0
|
|
136
|
+
outer = ''
|
|
118
137
|
params[:sort].each do |attribute, direction|
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
meta = @model.class.metadata[:attributes][attribute.to_s]
|
|
139
|
+
path = meta[:path]
|
|
140
|
+
@sort_select += "optional {\n" if meta[:mincount] == 0
|
|
121
141
|
@sort_select += "?concept <#{path}> ?__#{attribute} . "
|
|
122
|
-
@sort_select += "}\n" if
|
|
142
|
+
@sort_select += "}\n" if meta[:mincount] == 0
|
|
123
143
|
@sort += ',' if i.positive?
|
|
124
144
|
@sort += "#{direction.to_s.upcase}(?__#{attribute})"
|
|
145
|
+
# Carry the sort key out of the subquery so the outer query can re-sort on it.
|
|
146
|
+
@sort_project += " ?__#{attribute}"
|
|
147
|
+
outer += ',' if i.positive?
|
|
148
|
+
outer += "#{direction.to_s.upcase}(#{sort_key_expression(attribute, meta)})"
|
|
125
149
|
i += 1
|
|
126
150
|
end
|
|
127
151
|
|
|
128
|
-
|
|
152
|
+
if i.positive?
|
|
153
|
+
@sort = "ORDER BY #{@sort}"
|
|
154
|
+
# ?s tiebreaker keeps each subject's triples contiguous and ties deterministic.
|
|
155
|
+
@sort_outer = "ORDER BY #{outer} ?s"
|
|
156
|
+
end
|
|
129
157
|
end
|
|
130
158
|
|
|
131
159
|
self
|
|
@@ -219,6 +247,11 @@ module Solis
|
|
|
219
247
|
# object when the open `?s ?p ?o` pattern is combined with the disjunctive
|
|
220
248
|
# language_filter, returning the rdf:type object for every row. Aliasing through
|
|
221
249
|
# BIND blocks that optimizer rewrite and is a no-op on engines without the bug.
|
|
250
|
+
# Outer ordering: the inner subquery's ORDER BY only selects which rows survive
|
|
251
|
+
# LIMIT/OFFSET; it does NOT propagate through the enclosing join. The outer query
|
|
252
|
+
# therefore re-sorts on the same key(s) (@sort_outer, datatype-aware STR()). When
|
|
253
|
+
# no sort is requested, `order by ?s` gives a cheap deterministic default order.
|
|
254
|
+
outer_order = @sort_outer.empty? ? 'order by ?s' : @sort_outer
|
|
222
255
|
query = %(
|
|
223
256
|
#{prefixes}
|
|
224
257
|
SELECT ?s ?p ?o WHERE {
|
|
@@ -229,7 +262,7 @@ SELECT ?s ?p ?o WHERE {
|
|
|
229
262
|
}
|
|
230
263
|
#{language_filter}
|
|
231
264
|
}
|
|
232
|
-
|
|
265
|
+
#{outer_order}
|
|
233
266
|
)
|
|
234
267
|
|
|
235
268
|
Solis::LOGGER.info(query) if ConfigFile[:debug]
|
|
@@ -274,8 +307,12 @@ order by ?s
|
|
|
274
307
|
end
|
|
275
308
|
|
|
276
309
|
def core_query(relationship)
|
|
310
|
+
# @sort_project re-exposes the sort key(s) bound in @sort_select so the OUTER query
|
|
311
|
+
# can ORDER BY them. NB: for a multi-valued sort attribute this turns DISTINCT into
|
|
312
|
+
# one row per (concept, value) pair, which would skew LIMIT/OFFSET — sort attributes
|
|
313
|
+
# are expected to be single-valued.
|
|
277
314
|
core_query = %(
|
|
278
|
-
SELECT distinct (?concept AS ?s) WHERE {
|
|
315
|
+
SELECT distinct (?concept AS ?s)#{@sort_project} WHERE {
|
|
279
316
|
#{@filter[:values].join("\n")}
|
|
280
317
|
#{relationship}
|
|
281
318
|
#{@filter[:concepts].join("\n")}
|
|
@@ -286,6 +323,21 @@ order by ?s
|
|
|
286
323
|
)
|
|
287
324
|
end
|
|
288
325
|
|
|
326
|
+
# SPARQL expression to sort an attribute by in the OUTER query.
|
|
327
|
+
#
|
|
328
|
+
# The outer (post-join) ORDER BY on this Virtuoso build (08.03.3335) mis-collates
|
|
329
|
+
# language-tagged literals, so string-ish keys are wrapped in STR() — which fixes the
|
|
330
|
+
# ordering and is order-preserving for xsd:string, dates, gYear, booleans and EDTF.
|
|
331
|
+
# Numeric keys must NOT be wrapped: STR() would order them lexically ("100" < "9"),
|
|
332
|
+
# and numbers carry no language tag so they are unaffected by the collation bug.
|
|
333
|
+
def sort_key_expression(attribute, meta = @model.class.metadata[:attributes][attribute.to_s])
|
|
334
|
+
if XSD_NUMERIC_DATATYPES.include?(meta[:datatype_rdf].to_s)
|
|
335
|
+
"?__#{attribute}"
|
|
336
|
+
else
|
|
337
|
+
"STR(?__#{attribute})"
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
289
341
|
def prefixes
|
|
290
342
|
"
|
|
291
343
|
PREFIX sh: <http://www.w3.org/ns/shacl#>
|
data/lib/solis/version.rb
CHANGED