SPARQL多值属性 - 渲染结果

时间:2017-03-13 16:22:13

标签: sparql rdf jena semantic-web fuseki

我是SPARQL的新手,并且图形数据库作为一个整体查询所以请原谅任何无知,但我正在尝试使用存储在Fueski中的一些数据编写基本输出,并且我正在努力理解处理重复行的最佳实践至于各种概念之间存在的基数。

我将用一个简单的例子来表达我的观点。

数据集

这是我目前正在处理的数据类型和关系的代表性样本;

Data Set

基于这种结构,我产生了以下三元组(N-Triple格式);

<http://www.test.com/ontologies/Author/JohnGrisham>  <http://www.test.com/ontologies/property#firstName> "John" .
<http://www.test.com/ontologies/Author/JohnGrisham> <http://www.test.com/ontologies/property#lastName> "Grisham" .
<http://www.test.com/ontologies/Author/JohnGrisham> <http://www.test.com/ontologies/property#hasWritten> <http://www.test.com/ontologies/Book/TheClient> .
<http://www.test.com/ontologies/Author/JohnGrisham> <http://www.test.com/ontologies/property#hasWritten> <http://www.test.com/ontologies/Book/TheFirm> .

<http://www.test.com/ontologies/Book/TheFirm> <http://www.test.com/ontologies/property#name> "The Firm" .
<http://www.test.com/ontologies/Book/TheFirm> <http://www.test.com/ontologies/property#soldBy> <http://www.test.com/ontologies/Retailer/Foyles> .
<http://www.test.com/ontologies/Book/TheFirm> <http://www.test.com/ontologies/property#soldBy> <http://www.test.com/ontologies/Retailer/Waterstones> .

<http://www.test.com/ontologies/Book/TheClient> <http://www.test.com/ontologies/property#name> "The Client" .
<http://www.test.com/ontologies/Book/TheClient> <http://www.test.com/ontologies/property#soldBy> <http://www.test.com/ontologies/Retailer/Amazon> .
<http://www.test.com/ontologies/Book/TheClient> <http://www.test.com/ontologies/property#soldBy> <http://www.test.com/ontologies/Retailer/Waterstones> .


<http://www.test.com/ontologies/Retailer/Amazon> <http://www.test.com/ontologies/property#name> "Amazon" .
<http://www.test.com/ontologies/Retailer/Waterstones> <http://www.test.com/ontologies/property#name> "Waterstones" .
<http://www.test.com/ontologies/Retailer/Foyles> <http://www.test.com/ontologies/property#name> "Foyles" .

渲染输出格式

现在我要做的是渲染一个页面,其中显示所有作者,显示所有书籍和销售这些单独书籍的零售商的详细信息。所以像这样(suedo代码);

for-each:Author

   <h1>Author.firstName + Author.lastName</h1>

   for-each:Author.Book

     <h2>Book.Name</h2>

     Sold By:
     for-each:Book.Retailer

        <h2>Retailer.name</h2>

SPARQL

为了让渲染工作,我的想法是我需要作者的名字和姓氏,然后是他们拥有的所有书籍名称以及这些书籍销售的各种零售商名称,因此我提出了以下SPARQL;

PREFIX p: <http://www.test.com/ontologies/property#>

SELECT ?authorfirstname 
       ?authorlastname 
       ?bookname 
       ?retailername
WHERE {
    ?author p:firstName ?authorfirstname;
           p:lastName ?authorlastname;
           p:hasWritten ?book .
    OPTIONAL {
        ?book p:name ?bookname;
              p:soldBy ?retailer .
        ?retailer p:name ?retailername .
    }
}

这提供了以下结果;

Results Triple Table

不幸的是,由于行的重复,我的基本渲染尝试无法按预期生成输出,实际上它正在为查询返回的每一行渲染一个新的“作者”部分。

我想我想要了解的是应该如何进行这种渲染。

  • 渲染器是否应该将数据重新组合回它想要去的图表形式(我真的看不出这是怎么回事)

  • SPARQL是否无效 - 有没有办法在SPARQL语言中做我想做的事情?

  • 我刚做错了吗?

修订 - 对GROUP_CONCAT进行更详细的分析

在查看我可以使用的选项时,我遇到了GROUP_CONCAT,但经过一段时间的玩游戏后决定它可能不是给我想要的东西,可能不是最好的路线。原因是:

数据大小

虽然我在这篇文章中运行我的示例的数据集很小但只包含3个概念和一个非常有限的数据集我在现实世界中运行的实际概念和数据远远大于连接结果将产生的结果极长的分隔字符串,特别是对于描述等自由格式列。

失去上下文

在尝试group_concat时,我很快意识到我无法理解group_concat列中各种数据元素的相关内容。我可以通过上面的书籍示例来说明这一点。

SPARQL

PREFIX p: <http://www.test.com/ontologies/property#>

select ?authorfirstname 
        ?authorLastName 
        (group_concat(distinct ?bookname; separator = ";") as ?booknames)
        (group_concat(distinct ?retailername; separator = ";") as ?retailernames)
where {
  ?author p:firstName ?authorfirstname;
          p:lastName ?authorLastName;
          p:hasWritten ?book .
    OPTIONAL {
        ?book p:name ?bookname;
              p:soldBy ?retailer .
        ?retailer p:name ?retailername .
    }
}
group by ?authorfirstname ?authorLastName

这产生了以下输出;

firstname = "John"
lastname  = "Grisham"
booknames = "The Client;The Firm"
retailernames = "Amazon;Waterstones;Foyles"

正如您所看到的,这产生了一个结果行,您无法再了解各种数据元素的相关性。哪些零售商适合哪本书?

非常感谢任何帮助/指导。

当前解决方案

基于下面推荐的解决方案,我使用了密钥的概念来提供各种数据集,但是我稍微调整了一下,以便我使用每个概念的查询(例如作者,书籍和零售商),然后使用在我的渲染器中汇总结果的键。

作者结果

                  firstname  lastname   books
       --------------------------------------------------------------------------------
       1          John       Grisham  ontologies/Book/TheClient|ontologies/Book/TheFirm

预订结果

                  id                        name        retailers
       -------------------------------------------------------------------------------------------------------
       1          ontologies/Book/TheClient The Client   ontologies/Retailer/WaterStones|ontologies/Retailer/Amazon
       2          ontologies/Book/TheFirm   The Firm     ontologies/Retailer/WaterStones|ontologies/Retailer/Foyles

零售商结果

                  id                             name  
       -------------------------------------------------- 
       1          ontologies/Retailer/Amazon      Amazon
       2          ontologies/Retailer/Waterstones Waterstones
       3          ontologies/Retailer/Foyles      Foyles

我在渲染器中执行的操作是使用ID从各种结果集中提取结果...

 for-each author a : authors
    output(a.firstname)
    for-each book b : a.books.split("|")
    book = books.get(b) // get the result for book b (e.g. Id to Foreign    key)
      output(book.name)
      for-each retailer r : book.retailers.split("|")
        retailer = retailers.get(r)
        output(retailer.name)

如此有效地将各种不同的结果集拼接在一起并呈现它。

目前这似乎工作正常。

2 个答案:

答案 0 :(得分:1)

我发现从代码中的SPARQL结果中构造对象更容易,而不是尝试形成每个相关资源只返回一行的查询。

我会使用资源的URI来识别哪些行属于哪个资源(在本例中为author),然后根据所述URI合并结果行。

对于JS应用程序,我使用代码here从SPARQL结果中构造对象。

对于复数值,我在变量名中使用__来表示应该从值构造一个对象。例如,所有带有前缀为?book__的变量的值都将变为一个对象,其中变量的名称的其余部分作为对象属性的名称,每个对象由{{1}标识}。因此,拥有?book__id?book__id的值会导致作者的属性?book__name,例如book(如果有多本书,则为此类对象的列表)。

例如,在这种情况下,我将使用以下查询:

author.book = { id: '<book-uri>', name: 'book name'}

在应用程序代码中,我将构建看起来像这样的JavaScript对象(JavaScript表示法):

PREFIX p: <http://www.test.com/ontologies/property#>

SELECT ?id ?firstName ?lastName ?book__id ?book__name
       ?book__retailer
WHERE {
    ?id p:firstName ?firstName;
           p:lastName ?lastName;
           p:hasWritten ?book__id .
    OPTIONAL {
        ?book__id p:name ?book__name;
          p:soldBy/p:name ?book__retailer .
    }
}

答案 1 :(得分:0)

我认为,这是一个可以打击任何关系数据库的常见问题。正如你所说GROUP_CONCAT在许多情况下都很有用,但却失去了保真度。

我找到了一个你可能感兴趣的解决方案。假设你想构建一个通过作者循环的视图或结果树,然后为每个作者构建他们的书籍,然后为每个作者构建零售商。

SELECT DISTINCT ?authorname ?bookname ?retailername {
    ...
} ORDER BY ?authorname ?bookname ?retailername

这会给你这样的结果:

           author     book     retailer
           -----------------------------
1          author1    book1    retailer1
2          author1    book1    retailer2
3          author1    book2    retailer2
4          author2    book3    retailer2
5          author2    book3    retailer3
           ...

由于订购,可以逐步完成

get next result
currentauthor = author in result

print currentauthor

while author in next result = currentauthor:
    get next result
    currentbook = book in result
    print currentauthor
    while book in next result = currentbook:
        get next result
        print retailer in result