¿Es posible obtener la posición de un elemento en una Colección RDF en SPARQL?

Supongamos que tengo la siguiente statement de tortuga:

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

¿Hay alguna manera de obtener las posiciones de los elementos en la colección?

Por ejemplo, con esta consulta:

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

Yo obtengo:

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

Pero me gustaría obtener una consulta:

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

¿Es posible?

Una solución pura de SPARQL 1.1

He extendido los datos para hacer el problema un poco más difícil. Agreguemos un elemento duplicado a la lista, por ejemplo, un adicional :a a al final:

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

Entonces podemos usar una consulta como esta para extraer cada nodo de lista (y su elemento) junto con la posición del nodo en la lista. La idea es que podamos hacer coincidir todos los nodos individuales en la lista con un patrón como [] :list/rdf:rest* ?node . La posición de cada nodo, sin embargo, es la cantidad de nodos intermedios entre el encabezado de la lista y el ?node Podemos unir cada uno de esos nodos intermedios dividiendo el patrón en

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

Entonces, si agrupamos por ?node , la cantidad de enlaces ?mid Distintos ?mid es la posición de ?node en la lista. Por lo tanto, podemos usar la siguiente consulta (que también toma el elemento (el rdf:first ) asociado con cada nodo) para obtener las posiciones de los elementos en la lista:

 prefix :  prefix rdf:  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 | ---------------------- 

Esto funciona porque la estructura de una lista RDF es una lista vinculada como esta (donde ?head es el comienzo de la lista (el objeto de :list ), y es otra vinculación de ?mid debido al patrón [] :list/rdf:rest* ?mid ):

representación gráfica de la lista RDF

Comparación con las extensiones JEN ARQ

El autor de la pregunta también publicó una respuesta que usa las extensiones ARQ de Jena para trabajar con listas RDF. La solución publicada en esa respuesta es

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

Esta respuesta depende de usar el ARQ de Jena y habilitar las extensiones, pero es más conciso y transparente. Lo que no es obvio es si uno tiene un rendimiento obviamente preferible. Resulta que, para listas pequeñas, la diferencia no es particularmente significativa, pero para listas más grandes, las extensiones ARQ tienen un rendimiento mucho mejor. El tiempo de ejecución para la consulta SPARQL pura rápidamente se hace prohibitivamente largo, mientras que casi no hay diferencia en la versión que usa las extensiones 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 | ------------------------------------------- 

Estos valores específicos obviamente diferirán dependiendo de su configuración, pero la tendencia general debería ser observable en cualquier lugar. Como las cosas podrían cambiar en el futuro, esta es la versión particular de ARQ que estoy usando:

 $ 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 

Como tal, si supiera que tengo que procesar listas de tamaños no triviales y que tengo ARQ disponible, usaré la extensión.

He encontrado una manera de hacerlo usando la biblioteca de funciones de propiedad en ARQ. Como dice Steve Harris, esto no es estándar.

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

TL; DR – respuesta corta no con una pero, respuesta larga sí con un if.

Respuesta corta

No sin salir del estándar, a menos que tus listas tengan una duración limitada, entonces puedes hacer algo sucio como:

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

etc.

Algunos motores de consulta RDF tienen funciones no estándar que funcionarán en listas RDF, pero tendría que consultar la documentación de su sistema.

Respuesta larga

Este es un síntoma de que las Listas de RDF tienen una estructura y definición terribles. De alguna manera, terminamos con dos formas diferentes de representar listas, ¡ambas son horribles de trabajar!

Si controla los datos, use alguna representación más sensata, por ej.

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

entonces puedes consultar con:

 {  :member [ rdf:value :a ; :ordinal ?position ] }