DBpedia端点上的多个查询,用于使用Apache Jena检索电影信息

时间:2014-06-01 18:39:51

标签: java jena semantic-web dbpedia

我试图下载一些电影'使用Apache Jena并查询DBpedia公共端点的信息(生产年份和标题)。 我已经知道公共端点有一些安全限制,因此它不会授予使用在结果集中返回超过2000行的查询。 出于这个原因,我尝试使用LIMIT和OFFSET选项在多个查询中细分我的查询并使用Java程序(http://ideone.com/xF0GCE)我将它们保存在格式化的特定文件中方式:

public void movieQuery(String dbpediaFilms) throws IOException {
     String includeNamespaces = "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" +
      "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
      "PREFIX dcterms: <http://purl.org/dc/terms/>\n" +
      "PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>\n";

       String currQuery = includeNamespaces + "SELECT DISTINCT ?movie (str(?movie_title) as ?title) (str(?movie_year) as ?year) WHERE {\n" +
       " ?movie rdf:type dbpedia-owl:Film.\n" +
       " ?movie rdfs:label ?movie_title.\n" +
" ?movie dcterms:subject ?cat .\n" +
" ?cat rdfs:label ?movie_year .\n" +
" FILTER langMatches(lang(?movie_title), \"EN\") .\n" +
" FILTER regex(?movie_year, \"^[0-9]{4} \", \"i\")\n" +
" } limit 2000 offset ";
      int totalNumberOfFilms = 77794;
      int totNumQuery = 39;
      int offset = 0;
      int currNum = 0;

      for(int i = 1; i <= totNumQuery; i++) {
      try {

         Query query = QueryFactory.create(currQuery + offset);
         currNum += Utils.serializeMappingList(getMovieMappingList(query), dbpediaFilms);

       } catch (Exception ex) {
             ex.printStackTrace();
             throw ex;
        }

      offset += 2000;

      myWait(30);

     }

     System.out.println(currNum);


}

这是我用来检索我需要的信息的查询:

SELECT DISTINCT ?movie (str(?movie_title) as ?title) (str(?movie_year) as ?year) WHERE    { ?movie rdf:type dbpedia-owl:Film.
  ?movie rdfs:label ?movie_title.
  ?movie dcterms:subject ?cat .
  ?cat rdfs:label ?movie_year .
  FILTER langMatches(lang(?movie_title), "EN") .
  FILTER regex(?movie_year, "^[0-9]{4} ", "i")
  } limit 2000 offset $specific_offset

正如您在java代码中看到的,我将变量(offset)增加2000以获取正确的结果集分区。

运行初步查询,我发现使用此查询,DBpedia中不同影片的总数为77794:

select distinct count(?film) where {
?film rdf:type dbpedia-owl:Film.
}

问题在于,如果我计算获得的节点数量,则等于76000,所以我认为我使用这个程序错过了很多电影。 有人可以对我说,我怎样才能正确地得到整个结果集? 我是否被迫查询本地DBpedia转储以便正确获取结果?

提前多多谢谢你。

编辑: 我使用有用的建议@Joshua Taylor创建了一个新查询:

SELECT DISTINCT ?movie (str(?movie_year) as ?year) (str(?movie_title) as ?title)  WHERE {
?movie rdf:type dbpedia-owl:Film.
movie rdfs:label ?movie_title.
FILTER langMatches(lang(?movie_title), \"EN\") .
optional { ?movie dbpprop:released   ?rel_year }
optional{?movie dbpedia-owl:releaseDate ?owl_year}
 optional {?movie dcterms:subject ?sub.
 ?sub rdfs:label ?movie_year_sub
 filter regex(?movie_year_sub, ".*[0-9]{4}.*", "i") }
 BIND(COALESCE(?owl_year, ?rel_year, ?movie_year_sub) AS ?movie_year)
 } group by ?movie limit 2000 offset $specific_offset 

使用按规范分组,virtuoso端点让我得到没有重复行的正确结果集。 相反,当我尝试使用Apache Jena运行查询时,我无法执行它,因为我收到以下错误:

com.hp.hpl.jena.query.QueryParseException: Non-group key variable in SELECT: ?movie_year in expression str(?movie_year)

1 个答案:

答案 0 :(得分:1)

除了满足您原始查询的电影外,还有更多的电影,而您的查询并不一定只计算每部电影一次。 select distinct (count(?var) as ?nVar) …select (count(distinct ?var) as ?nVar) …之间存在很大差异。第一个只显示不同的计数,而第二个显示不同的绑定数。

每部电影可以获得多个结果行。在这部分查询中:

?movie rdf:type dbpedia-owl:Film.
?movie dcterms:subject ?cat .
?cat rdfs:label ?movie_year .
FILTER regex(?movie_year, "^[0-9]{4} ", "i")

您将获得影片所属每个类别的每个匹配标签的结果行。例如,如果某部电影属于 1984年的最差电影 2010电影重拍,则您将获得两个结果行。

还有一些合法的电影,你不会计算,因为有些电影可能没有英文电影片头或以一年开头的类别。

我不确定您是否能够获得完全满意的结果,因为看起来DBpedia似乎无法可靠地获得您想要的数据。也就是说,尝试这样的查询来开始。它将获得所有电影,并且(希望)提取足够的信息以在许多情况下获取日期。有些dbpprop:释放的值非常奇怪,但我不知道它们对你有多大用处。

select * where { 
  ?film a dbpedia-owl:Film 
  optional { ?film dbpprop:released        ?released    }
  optional { ?film dbpedia-owl:releaseDate ?releaseDate }
  optional { ?film dcterms:subject [ rdfs:label ?catLabel ]
             filter( regex( ?catLabel, "^[0-9]{4}.*films", "i" ) )
           }
}
order by ?film 
limit 100

SPARQL results

新查询后更新

您发布的与Jena无关的查询(因为它不合法的SPARQL,即使Virtuoso接受它)可以通过几种不同的方式修复,具体取决于具体内容你想要的确切。最简单,最直接的方式就是不分组。

SELECT DISTINCT ?movie (str(?movie_year) as ?year) (str(?movie_title) as ?title)
WHERE {
  ?movie rdf:type dbpedia-owl:Film.
  ?movie rdfs:label ?movie_title.
  FILTER langMatches(lang(?movie_title), 'en')
  optional { ?movie dbpprop:released   ?rel_year }
  optional { ?movie dbpedia-owl:releaseDate ?owl_year}
  optional { ?movie dcterms:subject ?sub.
             ?sub rdfs:label ?movie_year_sub
             filter regex(?movie_year_sub, ".*[0-9]{4}.*", "i")
           }
  BIND(COALESCE(?owl_year, ?rel_year, ?movie_year_sub) AS ?movie_year)
}
limit 2000

如果你这样做,当你有多个英文电影片,发行年份等时,你会得到多个结果。如果你想避免这种情况,那么你想要group by ?movie。耶拿有权拒绝像

这样的事情
select ?movie (str(?movie_title) as ?title) where { 
  ?movie :hasTitle ?movie_title
}
group by ?movie

因为str(?movie_title)没有意义。对于每个?movie,您实际上是一组?movie_title。您需要从该集合中获得代表性的标题。现在,它实际上看起来并不像任何一部电影都有多个英文标题。您可以查看以下查询:

SELECT ?movie (count(?mTitle) as ?nTitles)
WHERE {
  ?movie a dbpedia-owl:Film ;
         rdfs:label ?mTitle .
  filter langMatches(lang(?mTitle),'en')
}
group by ?movie
having count(?mTitle) > 1 

鉴于此,这意味着您可以放心group by ?movie ?movie_title,这样您就可以在投影变量列表中使用?movie_title。但是如何处理发布日期呢?原则上,你最终可能会得到不止一个。事实上,正如您在此查询中看到的那样,数据确实为您提供了多个数据:

SELECT DISTINCT ?movie (group_concat(?movie_year;separator=';') as ?years)
WHERE {
  ?movie rdf:type dbpedia-owl:Film.
  ?movie rdfs:label ?movie_title.
  FILTER langMatches(lang(?movie_title), 'en')
  optional { ?movie dbpprop:released   ?rel_year }
  optional { ?movie dbpedia-owl:releaseDate ?owl_year}
  optional { ?movie dcterms:subject ?sub.
             ?sub rdfs:label ?movie_year_sub
             filter regex(?movie_year_sub, ".*[0-9]{4}.*", "i")
           }
  BIND(COALESCE(?owl_year, ?rel_year, ?movie_year_sub) AS ?movie_year)
}
group by ?movie ?movie_title 
having count(?movie_year) > 1
limit 2000

这意味着您需要根据该集合获取值。 SPARQL为您提供了一些功能(例如maxminsum)。在这种情况下,我不知道是否有一种简单的方法可以选择最好的&#34;代表,所以你可能只想从sample得到它,给你一个这样的查询:

SELECT DISTINCT ?movie (str(sample(?movie_year)) as ?year) ?movie_title
WHERE {
  ?movie rdf:type dbpedia-owl:Film.
  ?movie rdfs:label ?movie_title.
  FILTER langMatches(lang(?movie_title), 'en')
  optional { ?movie dbpprop:released   ?rel_year }
  optional { ?movie dbpedia-owl:releaseDate ?owl_year}
  optional { ?movie dcterms:subject ?sub.
             ?sub rdfs:label ?movie_year_sub
             filter regex(?movie_year_sub, ".*[0-9]{4}.*", "i")
           }
  BIND(COALESCE(?owl_year, ?rel_year, ?movie_year_sub) AS ?movie_year)
}
group by ?movie ?movie_title 
limit 2000

SPARQL results

这是合法的SPARQL,由sparql.org validator确认(一旦你提供了一些前缀定义),所以Jena应该没问题,而Virtuoso(在这种情况下,DBpedia端点)也接受它。 / p>