我正在使用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.
}";
稍后我需要查询以检查2个列表是否相交:
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.
}";
我还需要按交叉点数排序的结果,因此最佳匹配位于顶部。
我怎样才能完成上述目标? 我只找到this对列表的引用,但由于我使用的是SPARQL,因此我无法理解它。
答案 0 :(得分:2)
首先,您是否确定当您谈论列表时,您一定打算使用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 ] ] ] .
正如你所看到的,它在三元组方面有很多开销,我们当然可以简化Turtle中的语法:
@prefix : <http://example.org/> .
:root :value ( "a" "b" "c" ) .
这相当于第一个例子,它隐藏了Turtle解析器将创建的显式三元组将遇到此语法。 dotNetRDF中包含的列表扩展专门用于处理RDF列表。
尽管列表中的含义可能只是与属性关联的一组值,例如
@prefix : <http://example.org/> .
:root :value "a" ;
:value "b" ;
:value "c" .
正如你所看到的,这实际上只是陈述了几个三元组,每个三元组都说明了属性的值。在Turtle中,我们可以使用,
语法进一步简化此操作,以避免重复谓词:
@prefix : <http://example.org/> .
:root :value "a" , "b" , "c" .
这种方法的缺点是,由于RDF图是无序的三元组,因此不能保留值的顺序或重复值。如果您需要订购或重复,则需要使用RDF列表方法。
我的其余部分将展示如何使用数据建模方法来做事。
如何执行此操作取决于您是想要RDF列表还是只需要一些值,在处理构建参数化SPARQL时,dotNetRDF没有任何内置支持来处理此类事情。
如果你想要一个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
}
或者您可以更改模板以使每个昵称具有三倍,此处我再次使用,
语法以避免重复主题和谓词:
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(data:person_1, ?personWithSameNickname)
中使用FILTER
来消除匹配,但如果您希望避免使用FILTER
,则可以在代码中执行此操作p>
如果您只是使用多重三重方法,则查询更简单:
queryString.CommandText =
@"SELECT ?personWithSameNickname WHERE {
data:person_1 app:nicknames ?nicknames .
?personWithSameNickname app:nicknames ?nicknames .
FILTER(!SAMETERM(data:person_1, ?personWithSameNickname))
}";
再次简单地选择起始节点的所有昵称,然后对所有人执行相同的操作,并依靠SPARQL连接语义来找到交集。
现在,如果您想根据交叉点的数量对人员进行排名,那么我们可以使用GROUP BY
和ORDER BY
执行此操作,这可以添加到查询的任一变体中。我将使用第二个变体,因为基本查询更简单:
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)";
首先我们向SELECT
添加一个聚合,特别是我们要计算昵称的数量。然后,我们还需要在GROUP BY
变量上添加?personWithSameNickname
,因为我们需要为每个具有相交昵称的人创建一个组。这也意味着我们将为每个组计算聚合,以便我们可以使用ORDER BY
按降序对匹配进行排名。