SPARQL 多值属性 - 渲染结果

SPARQL Multi-Valued properties - Rendering Results

我是 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

不幸的是,由于行重复,我的基本呈现尝试无法产生预期的输出,实际上它正在为从查询返回的每一行呈现一个新的 "Author" 部分。

我想我想了解的是应该如何完成这种类型的渲染。

修正案 - 关于 GROUP_CONCAT

的更详细分析

在查看可供我使用的选项时,我遇到了 GROUP_CONCAT 但经过一段时间的尝试后,我决定它可能不是能够给我想要的东西的选项,也可能不是最好的路线。原因是;

数据大小

虽然我的数据集 运行 我在这个 post 中的例子很小,只涵盖 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"

如您所见,这产生了一个结果行但是您无法再弄清各种数据元素之间的关系。哪些零售商出售哪本书?

任何 help/guidance 将不胜感激。

当前解

根据下面推荐的解决方案,我使用键的概念将各种数据集放在一起,但是我稍微调整了一下,以便我使用每个概念(例如作者、书籍和零售商)的查询,然后使用按键将结果汇集到我的渲染器中。

作者结果

                  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)

如此有效,您可以从各种不同的结果集中拼接您想要的内容并进行展示。

目前这似乎工作正常。

我发现用代码中的 SPARQL 结果构建对象比尝试形成一个 returns 每个相关资源只有一行的查询更容易。

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

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

对于复杂的值,我在变量名中使用 __ 来表示应该从该值构造一个对象。例如,所有具有前缀为 ?book__ 的变量的值将被转换为一个对象,该对象的变量名称的其余部分作为对象属性的名称,每个对象由 ?book__id 标识。因此,具有 ?book__id?book__name 的值将导致作者的属性 book,例如 author.book = { id: '<book-uri>', name: 'book name'}(如果有多本书,则为此类对象的列表)。

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

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 .
    }
}

在应用程序代码中,我将构建如下所示的 Author 对象(JavaScript 表示法):

[{
    id: '<http://www.test.com/ontologies/Author/JohnGrisham>',
    firstName: 'John',
    lastName: 'Grisham',
    book: [
        {
            id: '<http://www.test.com/ontologies/Book/TheFirm>',
            name: 'The Firm',
            retailer: ['Amazon', 'Waterstones', 'Foyles']
        },
        {
            id: '<http://www.test.com/ontologies/Book/TheClient>',
            name: 'The Client',
            retailer: ['Amazon', 'Waterstones', 'Foyles']
        }
    ]
}]

我想这是一个可以影响任何关系数据库的常见问题。正如你所说 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