Как получить базовые ассоциации терминов из списка терминов в SPARQL

Одному из моих коллег нужно получить два набора троек RDF, включающих ассоциации терминов. Термины берутся из списка, а ассоциации берутся из набора троек, использующих эти термины.

  • Первый набор состоит из всех троек, причем любой элемент в списке терминов является либо субъектом, либо объектом тройки.

  • Второй набор состоит из троек, в которых любые два термина являются либо одним, либо двумя предикатами, удаленными друг от друга, где предикаты не обязательно являются двунаправленными. Итак, для s1 и s2 в списке терминов верны две тройки s1 s3 и s2 s3.

Думаю, у меня уже есть ответы, но я хотел попросить внести свой вклад в базу SPARQL, а также проверить себя.


person Bondolin    schedule 26.07.2013    source источник
comment
Если у вас уже есть работающее решение, этот вопрос может быть более актуальным на codereview.stackexchange.com. Если что-то в одном из этих решений не работает, было бы полезно показать это. Как бы то ни было, этот вопрос довольно открытый и может относиться к слишком широкой категории: либо слишком много возможных ответов, либо хорошие ответы будут слишком длинными для этого формата. Пожалуйста, добавьте детали, чтобы сузить набор ответов или выделить проблему, на которую можно ответить в нескольких абзацах.   -  person Joshua Taylor    schedule 26.07.2013


Ответы (2)


Учитывая такие данные:

@prefix : <urn:ex:> .

:a :p :b .
:a :p :e .

:b :p :c .
:b :p :d .

:c :p :a .
:c :p :f .

:d :p :a .
:d :p :d .

если мы возьмем (:b :c) в качестве набора интересных терминов, следующий запрос найдет все интересующие вас тройки. Обратите внимание, что условие из первого набора, т.е. что из ?s ?p ?o либо ?s, либо ?o находится в списке терминов, получает часть второго набора, а именно часть, где два термина связаны, т. е. где оба ?s и ?o находятся в списке терминов .

prefix : <urn:ex:>

select distinct ?s ?p ?between ?q ?o where { 
  # term list appearing twice in order to 
  # get all pairs of items
  values ?x { :b :c }
  values ?y { :b :c }

  # This handles the first set (where either the subject or
  # object is from the term list).  Note that the first set
  # includes part of the second set;  when two terms from 
  # the list are separated by just one predicate, then it's
  # a case where either the subject or object are from the
  # term list (since *both* are).
  { ?s ?p ?x bind(?x as ?o)} UNION { ?x ?p ?o bind(?x as ?s)}

  UNION 

  # The rest of the second set is when two terms from the
  # list are connected by a path of length two.  This is 
  # a staightforward pattern to write.
  { ?x ?p ?between . ?between ?q ?y .
    bind(?x as ?s)
    bind(?y as ?o) }
}

В результатах одиночные тройки — это строки, в которых связаны только s, p и o. Они охватывают ваш первый сет, а также часть «расстояние = 1» вашего второго сета. Остальная часть второго набора также связывает between и q. С точки зрения примера в вашем вопросе, between равно s3.

$ arq --data data.n3 --query query.sparql
-------------------------------
| s  | p  | between | q  | o  |
===============================
| :a | :p |         |    | :b |
| :b | :p |         |    | :d |
| :b | :p |         |    | :c |
| :c | :p |         |    | :f |
| :c | :p |         |    | :a |
| :c | :p | :a      | :p | :b |
-------------------------------

Обновление на основе комментария

Учитывая пример в комментарии, я думаю, что этот запрос можно резко сократить до следующего:

prefix : <urn:ex:>

select distinct ?x ?p ?between ?q ?y where { 
  values ?x { :b :c }
  values ?y { :b :c }

  { ?x ?p ?between } UNION { ?between ?p ?x }
  { ?between ?q ?y } UNION { ?y ?q ?between }
}

Когда мы связываем ?x ?p ?between или ?between ?p ?x, мы просто говорим, что между ?x и ?between есть граница (в любом направлении). ?y и ?q расширяют этот путь, поэтому мы имеем:

?x --?p-- ?between --?q-- ?y

где фактические направления --?p-- и --?q-- могут быть влево или вправо. Это охватывает все случаи, которые нам нужны. Вероятно, нетрудно понять, почему пути длины два будут соответствовать этому шаблону, но случай троек, в которых только субъект или объект является специальным термином, заслуживает уточнения. Учитывая тройку

<term> <prop> <non-term>

мы можем получить путь

<term> --<prop>-- <non-term> --<prop>-- <term>

и это работает в случае, когда <term> является объектом, а <non-term> является субъектом. Он также охватывает случай, когда оба и субъект, и объект являются терминами. По приведенным выше данным результаты таковы:

$ arq --data data.n3 --query paths.sparql
-------------------------------
| x  | p  | between | q  | y  |
===============================
| :b | :p | :d      | :p | :b |   
| :b | :p | :c      | :p | :b |   
| :b | :p | :a      | :p | :b |
| :c | :p | :a      | :p | :b |
| :b | :p | :a      | :p | :c |
| :c | :p | :f      | :p | :c |
| :c | :p | :a      | :p | :c |
| :c | :p | :b      | :p | :c |
-------------------------------

Если мы добавим некоторую информацию о том, куда указывали ?p и ?q, мы сможем восстановить пути:

prefix : <urn:ex:>

select distinct ?x ?p ?pdir ?between ?q ?qdir ?y where { 
  values ?x { :b :c }
  values ?y { :b :c }

  { ?x ?p ?between bind("right" as ?pdir)} UNION { ?between ?p ?x bind("left" as ?pdir)}
  { ?between ?q ?y bind("right" as ?qdir)} UNION { ?y ?q ?between bind("left" as ?qdir)}
}

Это дает вывод:

$ arq --data data.n3 --query paths.sparql
---------------------------------------------------
| x  | p  | pdir    | between | q  | qdir    | y  |
===================================================
| :b | :p | "right" | :d      | :p | "left"  | :b |   # :b -> :d
| :b | :p | "right" | :c      | :p | "left"  | :b |   # :b -> :c 
| :b | :p | "left"  | :a      | :p | "right" | :b |   # :a -> :b 
| :c | :p | "right" | :a      | :p | "right" | :b |   # :c -> :a -> :b
| :b | :p | "left"  | :a      | :p | "left"  | :c |   # :c -> :a -> :b 
| :c | :p | "right" | :f      | :p | "left"  | :c |   # :c -> :f 
| :c | :p | "right" | :a      | :p | "left"  | :c |   # :c -> :a 
| :c | :p | "left"  | :b      | :p | "right" | :c |   # :b -> :c 
---------------------------------------------------

Есть повторение пути c -> a -> b, но его, вероятно, можно отфильтровать.

Если вы на самом деле ищете здесь набор троек, а не конкретные пути, вы можете использовать конструктивный запрос, который возвращает вам график (поскольку набор троек — это график):

prefix : <urn:ex:>

construct {
  ?s1 ?p ?o1 .
  ?s2 ?q ?o2 .
}
where { 
  values ?x { :b :c }
  values ?y { :b :c }

  { ?x ?p ?between .
    bind(?x as ?s1)
    bind(?between as ?o1) }
  UNION
  { ?between ?p ?x .
    bind(?between as ?s1)
    bind(?x as ?o1)}

  { ?between ?q ?y .
    bind(?between as ?s2)
    bind(?y as ?o2) }
  UNION 
  { ?y ?q ?between .
    bind(?y as ?s2)
    bind(?between as ?o2)}
}
$ arq --data data.n3 --query paths-construct.sparql
@prefix :        <urn:ex:> .

<urn:ex:b>
      <urn:ex:p>    <urn:ex:c> ;
      <urn:ex:p>    <urn:ex:d> .

<urn:ex:c>
      <urn:ex:p>    <urn:ex:f> ;
      <urn:ex:p>    <urn:ex:a> .

<urn:ex:a>
      <urn:ex:p>    <urn:ex:b> .
person Joshua Taylor    schedule 26.07.2013
comment
Спасибо, Джошуа. Единственная проблема заключается в том, что мне нужно, чтобы во второй набор были включены шаблоны формы a -> c, a -> b и c -> a, b -> a. Тем не менее, это простое дополнение к вашему ответу, и мой ответ аналогичен, поэтому цель 2 подтверждения также была достигнута. - person Bondolin; 26.07.2013
comment
@NargothBond Мне было не совсем понятно, что вы имели в виду, когда сказали, что предикаты не обязательно двунаправлены. Учитывая этот новый пример, имеете ли вы в виду, что два термина из списка соединены ненаправленным путем длины два (то есть путем длины два, когда каждая тройка рассматривается как ненаправленное ребро между подлежащим и предикатом? )? - person Joshua Taylor; 26.07.2013
comment
:P Извините, я, кажется, хорош в том, чтобы быть неясным. Я пытался сказать что-то вроде того, как будто они соединены двумя ненаправленными ребрами, хотя ребра RDF по определению являются направленными. - person Bondolin; 26.07.2013
comment
@NargothBond Хорошо, основываясь на этом разъяснении, я обновил ответ, добавив гораздо более короткое решение. - person Joshua Taylor; 26.07.2013
comment
Спасибо! Это лучше работает. - person Bondolin; 26.07.2013

Вы можете использовать UNION в своих запросах. В любом случае у вас есть набор шаблонов, которые вы ищете, и вы хотите собрать информацию из UNION этих шаблонов.

Для первого набора получение всех троек, содержащих элемент списка либо в теме, либо в объекте,

SELECT ?s ?p ?o # result triples
WHERE
{
  # get a term bound to ?term
  GRAPH <urn:termsList/>
  { ?term a <urn:types/word> } # or however the terms are stored

  # match ?term against the basic patterns
  GRAPH <urn:associations/>
  {
    {
      ?term ?p ?o . # basic pattern #1
      BIND(?term AS ?s) # so that ?term shows up in the results
    }
    UNION # take ?term as either subject or object
    { 
      ?s ?p ?term . # basic pattern #2
      BIND(?term AS ?o)
    } 
  }
}

Сначала получите привязку всех терминов (?term a…).
Затем сопоставьте его с основными шаблонами:

?term ?p ?o

и

?s ?p ?term.

После каждого совпадения с шаблоном используйте привязку, чтобы поместить ?term в правильное место среди результатов. Например, первый шаблон только что связал ?p и ?o, поэтому их соответствующие ?s должны быть связаны следующим, иначе он просто будет отображаться пустым.

Для второго набора сначала мы получаем два слова из списка. Нам нужно соответствие «многие ко многим»:

?term1 a … .
?term2 a … .

Основные узоры:

?term1 ?p1 ?term2

?term1 ?p1 ?term .
?term2 ?p2 ?term .

?term1 ?p1 ?term .
?term ?p2 ?term2 .

?term ?p1 ?term1 .
?term ?p2 ?term2 .

Добавьте фильтр к каждому из трех последних, чтобы убедиться, что ?term1 и ?term2 не совпадают:

FILTER(!SAMETERM(?term1, ?term2))

(Мы могли бы поместить эти фильтры вне всех объединений, но более эффективно фильтровать переменные локально перед их дальнейшим использованием.)

Наконец, ОБЪЕДИНИТЕ результаты вместе:

SELECT ?s1 ?p1 ?o1 ?s2 ?p2 ?o2
WHERE
{
  GRAPH <urn:termsList/>
  { 
    ?term1 a <urn:types/word> . # outer loop variable
    ?term2 a <urn:types/word> . # inner loop variable
  }
  GRAPH <urn:associations/>
  {
    {
      # Only need to check one direction; either end gets 
      # matched into ?term1 at some point
      ?term1 ?p1 ?term2 .
      BIND (?term1 AS ?s1) .
      BIND (?term2 AS ?o1) . # Note we leave ?s2, ?p2, ?o2 unbound here
    }
    UNION
    { 
      ?term1 ?p1 ?term .
      ?term2 ?p2 ?term .
      FILTER(!SAMETERM(?term1, ?term2))
      BIND(?term1 AS ?s1) .
      BIND(?term AS ?o1) .
      BIND(?term2 AS ?s2) .
      BIND(?term AS ?o2)
    }
    UNION
    { 
      ?term1 ?p1 ?term .
      ?term ?p2 ?term2 .
      FILTER(!SAMETERM(?term1, ?term2))
      BIND(?term1 AS ?s1) .
      BIND(?term AS ?o1) .
      BIND(?term AS ?s2) .
      BIND(?term2 AS ?o2)
    }
    UNION
    { 
      ?term ?p1 ?term1 .
      ?term ?p2 ?term2 .
      FILTER(!SAMETERM(?term1, ?term2))
      BIND(?term AS ?s1) .
      BIND(?term1 AS ?o1) .
      BIND(?term AS ?s2) .
      BIND(?term2 AS ?o2)
    }
  }
}

Мы протестируем запросы по следующим текстам: Для списка слов --

# For God so loved the world, that he gave his only begotten Son, that 
# whosoever believeth in him should not perish, but have everlasting life.
# John 3:16

@prefix : <urn:terms/> .
@prefix t: <urn:types/> .

:For a t:word .
:God a t:word .
:so a t:word .
:loved a t:word .
:the a t:word .
:world a t:word .
:that a t:word .
:he a t:word .
:gave a t:word .
:his a t:word .
:only a t:word .
:begotten a t:word .
:Son a t:word .
:that a t:word .
:whosoever a t:word .
:believeth a t:word .
:in a t:word .
:him a t:word .
:should a t:word .
:not a t:word .
:perish a t:word .
:but a t:word .
:have a t:word .
:everlasting a t:word .
:life a t:word .

И список ассоциаций:

# For the wages of sin is death; but the gift of God is eternal life through 
# Jesus Christ our Lord.
# Romans 6:23

@prefix : <urn:terms/> .
@prefix g: <urn:grammar/> .

:For g:clauseAt :wages ;
     g:nextClauseHeadAt :but .
:the g:describes :wages .
:wages g:predicate :is .
:of g:describes :wages ;
    g:nominative :sin .
:is g:object :death .

:but g:clauseAt :gift .
:the g:describes :gift .
:gift g:predicate :is .
:of g:describes :gift ;
    g:nominative :God .
:is g:object :life .
:eternal g:describes :life .
:through g:describes :is ;
         g:nominative :Jesus .
:Christ g:describes :Jesus .
:our g:describes :Lord .
:Lord g:describes :Jesus .

Запрос 1:

----------------------------------------------------------------------------
| s                   | p                              | o                 |
============================================================================
| <urn:terms/For>     | <urn:grammar/nextClauseHeadAt> | <urn:terms/but>   |
| <urn:terms/For>     | <urn:grammar/clauseAt>         | <urn:terms/wages> |
| <urn:terms/of>      | <urn:grammar/nominative>       | <urn:terms/God>   |
| <urn:terms/is>      | <urn:grammar/object>           | <urn:terms/life>  |
| <urn:terms/eternal> | <urn:grammar/describes>        | <urn:terms/life>  |
| <urn:terms/but>     | <urn:grammar/clauseAt>         | <urn:terms/gift>  |
| <urn:terms/For>     | <urn:grammar/nextClauseHeadAt> | <urn:terms/but>   |
| <urn:terms/the>     | <urn:grammar/describes>        | <urn:terms/gift>  |
| <urn:terms/the>     | <urn:grammar/describes>        | <urn:terms/wages> |
----------------------------------------------------------------------------

Запрос 2:

----------------------------------------------------------------------------------------------------------------------------------------
| s1              | p1                             | o1                | s2              | p2                      | o2                |
========================================================================================================================================
| <urn:terms/For> | <urn:grammar/nextClauseHeadAt> | <urn:terms/but>   |                 |                         |                   |
| <urn:terms/For> | <urn:grammar/clauseAt>         | <urn:terms/wages> | <urn:terms/the> | <urn:grammar/describes> | <urn:terms/wages> |
| <urn:terms/but> | <urn:grammar/clauseAt>         | <urn:terms/gift>  | <urn:terms/the> | <urn:grammar/describes> | <urn:terms/gift>  |
| <urn:terms/the> | <urn:grammar/describes>        | <urn:terms/wages> | <urn:terms/For> | <urn:grammar/clauseAt>  | <urn:terms/wages> |
| <urn:terms/the> | <urn:grammar/describes>        | <urn:terms/gift>  | <urn:terms/but> | <urn:grammar/clauseAt>  | <urn:terms/gift>  |
----------------------------------------------------------------------------------------------------------------------------------------

Обратите внимание, что здесь есть некоторая избыточность. Это связано с двойной петлей того, как мы привязываем значения к ?term1 и ?term2, так что ?term1 становится ?term2 и наоборот. Если это неприемлемо, вы можете просто изменить строку 1 только на

SELECT DISTINCT ?s1 ?p1 ?o1

Это, конечно, делает привязки для ?s2 и ?o2 ненужными, так как они связаны только для SELECT.

«Ибо если мы соединились с [Христом] в смерти, подобной его, то непременно соединимся с ним в воскресении, подобном его» (Римлянам 6:5).

person Bondolin    schedule 26.07.2013