Можно ли получить положение элемента в RDF Collection в SPARQL?

Предположим, что у меня есть следующее объявление черепахи:

@prefix : <http://example.org#> .

:ls :list (:a :b :c)

Есть ли способ получить позиции элементов в коллекции?

Например, с этим запросом:

PREFIX :     <http://example.org#>
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 

SELECT ?elem WHERE {
 ?x :list ?ls .
 ?ls rdf:rest*/rdf:first ?elem .
}

Я получил:

--------
| elem |
========
| :a   |
| :b   |
| :c   |
--------

Но я хотел бы получить запрос:

--------------
| elem | pos |
==============
| :a   |  0  |
| :b   |  1  |
| :c   |  2  |
--------------

Является ли это возможным?

3 ответа

Решение

Чистое решение SPARQL 1.1

Я расширил данные, чтобы сделать проблему немного сложнее. Давайте добавим в список дублированный элемент, например, дополнительный :a в конце:

@prefix : <http://example.org#> .

:ls :list (:a :b :c :a) .

Затем мы можем использовать такой запрос, чтобы извлечь каждый узел списка (и его элемент) вместе с позицией узла в списке. Идея состоит в том, что мы можем сопоставить все отдельные узлы в списке с помощью шаблона [] :list/rdf:rest* ?node, Позиция каждого узла, тем не менее, представляет собой количество промежуточных узлов между заголовком списка и ?node, Мы можем сопоставить каждый из этих промежуточных узлов, разбив шаблон на

[] :list/rdf:rest* ?mid . ?mid rdf:rest* :node .

Тогда если мы сгруппировать по ?nodeколичество различных ?mid привязки это позиция ?node в списке. Таким образом, мы можем использовать следующий запрос (который также захватывает элемент (rdf:first) связаны с каждым узлом), чтобы получить позиции элементов в списке:

prefix : <https://stackru.com/q/17523804/1281433/>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

select ?element (count(?mid)-1 as ?position) where { 
  [] :list/rdf:rest* ?mid . ?mid rdf:rest* ?node .
  ?node rdf:first ?element .
}
group by ?node ?element
----------------------
| element | position |
======================
| :a      | 0        |
| :b      | 1        |
| :c      | 2        |
| :a      | 3        |
----------------------

Это работает, потому что структура списка RDF является связанным списком, подобным этому (где ?head это начало списка (объект :list), и является еще одним обязательным ?mid из-за картины [] :list/rdf:rest* ?mid):

графическое представление списка RDF

Сравнение с расширениями Jena ARQ

Автор вопроса также опубликовал ответ, который использует расширения Jena ARQ для работы со списками RDF. Решение, опубликованное в этом ответе:

PREFIX :     <http://example.org#>
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 
PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>

SELECT ?elem ?pos WHERE {
 ?x :list ?ls .
 ?ls list:index (?pos ?elem).
}

Этот ответ зависит от использования Jena ARQ и включения расширений, но он более лаконичен и прозрачен. Что не очевидно, так это то, имеет ли кто-то явно предпочтительную производительность. Как оказалось, для небольших списков разница не особенно значительна, но для больших списков расширения ARQ имеют гораздо лучшую производительность. Время выполнения для чистого запроса SPARQL быстро становится чрезмерно длинным, в то время как в версии, использующей расширения ARQ, различий почти нет.

-------------------------------------------
| num elements | pure SPARQL | list:index |
===========================================
|      50      |    1.1s     |    0.8s    |
|     100      |    1.5s     |    0.8s    |
|     150      |    2.5s     |    0.8s    |
|     200      |    4.8s     |    0.8s    |
|     250      |    9.7s     |    0.8s    |
-------------------------------------------

Эти конкретные значения, очевидно, будут отличаться в зависимости от вашей настройки, но общая тенденция должна быть заметна в любом месте. Поскольку в будущем все может измениться, вот конкретная версия ARQ, которую я использую:

$ arq --version
Jena:       VERSION: 2.10.0
Jena:       BUILD_DATE: 2013-02-20T12:04:26+0000
ARQ:        VERSION: 2.10.0
ARQ:        BUILD_DATE: 2013-02-20T12:04:26+0000

Таким образом, если бы я знал, что мне нужно обрабатывать списки нетривиальных размеров и что у меня был доступен ARQ, я бы использовал расширение.

Я нашел способ сделать это, используя библиотеку функций свойств в ARQ. Как говорит Стив Харрис, это нестандартно.

PREFIX :     <http://example.org#>
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 
PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>

SELECT ?elem ?pos WHERE {
 ?x :list ?ls .
 ?ls list:index (?pos ?elem).
}

TL;DR - короткий ответ "нет" с "но", длинный ответ "да" с "если".

Короткий ответ

Не выходя за пределы стандарта, если ваши списки не имеют ограниченной длины, вы можете сделать что-то грязное, например:

{ ?x :list (:a) BIND(1 AS ?length) }
UNION
{ ?x :list ([], :a) BIND(2 AS ?length) }
UNION
{ ?x :list ([], [], :a) BIND(3 AS ?length) }
...

и т.п.

Некоторые механизмы запросов RDF имеют нестандартные функции, которые будут работать со списками RDF, но вам придется ознакомиться с документацией для вашей системы.

Длинный ответ

Это признак того, что списки RDF имеют ужасную структуру и определение. Каким-то образом мы получили два разных способа представления списков, с которыми ужасно работать!

Если вы управляете данными, используйте более разумное представление, например

<x> :member [
   rdf:value :a ;
   :ordinal 1 ;
], [
   rdf:value :b ;
   :ordinal 2 ;
], [
   rdf:value :c ;
   :ordinal 3 ;
]
...

тогда вы можете запросить с помощью:

{ <x> :member [ rdf:value :a ; :ordinal ?position ] }
Другие вопросы по тегам