001/** 002 * Copyright (C) 2007 - 2016, Jens Lehmann 003 * 004 * This file is part of DL-Learner. 005 * 006 * DL-Learner is free software; you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation; either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * DL-Learner is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package org.dllearner.kb.sparql; 020 021import org.aksw.jena_sparql_api.cache.core.QueryExecutionFactoryCacheEx; 022import org.aksw.jena_sparql_api.cache.extra.CacheFrontend; 023import org.aksw.jena_sparql_api.core.FluentQueryExecutionFactory; 024import org.aksw.jena_sparql_api.core.QueryExecutionFactory; 025import org.aksw.jena_sparql_api.http.QueryExecutionHttpWrapper; 026import org.aksw.jena_sparql_api.model.QueryExecutionFactoryModel; 027import org.aksw.jena_sparql_api.pagination.core.QueryExecutionFactoryPaginated; 028import org.apache.jena.query.QueryExecution; 029import org.apache.jena.rdf.model.*; 030import org.apache.jena.riot.WebContent; 031import org.apache.jena.sparql.engine.http.QueryEngineHTTP; 032import org.apache.log4j.Level; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import java.util.Set; 037 038/** 039 * According to the definition at http://www.w3.org/Submission/CBD/ ... 040 * <p> 041 * An alternative form of description, which includes all statements expressed along both 042 * outbound and inbound arc paths, terminated in like fashion as a concise bounded description but extending from the 043 * starting node in both directions; thus enabling the requesting agent to potentially infer itself any implicit 044 * statements based on symmetric property pairs. We can call this derivative of a concise bounded description a 045 * <em>symmetric concise bounded description</em>. 046 * <p> 047 * Specifically, given a particular node (the starting node) in a particular RDF graph (the source graph), a subgraph of 048 * that particular graph, taken to comprise a symmetric concise bounded description of the resource denoted by the 049 * starting node, can be identified as follows: 050 * 051 * <ol> 052 * <li> 053 * Include in the subgraph all statements in the source graph where the object of the statement is the starting node; 054 * </li> 055 * <li> 056 * Recursively, for all statements identified in the subgraph thus far having a blank node subject not equal 057 * to the starting node, include in the subgraph all statements in the source graph where the object of the 058 * statement is the blank node in question and which are not already included in the subgraph. 059 * </li> 060 * <li> 061 * Recursively, for all statements included in the subgraph thus far, for all reifications of each statement 062 * in the source graph, include the symmetric concise bounded description beginning from the rdf:Statement 063 * node of each reification. 064 * </li> 065 * <li> 066 * Include in the subgraph the concise bounded description beginning from the starting node. 067 * </li> 068 * </ol> 069 * <p> 070 * 071 * @author Lorenz Buehmann 072 */ 073public class SymmetricConciseBoundedDescriptionGeneratorImpl implements ConciseBoundedDescriptionGenerator{ 074 075 private static final Logger logger = LoggerFactory.getLogger(SymmetricConciseBoundedDescriptionGeneratorImpl.class); 076 077 private QueryExecutionFactory qef; 078 079 private Set<String> namespaces; 080 081 public SymmetricConciseBoundedDescriptionGeneratorImpl(SparqlEndpoint endpoint, CacheFrontend cache) { 082 qef = FluentQueryExecutionFactory 083 .http(endpoint.getURL().toString(), endpoint.getDefaultGraphURIs()) 084 .config().withPostProcessor(qe -> ((QueryEngineHTTP) ((QueryExecutionHttpWrapper) qe).getDecoratee()) 085 .setModelContentType(WebContent.contentTypeRDFXML)) 086 .end() 087 .create(); 088 089 if(cache != null){ 090 qef = new QueryExecutionFactoryCacheEx(qef, cache); 091 } 092 qef = new QueryExecutionFactoryPaginated(qef, 10000); 093 } 094 095 public SymmetricConciseBoundedDescriptionGeneratorImpl(SparqlEndpoint endpoint) { 096 this(endpoint, null); 097 } 098 099 public SymmetricConciseBoundedDescriptionGeneratorImpl(QueryExecutionFactory qef) { 100 this.qef = qef; 101 } 102 103 public SymmetricConciseBoundedDescriptionGeneratorImpl(Model model) { 104 this(new QueryExecutionFactoryModel(model)); 105 } 106 107 /* (non-Javadoc) 108 * @see org.dllearner.kb.sparql.ConciseBoundedDescriptionGenerator#getConciseBoundedDescription(java.lang.String, int, boolean) 109 */ 110 @Override 111 public Model getConciseBoundedDescription(String resource, int depth, boolean withTypesForLeafs) { 112 logger.debug("computing CBD of depth {} for {} ...", resource, depth); 113 Model cbd = ModelFactory.createDefaultModel(); 114 cbd.add(getIncomingModel(resource, depth)); 115 cbd.add(getOutgoingModel(resource, depth)); 116 logger.debug("CBD size: {}", cbd.size()); 117 return cbd; 118 } 119 120 @Override 121 public void setAllowedPropertyNamespaces(Set<String> namespaces) { 122 this.namespaces = namespaces; 123 } 124 125 private Model getIncomingModel(String resource, int depth){ 126 String query = makeConstructQueryObject2(resource, depth); 127 logger.debug("computing incoming triples for {}\n{}", resource, query); 128 try(QueryExecution qe = qef.createQueryExecution(query)) { 129 return qe.execConstruct(); 130 } catch (Exception e) { 131 logger.error("Failed to retrieve incoming CBD for " + resource + ".\nQuery:\n" + query, e); 132 } 133 return null; 134 } 135 136 private Model getOutgoingModel(String resource, int depth){ 137 String query = makeConstructQuerySubject(resource, depth); 138 logger.debug("computing outgoing triples for {}\n{}", resource, query); 139 try(QueryExecution qe = qef.createQueryExecution(query)) { 140 return qe.execConstruct(); 141 } catch (Exception e) { 142 logger.error("Failed to retrieve outgoing CBD for " + resource + ".\nQuery:\n" + query, e); 143 } 144 return null; 145 } 146 147 /** 148 * A SPARQL CONSTRUCT query is created, to get a RDF graph for the given resource and recursion depth. 149 * @param resource The resource for which a CONSTRUCT query is created. 150 * @return The CONSTRUCT query 151 */ 152 private String makeConstructQuerySubject(String resource, int depth){ 153 StringBuilder sb = new StringBuilder(); 154 sb.append("CONSTRUCT {\n"); 155 sb.append("<").append(resource).append("> ").append("?p0 ").append("?o0").append(".\n"); 156 for(int i = 1; i < depth; i++){ 157 sb.append("?o").append(i-1).append(" ").append("?p").append(i).append(" ").append("?o").append(i).append(".\n"); 158 } 159 sb.append("}\n"); 160 sb.append("WHERE {\n"); 161 sb.append("<").append(resource).append("> ").append("?p0 ").append("?o0").append(".\n"); 162 for(int i = 1; i < depth; i++){ 163 sb.append("OPTIONAL{\n"); 164 sb.append("?o").append(i-1).append(" ").append("?p").append(i).append(" ").append("?o").append(i).append(".\n"); 165 } 166 for(int i = 1; i < depth; i++){ 167 sb.append("}"); 168 } 169 sb.append("}\n"); 170 171 return sb.toString(); 172 } 173 174 /** 175 * A SPARQL CONSTRUCT query is created, to get a RDF graph for the given resource and recursion depth. 176 * @param resource The resource for which a CONSTRUCT query is created. 177 * @return The CONSTRUCT query 178 */ 179 private String makeConstructQueryObject(String resource, int depth){ 180 StringBuilder sb = new StringBuilder(); 181 sb.append("CONSTRUCT {\n"); 182 sb.append("?s0 ").append("?p0 ").append("<").append(resource).append(">").append(".\n"); 183 for(int i = 1; i < depth; i++){ 184 sb.append("?o").append(i).append(" ").append("?p").append(i).append(" ").append("?s").append(i-1).append(".\n"); 185 } 186 sb.append("}\n"); 187 sb.append("WHERE {\n"); 188 sb.append("?s0 ").append("?p0 ").append("<").append(resource).append(">").append(".\n"); 189 for(int i = 1; i < depth; i++){ 190 sb.append("OPTIONAL{\n"); 191 sb.append("?o").append(i).append(" ").append("?p").append(i).append(" ").append("?s").append(i-1).append(".\n"); 192 } 193 for(int i = 1; i < depth; i++){ 194 sb.append("}"); 195 } 196 sb.append("}\n"); 197 198 return sb.toString(); 199 } 200 201 /** 202 * A SPARQL CONSTRUCT query is created, to get a RDF graph for the given resource and recursion depth. 203 * @param resource The resource for which a CONSTRUCT query is created. 204 * @return The CONSTRUCT query 205 */ 206 private String makeConstructQueryObject2(String resource, int depth){ 207 StringBuilder sb = new StringBuilder(); 208 sb.append("CONSTRUCT {\n"); 209 sb.append("?s0 ").append("?p0 ").append("<").append(resource).append(">").append(".\n"); 210 if(depth > 1) { 211 sb.append("?s0 ").append("?p0_out ").append("?o0_out").append(".\n"); 212 } 213 for(int i = 1; i < depth; i++){ 214 sb.append("?o").append(i).append(" ").append("?p").append(i).append(" ").append("?s").append(i-1).append(".\n"); 215 } 216 sb.append("}\n"); 217 sb.append("WHERE {\n"); 218 sb.append("?s0 ").append("?p0 ").append("<").append(resource).append(">").append(".\n"); 219 if(depth > 1) { 220 sb.append("OPTIONAL{\n"); 221 sb.append("?s0 ").append("?p0_out ").append("?o0_out").append(".\n"); 222 sb.append("}\n"); 223 } 224 for(int i = 1; i < depth; i++){ 225 sb.append("OPTIONAL{\n"); 226 sb.append("?o").append(i).append(" ").append("?p").append(i).append(" ").append("?s").append(i-1).append(".\n"); 227 228 } 229 for(int i = 1; i < depth; i++){ 230 sb.append("}"); 231 } 232 sb.append("}\n"); 233 234 return sb.toString(); 235 } 236 237 @Override 238 public void setIgnoredProperties(Set<String> properties) { 239 } 240 241 /* (non-Javadoc) 242 * @see org.dllearner.kb.sparql.ConciseBoundedDescriptionGenerator#setAllowedObjectNamespaces(java.util.Set) 243 */ 244 @Override 245 public void setAllowedObjectNamespaces(Set<String> namespaces) { 246 } 247 248 public static void main(String[] args) throws Exception{ 249 org.apache.log4j.Logger.getRootLogger().setLevel(Level.DEBUG); 250 SparqlEndpoint endpoint = SparqlEndpoint.create("http://sake.informatik.uni-leipzig.de:8890/sparql", "http://dbpedia.org"); 251// endpoint = SparqlEndpoint.getEndpointDBpedia(); 252 ConciseBoundedDescriptionGenerator cbdGen = new SymmetricConciseBoundedDescriptionGeneratorImpl(endpoint); 253 254 Resource res = ResourceFactory.createResource("http://dbpedia.org/resource/Santa_Clara,_California"); 255 256 Model cbd = cbdGen.getConciseBoundedDescription(res.getURI(), 2); 257 System.out.println("#triples =\t" + cbd.size()); 258 259 System.out.println("#triples_out =\t" + cbd.listStatements(res, null, (RDFNode) null).toSet().size()); 260 cbd.listStatements(res, null, (RDFNode) null).toList().forEach(System.out::println); 261 262 System.out.println("#triples_in =\t" + cbd.listStatements(null, null, res).toSet().size()); 263 cbd.listStatements(null, null, res).toList().forEach(System.out::println); 264 265 } 266 267}