SPARQL 教程五

可选信息

  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 表示。

  如果一个节点拥有 nameFN 这两种属性,该两种属性的值一样,则查询结果是一个值;如果不一样,则返回第一个OPTIONAL查到 ?name 的值。如果一个节点只拥有 nameFN 这两种属性的一种,则只返回那一种的值。如果一个 foaf:Person类型的结点不拥有 nameFN 这两种属性,则查询结果为空。

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

下一篇:联合查询

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值