可选信息
RDF 是半结构化的数据,因此 SPARQL 具有查询数据的能力,但在数据不存在时不会使查询失败。查询使用可选部分来扩展查询结果中的信息,但仍会返回非可选的信息。换句话说,即使可选部分的匹配失败,查询仍然会返回那些必要的信息。这使得查询能够在数据存在的情况下提供更多的上下文信息,而在数据缺失的情况下也不会导致查询失败。
可选项
该查询(q-opt1.rq)可获取一个人的姓名和年龄(如果该信息存在)。
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age }
}
数据 (vc-db-2.rdf)中的四个人中有两个人有年龄属性,因此有两个查询方案有年龄信息。 但是,由于年龄的三元组模式是可选的,因此对于没有年龄信息的人,也有一种模式解决方案。
------------------------
| name | age |
=======================
| "Becky Smith" | 23 |
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones" | |
-----------------------
Java 代码如下:
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
public class Test05 {
static final String inputFileName = "java01/src/main/java/come/jena/rdf/vc-db-2.rdf";
public static void main(String[] args) {
// Load RDF data into a model
Model model = ModelFactory.createDefaultModel();
model.read(inputFileName); // Load your RDF data file
String queryString = "PREFIX pr:<http://www.w3.org/2001/vcard-rdf/3.0#>\n" +
"PREFIX info: <http://somewhere/peopleInfo#>" +
"SELECT ?name ?age " +
"WHERE { ?person pr:FN ?name ." +
" OPTIONAL {?person info:age ?age .}}";
Query query = QueryFactory.create(queryString);
// Execute the query
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
ResultSet results = qexec.execSelect();
// Process the query results
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
System.out.print("name: " + soln.get("?name"));
String varAge = null;
if (soln.getLiteral("?age") != null) { //判断是否查询到了 age
varAge = soln.getLiteral("?age").getLexicalForm();
// 不执行这一步输出RDF中定义的数据类型:http://www.w3.org/2001/XMLSchema#integer
}
System.out.println(" age: " + varAge);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:
如果没有可选子句,就不会检索到任何年龄信息。如果包含了三元组模式但不是可选的,那么我们就会得到查询结果(q-opt2.rq):
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
?person info:age ?age .
}
只有两个解决方案:
-----------------------
| name | age |
=======================
| "Becky Smith" | 23 |
| "John Smith" | 25 |
-----------------------
Java 代码如下:
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
public class Test05 {
static final String inputFileName = "java01/src/main/java/come/jena/rdf/vc-db-2.rdf";
public static void main(String[] args) {
// Load RDF data into a model
Model model = ModelFactory.createDefaultModel();
model.read(inputFileName); // Load your RDF data file
// Define a SPARQL query
String queryString = "PREFIX pr:<http://www.w3.org/2001/vcard-rdf/3.0#>\n" +
"PREFIX info: <http://somewhere/peopleInfo#>" +
"SELECT ?name ?age " +
"WHERE { ?person pr:FN ?name ." +
" ?person info:age ?age .}";
Query query = QueryFactory.create(queryString);
// Execute the query
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
ResultSet results = qexec.execSelect();
// Process the query results
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
//System.out.println(soln);
//System.out.println( soln);
System.out.print("name: " + soln.get("?name"));
System.out.println(" age: " + soln.getLiteral("?age").getLexicalForm());
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:
因为 info:age 属性现在必须出现在解决方案中。
带有过滤器的可选项
“OPTIONAL” 是一个二元运算符,它组合了两个图模式。可选模式是任何分组模式,并且可以包含任何 SPARQL 模式类型。如果分组匹配成功,解决方案将被扩展,如果匹配失败,则保持原始解决方案(在 q-opt3.rq 查询中)。换句话说,可选操作允许在第一个模式成功匹配时扩展解决方案,如果不匹配则保持原样。这种机制使得在不中断查询的情况下,可以根据条件决定是否添加额外的信息。
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age . FILTER ( ?age > 24 ) }
}
因此,如果我们在可选部分过滤年龄大于24岁的情况,我们仍将获得4个解决方案(来自 vcard:FN 模式),但年龄只有在通过测试时才会获取。
-----------------------
| name | age |
=======================
| "Becky Smith" | |
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones" | |
-----------------------
由于 "Becky Smith "的年龄小于 24 岁,因此其结果不包括其年龄。
Java 代码:
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
public class Test05 {
static final String inputFileName = "java01/src/main/java/come/jena/rdf/vc-db-2.rdf";
public static void main(String[] args) {
// Load RDF data into a model
Model model = ModelFactory.createDefaultModel();
model.read(inputFileName); // Load your RDF data file
// Define a SPARQL query
String queryString = "PREFIX pr:<http://www.w3.org/2001/vcard-rdf/3.0#>\n" +
"PREFIX info: <http://somewhere/peopleInfo#>" +
"SELECT ?name ?age " +
"WHERE { ?person pr:FN ?name ." +
" OPTIONAL {?person info:age ?age ." +
"FILTER ( ?age > 24 )}}";
Query query = QueryFactory.create(queryString);
// Execute the query
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
ResultSet results = qexec.execSelect();
// Process the query results
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
System.out.print("name: " + soln.get("?name"));
String varAge = null;
if (soln.getLiteral("?age") != null) {
varAge = soln.getLiteral("?age").getLexicalForm();
}
System.out.println(" age: " + varAge);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:
如果将过滤条件从可选部分移出,那么它可以影响解决方案的数量,但可能需要使过滤条件更复杂,以允许不包含变量 age(q-opt4.rq)。
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age . }
FILTER ( !bound(?age) || ?age > 24 )
}
如果解决方案中有一个年龄变量,那么它必须大于 24 岁。它也可以不包含 age 变量。 现在有三个解决方案:
-----------------------
| name | age |
=======================
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones" | |
-----------------------
当对一个表达式进行求值时,如果其中存在未绑定的变量,而在预期的情况下应该是绑定的变量,这会引发求值异常,导致整个表达式失败。这句话的意思是,如果在表达式中使用了未绑定的变量,而实际上这个变量应该是已经绑定过的,就会发生求值异常,并导致整个表达式无法成功求值。
可选模式以及与查询顺序
需要注意的一点是,在两个或更多的可选模式中使用相同的变量(并且不仅仅在某些基本模式中使用)。在SPARQL查询中,如果多个可选模式使用相同的变量,可能会导致结果不如预期。这是因为在可选模式中,如果某个可选部分匹配失败,相关的变量可能会保持未绑定状态,从而影响后续模式的匹配。因此,使用相同变量的可选模式之间需要仔细考虑,以确保结果的准确性。
在下面的查询中,?x a foaf:Person.
代表定义变量 ?x
为 foaf:Person类型的结点。OPTIONAL { ?x foaf:name ?name }
意思是上述的 ?x
结点具有 name 这一属性,该属性用变量 ?name
表示。OPTIONAL { ?x vCard:FN ?name }
意思是上述的 ?x
结点具有 FN 这一属性,该属性用变量 ?name
表示。
如果一个节点拥有 name
和 FN
这两种属性,该两种属性的值一样,则查询结果是一个值;如果不一样,则返回第一个OPTIONAL查到 ?name
的值。如果一个节点只拥有 name
和 FN
这两种属性的一种,则只返回那一种的值。如果一个 foaf:Person类型的结点不拥有 name
和 FN
这两种属性,则查询结果为空。
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name
WHERE
{
?x a foaf:Person .
OPTIONAL { ?x foaf:name ?name }
OPTIONAL { ?x vCard:FN ?name }
}
如果第一个可选模式将 ?name 和 ?x 绑定到某些值,第二个可选模式是尝试匹配与已知值相关的三元组(?x 和 ?name 已有值)。如果第一个可选模式没有匹配成功,那么第二个可选模式会尝试将其三元组与两个变量进行匹配。这是在描述当使用多个可选模式时,查询引擎如何尝试在不同情况下匹配变量和三元组。
以一组数据为例,其中存在各种数值组合:
<rdf:RDF
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:vCard='http://www.w3.org/2001/vcard-rdf/3.0#'
xmlns:info='http://somewhere/peopleInfo#'
xmlns:foaf='http://xmlns.com/foaf/0.1/'
>
<!-- both vCard:FN and foaf:name have values, and the values are the same -->
<foaf:Person rdf:about="http://somewhere/JohnSmith">
<vCard:FN>John Smith</vCard:FN>
<foaf:name>John Smith</foaf:name>
</foaf:Person>
<!-- both vCard:FN and foaf:name have values, but the values are not the same -->
<foaf:Person rdf:about="http://somewhere/RebeccaSmith">
<vCard:FN>Becky Smith</vCard:FN>
<foaf:name>Rebecca Smith</foaf:name>
</foaf:Person>
<!-- only vCard:FN has values -->
<foaf:Person rdf:about="http://somewhere/SarahJones">
<vCard:FN>Sarah Jones</vCard:FN>
</foaf:Person>
<!-- only foaf:name has values -->
<foaf:Person rdf:about="http://somewhere/MattJones">
<foaf:name>Matthew Jones</foaf:name>
</foaf:Person>
<!-- neither vCard:FN nor foaf:name have values -->
<foaf:Person rdf:about="http://somewhere/AdamJones" />
</rdf:RDF>
执行上述查询将得到这些解决方案:
-------------------
| name |
===================
| "John Smith" |
| "Matthew Jones" |
| "Sarah Jones" |
| |
| "Rebecca Smith" |
-------------------
Java 代码如下:
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
public class Test06 {
static final String inputFileName = "java01/src/main/java/come/jena/rdf/test01.rdf";
public static void main(String[] args) {
// Load RDF data into a model
Model model = ModelFactory.createDefaultModel();
model.read(inputFileName); // Load your RDF data file
// Define a SPARQL query
String queryString = "PREFIX pr:<http://www.w3.org/2001/vcard-rdf/3.0#>\n" +
"PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"SELECT ?name " +
"WHERE { ?x a foaf:Person ." +
" OPTIONAL {?x foaf:name ?name } " +
"OPTIONAL {?x pr:FN ?name } }";
Query query = QueryFactory.create(queryString);
// Execute the query
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
ResultSet results = qexec.execSelect();
// Process the query results
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
//System.out.println(soln);
//System.out.println( soln);
System.out.println("name: " + soln.get("?name"));
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:
Jena 官方文档:https://jena.apache.org/tutorials/sparql_optionals.html