在dotNETRDF中使用SPARQL列表-列表的交集

在dotNETRDF中使用SPARQL列表-列表的交集,rdf,sparql,dotnetrdf,Rdf,Sparql,Dotnetrdf,我正在使用dotNetRDF,很难理解如何使用提供的列表帮助程序 目前,我没有使用列表,只有一项如下所示: paramString.SetParameter("nickname", g.CreateLiteralNode(nicknameString)); paramString.CommandText = @"INSERT DATA { data:person_1 app:nickname @nicknam

我正在使用dotNetRDF,很难理解如何使用提供的列表帮助程序

目前,我没有使用列表,只有一项如下所示:

 paramString.SetParameter("nickname", g.CreateLiteralNode(nicknameString));
 paramString.CommandText =
            @"INSERT DATA 
            { 
                data:person_1 app:nickname @nickname.                   
            }";
但现在我需要考虑多个昵称:

 //doesn't work with array, and there's no "CreateListNode()" 
 //paramString.SetParameter("nicknames", g.CreateLiteralNode(nicknamesArray)); 
 paramString.CommandText =
            @"INSERT DATA 
            { 
                data:person_1 app:nicknames @nicknames.                   
            }";
稍后我需要查询以检查两个列表是否相交:

queryString.CommandText =
            @"SELECT ?personWithSameNickname WHERE { 

                data:person_1 app:nicknames ?nicknames.

                #here I need to get people that have at 
                #least one nickname in common with data:person_1, 
                #aka at least one intersection in their nickname lists
                ?personWithSameNickname app:nicknames ?nicknames. 
            }";         
我还需要按交叉点的数量排序的结果,以便最佳匹配位于顶部

我怎样才能做到以上几点? 我只找到了对列表的引用,但由于我使用SPARQL,所以我不能完全理解它

关于数据建模的注记 所以,首先,你确定当你谈论列表时,你一定想要RDF列表吗?这种区别很重要,因为它改变了数据的形状和完成事情的方式

RDF列表是将值连接在一起的空白节点的有序序列,例如

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

:root :values [ rdf:first "a" ;
                rdf:rest [ rdf:first "b" ;
                           rdf:rest [ rdf:first "c" ;
                                      rdf:reset rdf:nil ] ] ] .
这种方法的缺点是,由于RDF图是无序的三元组,因此不能保留值的顺序或重复值。如果您需要订单或副本,则需要使用RDF列表方法

我的回答的其余部分将展示如何使用这两种数据建模方法

插入值列表 如何做到这一点取决于您想要的是一个RDF列表还是一系列的值,您完全正确,dotNetRDF在处理构建参数化SPARQL时没有任何内置的支持来处理这些事情

如果您想要一个RDF列表,那么您需要编写模板,以便它可以容纳必要数量的项目,例如:

paramString.CommandText =
        @"INSERT DATA 
        { 
            data:person_1 app:nicknames [ rdf:first @nick1 ;
                                          rdf:rest [ rdf:first @nick2 ;
                                                      rdf:rest rdf:nil ] ] .                   
        }";
paramString.SetParameter("nick1", "Rob");
paramString.SetParameter("nick2", "Bob");
显然,您可以根据需要扩展此模式以处理较短/较长的列表。显然,这需要用户做大量的工作,因此如果这是您需要的,那么我们当然可以考虑在将来的版本中为用户添加一个功能

如果您只是插入几个值,您可以使用单个三重模板,只需依次插入每个参数并执行它,例如

paramString.CommandText =
        @"INSERT DATA 
        { 
            data:person_1 app:nicknames @nickname.                   
        }";
foreach (String nick : nicknames)
{
   paramString.SetParameter("nickname", nick);
   // Execute the update
}
或者您可以将模板更改为每个昵称有一个三元组,这里我再次使用
语法,以避免重复主语和谓语:

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

:root :value "a" , "b" , "c" .
paramString.CommandText =
        @"INSERT DATA 
        { 
            data:person_1 app:nicknames @nick1 , @nick2 .                   
        }";
paramString.SetParameter("nick1", "Rob");
paramString.SetParameter("nick2", "Bob");
与RDF列表方法一样,您可以根据需要将此模式扩展到更多/更少的列表项。同样,如果这是您希望dotNetRDF为您做的事情,我们可以考虑在将来的版本中添加它

检查两个列表是否相交 对于RDF列表方法:

queryString.CommandText =
        @"SELECT ?personWithSameNickname WHERE { 
            data:person_1 app:nicknames [ rdf:rest*/rdf:first ?nicknames ].
            ?personWithSameNickname app:nicknames [ rdf:rest*/rdf:first ?nicknames ] .
            FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
        }"; 
基本上,您只需为起始节点选择所有昵称,然后对所有人执行相同的操作,并依赖SPARQL连接语义来找到交集

注意使用
rdf:rest*/rdf:first
属性路径遍历到rdf列表的所有值节点,以提取实际昵称。此外,由于起始节点将与其自身相交,因此我们使用
!SAMETERM(数据:person_1,PersonWithAmenicKname)
过滤器中消除自身的匹配,但是如果您希望避免
过滤器,可以在代码中执行此操作

如果您只是使用多重三元组方法,则查询更简单:

queryString.CommandText =
        @"SELECT ?personWithSameNickname WHERE { 
            data:person_1 app:nicknames ?nicknames .
            ?personWithSameNickname app:nicknames ?nicknames .
            FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
        }"; 
同样,只需为起始节点选择所有昵称,然后对所有人员执行相同的操作,并依赖SPARQL连接语义来找到交集

现在,如果您想根据交叉口的数量对人们进行排名,那么我们可以使用
groupby
orderby
进行排序,这可以添加到查询的任一变体中。我将使用第二种变体,因为基本查询更简单:

queryString.CommandText =
        @"SELECT ?personWithSameNickname (COUNT(?nicknames) AS ?matches) WHERE { 
            data:person_1 app:nicknames ?nicknames .
            ?personWithSameNickname app:nicknames ?nicknames .
            FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
        }
        GROUP BY ?personWithSameNickname
        ORDER BY DESC(?matches)";

因此,首先,我们将一个聚合添加到
选择
,特别是我们要计算昵称的数量。然后,我们还需要在
?personWithSameNickname
变量上添加一个
groupby
,因为我们希望每个昵称相交的人都有一个组。这也意味着我们将为每个组计算聚合,这样我们就可以使用
orderby
按降序排列匹配项

罗布,谢谢你的回答。把一切都弄清楚。拥有像CreateListNode()这样的功能会很有用,但可能还有更重要的功能需要实现。我现在对迭代构建查询很在行。再次感谢
queryString.CommandText =
        @"SELECT ?personWithSameNickname WHERE { 
            data:person_1 app:nicknames ?nicknames .
            ?personWithSameNickname app:nicknames ?nicknames .
            FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
        }"; 
queryString.CommandText =
        @"SELECT ?personWithSameNickname (COUNT(?nicknames) AS ?matches) WHERE { 
            data:person_1 app:nicknames ?nicknames .
            ?personWithSameNickname app:nicknames ?nicknames .
            FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
        }
        GROUP BY ?personWithSameNickname
        ORDER BY DESC(?matches)";