@@ -233,9 +233,20 @@ extension-element-prefixes="ixsl"
233233
234234 <!-- Turns the view's SELECT into a query for the predicates that link its results to the rest
235235 of the graph: ?predicate paired with ?inverse, which flags the incoming ones. The SELECT
236- becomes a subquery (in its own group - a SubSelect has to be the sole content of its
237- braces, and the WHERE has a second member), so discovery covers the whole result set
238- rather than the loaded page; the caller strips LIMIT/OFFSET/ORDER off it first.
236+ becomes a subquery, so discovery covers the whole result set rather than the loaded page;
237+ the caller strips LIMIT/OFFSET/ORDER off it first.
238+
239+ SELECT ?predicate ?inverse {
240+ { SELECT ?predicate (false AS ?inverse) { { <view> } <outgoing> } GROUP BY ?predicate }
241+ UNION
242+ { SELECT ?predicate (true AS ?inverse) { { <view> } <incoming> } GROUP BY ?predicate }
243+ } LIMIT 100
244+
245+ Each direction joins the view on its own and collapses to its predicates with GROUP BY before
246+ the UNION. Joining the view once with an outgoing/incoming UNION and deduplicating with a
247+ top-level DISTINCT returns the same rows, but materialises every joined triple first: on a
248+ view whose results have millions of incoming triples (Seimas members and their ~4.9M roll-call
249+ votes) that took 27.7 s, this takes 8.5 s.
239250
240251 Outgoing objects that are literals, or that are never subjects themselves, are filtered
241252 out: a pivot onto them would render an empty view. Incoming subjects need no such check -
@@ -249,111 +260,131 @@ extension-element-prefixes="ixsl"
249260 <xsl : template match =" /json:map" mode =" ldh:link-predicates" priority =" 1" >
250261 <xsl : param name =" suffix" select =" ldh:fresh-var-suffix(., ('predicate', 'inverse', 'object', 'subject'))" as =" xs:string" tunnel =" yes" />
251262 <xsl : param name =" limit" select =" 100" as =" xs:integer" tunnel =" yes" />
252- <xsl : variable name =" focus-var" select =" '?' || ldh:first-var-name(.)" as =" xs:string" />
253- <xsl : variable name =" predicate-var" select =" '?predicate' || $suffix" as =" xs:string" />
254- <xsl : variable name =" inverse-var" select =" '?inverse' || $suffix" as =" xs:string" />
255- <xsl : variable name =" object-var" select =" '?object' || $suffix" as =" xs:string" />
256- <xsl : variable name =" subject-var" select =" '?subject' || $suffix" as =" xs:string" />
263+ <xsl : variable name =" select" select =" ." as =" element(json:map)" />
257264
258265 <json : map >
259266 <json : string key =" queryType" >SELECT</json : string >
260- <json : boolean key =" distinct" >true</json : boolean >
261267 <json : array key =" variables" >
262- <json : string ><xsl : value-of select =" $ predicate-var " /></json : string >
263- <json : string ><xsl : value-of select =" $ inverse-var " /></json : string >
268+ <json : string ><xsl : value-of select =" '? predicate' || $suffix " /></json : string >
269+ <json : string ><xsl : value-of select =" '? inverse' || $suffix " /></json : string >
264270 </json : array >
265271 <!-- hoist the dataset clause, which is only legal on the outermost query (see ldh:wrap-describe) -->
266272 <xsl : copy-of select =" json:map[@key = 'from']" />
267273 <json : array key =" where" >
268274 <json : map >
269- <json : string key =" type" >group </json : string >
275+ <json : string key =" type" >union </json : string >
270276 <json : array key =" patterns" >
271- <xsl : apply-templates select =" ." mode =" ldh:strip-from" />
277+ <xsl : for-each select =" (false(), true())" >
278+ <json : map >
279+ <json : string key =" type" >group</json : string >
280+ <json : array key =" patterns" >
281+ <xsl : call-template name =" ldh:LinkPredicatesSubquery" >
282+ <xsl : with-param name =" select" select =" $select" />
283+ <xsl : with-param name =" suffix" select =" $suffix" />
284+ <xsl : with-param name =" inverse" select =" ." />
285+ </xsl : call-template >
286+ </json : array >
287+ </json : map >
288+ </xsl : for-each >
272289 </json : array >
273290 </json : map >
291+ </json : array >
292+ <json : number key =" limit" ><xsl : value-of select =" $limit" /></json : number >
293+ </json : map >
294+ </xsl : template >
295+
296+ <!-- one direction of the link-predicate discovery: the view's results joined with their outgoing (or incoming) triples, grouped by predicate -->
297+ <xsl : template name =" ldh:LinkPredicatesSubquery" >
298+ <xsl : param name =" select" as =" element(json:map)" />
299+ <xsl : param name =" suffix" as =" xs:string" />
300+ <xsl : param name =" inverse" as =" xs:boolean" />
301+ <xsl : variable name =" focus-var" select =" '?' || ldh:first-var-name($select)" as =" xs:string" />
302+ <xsl : variable name =" predicate-var" select =" '?predicate' || $suffix" as =" xs:string" />
303+ <xsl : variable name =" object-var" select =" '?object' || $suffix" as =" xs:string" />
274304
305+ <json : map >
306+ <json : string key =" type" >query</json : string >
307+ <json : string key =" queryType" >SELECT</json : string >
308+ <json : array key =" variables" >
309+ <json : string ><xsl : value-of select =" $predicate-var" /></json : string >
275310 <json : map >
276- <json : string key =" type" >union</json : string >
311+ <json : string key =" expression" >"<xsl : value-of select =" $inverse" />"^^&xsd; boolean</json : string >
312+ <json : string key =" variable" ><xsl : value-of select =" '?inverse' || $suffix" /></json : string >
313+ </json : map >
314+ </json : array >
315+ <json : array key =" where" >
316+ <!-- the view's SELECT in its own group: a SubSelect has to be the sole content of its braces -->
317+ <json : map >
318+ <json : string key =" type" >group</json : string >
277319 <json : array key =" patterns" >
278- <!-- outgoing: the result set's own properties -->
279- <json : map >
280- <json : string key =" type" >group</json : string >
281- <json : array key =" patterns" >
282- <xsl : call-template name =" ldh:GraphUnionPattern" >
283- <xsl : with-param name =" subject" select =" $focus-var" />
284- <xsl : with-param name =" predicate" select =" $predicate-var" />
285- <xsl : with-param name =" object" select =" $object-var" />
286- <xsl : with-param name =" graph-var" select =" '?graph' || $suffix" />
287- </xsl : call-template >
320+ <xsl : apply-templates select =" $select" mode =" ldh:strip-from" />
321+ </json : array >
322+ </json : map >
288323
289- <json : map >
290- <json : string key =" type" >filter</json : string >
291- <json : map key =" expression" >
292- <json : string key =" type" >operation</json : string >
293- <json : string key =" operator" >!</json : string >
294- <json : array key =" args" >
295- <json : map >
296- <json : string key =" type" >operation</json : string >
297- <json : string key =" operator" >isliteral</json : string >
298- <json : array key =" args" >
299- <json : string ><xsl : value-of select =" $object-var" /></json : string >
300- </json : array >
301- </json : map >
302- </json : array >
303- </json : map >
304- </json : map >
324+ <xsl : choose >
325+ <!-- incoming: what points at the result set -->
326+ <xsl : when test =" $inverse" >
327+ <xsl : call-template name =" ldh:GraphUnionPattern" >
328+ <xsl : with-param name =" subject" select =" '?subject' || $suffix" />
329+ <xsl : with-param name =" predicate" select =" $predicate-var" />
330+ <xsl : with-param name =" object" select =" $focus-var" />
331+ <xsl : with-param name =" graph-var" select =" '?subjectGraph' || $suffix" />
332+ </xsl : call-template >
333+ </xsl : when >
334+ <!-- outgoing: the result set's own properties -->
335+ <xsl : otherwise >
336+ <xsl : call-template name =" ldh:GraphUnionPattern" >
337+ <xsl : with-param name =" subject" select =" $focus-var" />
338+ <xsl : with-param name =" predicate" select =" $predicate-var" />
339+ <xsl : with-param name =" object" select =" $object-var" />
340+ <xsl : with-param name =" graph-var" select =" '?graph' || $suffix" />
341+ </xsl : call-template >
305342
306- <json : map >
307- <json : string key =" type" >filter</json : string >
308- <json : map key =" expression" >
343+ <json : map >
344+ <json : string key =" type" >filter</json : string >
345+ <json : map key =" expression" >
346+ <json : string key =" type" >operation</json : string >
347+ <json : string key =" operator" >!</json : string >
348+ <json : array key =" args" >
349+ <json : map >
309350 <json : string key =" type" >operation</json : string >
310- <json : string key =" operator" >exists </json : string >
351+ <json : string key =" operator" >isliteral </json : string >
311352 <json : array key =" args" >
312- <json : map >
313- <json : string key =" type" >group</json : string >
314- <json : array key =" patterns" >
315- <xsl : call-template name =" ldh:GraphUnionPattern" >
316- <xsl : with-param name =" subject" select =" $object-var" />
317- <xsl : with-param name =" predicate" select =" '?objectPredicate' || $suffix" />
318- <xsl : with-param name =" object" select =" '?objectObject' || $suffix" />
319- <xsl : with-param name =" graph-var" select =" '?objectGraph' || $suffix" />
320- </xsl : call-template >
321- </json : array >
322- </json : map >
353+ <json : string ><xsl : value-of select =" $object-var" /></json : string >
323354 </json : array >
324355 </json : map >
325- </json : map >
326-
327- <json : map >
328- <json : string key =" type" >bind</json : string >
329- <json : string key =" variable" ><xsl : value-of select =" $inverse-var" /></json : string >
330- <json : string key =" expression" >"false"^^&xsd; boolean</json : string >
331- </json : map >
332- </json : array >
356+ </json : array >
357+ </json : map >
333358 </json : map >
334359
335- <!-- incoming: what points at the result set -->
336360 <json : map >
337- <json : string key =" type" >group</json : string >
338- <json : array key =" patterns" >
339- <xsl : call-template name =" ldh:GraphUnionPattern" >
340- <xsl : with-param name =" subject" select =" $subject-var" />
341- <xsl : with-param name =" predicate" select =" $predicate-var" />
342- <xsl : with-param name =" object" select =" $focus-var" />
343- <xsl : with-param name =" graph-var" select =" '?subjectGraph' || $suffix" />
344- </xsl : call-template >
345-
346- <json : map >
347- <json : string key =" type" >bind</json : string >
348- <json : string key =" variable" ><xsl : value-of select =" $inverse-var" /></json : string >
349- <json : string key =" expression" >"true"^^&xsd; boolean</json : string >
350- </json : map >
351- </json : array >
361+ <json : string key =" type" >filter</json : string >
362+ <json : map key =" expression" >
363+ <json : string key =" type" >operation</json : string >
364+ <json : string key =" operator" >exists</json : string >
365+ <json : array key =" args" >
366+ <json : map >
367+ <json : string key =" type" >group</json : string >
368+ <json : array key =" patterns" >
369+ <xsl : call-template name =" ldh:GraphUnionPattern" >
370+ <xsl : with-param name =" subject" select =" $object-var" />
371+ <xsl : with-param name =" predicate" select =" '?objectPredicate' || $suffix" />
372+ <xsl : with-param name =" object" select =" '?objectObject' || $suffix" />
373+ <xsl : with-param name =" graph-var" select =" '?objectGraph' || $suffix" />
374+ </xsl : call-template >
375+ </json : array >
376+ </json : map >
377+ </json : array >
378+ </json : map >
352379 </json : map >
353- </json : array >
380+ </xsl : otherwise >
381+ </xsl : choose >
382+ </json : array >
383+ <json : array key =" group" >
384+ <json : map >
385+ <json : string key =" expression" ><xsl : value-of select =" $predicate-var" /></json : string >
354386 </json : map >
355387 </json : array >
356- <json : number key =" limit" ><xsl : value-of select =" $limit" /></json : number >
357388 </json : map >
358389 </xsl : template >
359390
0 commit comments