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 com.google.common.collect.Sets;
022import org.aksw.jena_sparql_api.cache.core.QueryExecutionFactoryCacheEx;
023import org.aksw.jena_sparql_api.cache.extra.CacheFrontend;
024import org.aksw.jena_sparql_api.cache.h2.CacheUtilsH2;
025import org.aksw.jena_sparql_api.core.FluentQueryExecutionFactory;
026import org.aksw.jena_sparql_api.core.QueryExecutionFactory;
027import org.aksw.jena_sparql_api.http.QueryExecutionHttpWrapper;
028import org.aksw.jena_sparql_api.model.QueryExecutionFactoryModel;
029import org.aksw.jena_sparql_api.pagination.core.QueryExecutionFactoryPaginated;
030import org.apache.jena.query.ParameterizedSparqlString;
031import org.apache.jena.query.QueryExecution;
032import org.apache.jena.rdf.model.Model;
033import org.apache.jena.riot.WebContent;
034import org.apache.jena.sparql.core.Var;
035import org.apache.jena.sparql.engine.http.QueryEngineHTTP;
036import org.apache.jena.sparql.util.FmtUtils;
037import org.apache.jena.vocabulary.RDF;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import java.util.Set;
042import java.util.TreeSet;
043import java.util.concurrent.TimeUnit;
044import java.util.stream.Collectors;
045
046/**
047 * {@inheritDoc}
048 * @author Lorenz Buehmann
049 *
050 */
051public class ConciseBoundedDescriptionGeneratorImpl extends AbstractConciseBoundedDescriptionGenerator {
052        
053        private boolean useSingleQuery = false;
054
055        public ConciseBoundedDescriptionGeneratorImpl(QueryExecutionFactory qef) {
056                super(qef);
057        }
058
059        /**
060         * @deprecated Will be removed in next release as it is redundant. Please use {@link ConciseBoundedDescriptionGeneratorImpl#ConciseBoundedDescriptionGeneratorImpl(QueryExecutionFactory)}
061         * @param endpoint
062         * @param cacheDir
063         */
064        @Deprecated()
065        public ConciseBoundedDescriptionGeneratorImpl(SparqlEndpoint endpoint, String cacheDir) {
066                this(FluentQueryExecutionFactory
067                                .http(endpoint.getURL().toString(), endpoint.getDefaultGraphURIs())
068                                .config().withPostProcessor(qe -> ((QueryEngineHTTP) ((QueryExecutionHttpWrapper) qe).getDecoratee())
069                                                .setModelContentType(WebContent.contentTypeRDFXML))
070                                .end()
071                                .create());
072
073                if(cacheDir != null){
074                                long timeToLive = TimeUnit.DAYS.toMillis(30);
075                                CacheFrontend cacheFrontend = CacheUtilsH2.createCacheFrontend(cacheDir, true, timeToLive);
076                                qef = new QueryExecutionFactoryCacheEx(qef, cacheFrontend);
077                }
078                qef = new QueryExecutionFactoryPaginated(qef, 10000);
079        }
080
081        public ConciseBoundedDescriptionGeneratorImpl(SparqlEndpoint endpoint) {
082                this(endpoint, null);
083        }
084        
085        public ConciseBoundedDescriptionGeneratorImpl(Model model) {
086                this(new QueryExecutionFactoryModel(model));
087        }
088
089        @Override
090        public Model getConciseBoundedDescription(Set<String> resources, int depth, boolean withTypesForLeafs) {
091                if (useSingleQuery) {
092                        log.trace("Computing CBDs for {} ...", resources);
093                        long start = System.currentTimeMillis();
094                        // build the template
095                        ParameterizedSparqlString template = generateQueryTemplate(depth, withTypesForLeafs, true);
096
097                        // set the VALUES clause
098                        String query = template.toString().replace("%VALUES%", resources.stream().map(r -> "<" + r + ">").collect(Collectors.joining(" ")));
099                        log.trace(query);
100                        System.out.println(query);
101
102                        try (QueryExecution qe = qef.createQueryExecution(query)) {
103                                Model model = qe.execConstruct();
104                                log.trace("Got {} triples in {} ms.", model.size(), (System.currentTimeMillis() - start));
105                                return model;
106                        } catch (Exception e) {
107                                log.error("Failed to computed CBD for resources {}", resources);
108                                throw new RuntimeException("Failed to computed CBD for resource " + resources, e);
109                        }
110                } else {
111                        return super.getConciseBoundedDescription(resources, depth, withTypesForLeafs);
112                }
113        }
114
115
116        /**
117         * A SPARQL CONSTRUCT query is created, to get a RDF graph for the given example with a specific recursion depth.
118         * @param resource The example resource for which a CONSTRUCT query is created.
119         * @return the SPARQL query
120         */
121        protected String generateQuery(String resource, int depth, boolean withTypesForLeafs){
122                ParameterizedSparqlString template = generateQueryTemplate(depth, withTypesForLeafs, false);
123                template.setIri("s", resource);
124                return template.toString();
125        }
126
127        private ParameterizedSparqlString generateQueryTemplate(int depth, boolean withTypesForLeafs, boolean withValuesSubjectAnchor){
128                int lastIndex = Math.max(0, depth - 1);
129
130
131                StringBuilder sb = new StringBuilder();
132                sb.append("CONSTRUCT {\n");
133                sb.append(triplePattern("?s", "?p0", "?o0"));
134//              sb.append("?p0 a ?type0.\n");
135                for(int i = 1; i < depth; i++){
136                        sb.append(triplePattern("?o" + (i-1), "?p" + i, "?o" + i));
137                }
138                if(withTypesForLeafs){
139                        sb.append("?o").append(lastIndex).append(" a ?type.\n");
140                }
141                sb.append("} WHERE {\n");
142                if(withValuesSubjectAnchor) {
143                        sb.append("VALUES ?s {%VALUES%}");
144                }
145                sb.append(triplePattern("?s", "?p0", "?o0"));
146                sb.append(createPredicateFilter(Var.alloc("p0")));
147                sb.append(createObjectFilter(Var.alloc("p0"), Var.alloc("o0")));
148//              sb.append("?p0 a ?type0.\n");
149                for(int i = 1; i < depth; i++){
150                        sb.append("OPTIONAL{\n");
151                        sb.append(triplePattern("?o" + (i-1), "?p" + i, "?o" + i));
152                        sb.append(createPredicateFilter(Var.alloc("p" + i)));
153                        sb.append(createObjectFilter(Var.alloc("p" + i), Var.alloc("o" + i)));
154                }
155                if(withTypesForLeafs){
156                        sb.append("OPTIONAL{?o").append(lastIndex).append(" a ?type.}\n");
157                }
158                for(int i = 1; i < depth; i++){
159                        sb.append("}");
160                }
161                sb.append("}\n");
162
163                return new ParameterizedSparqlString(sb.toString());
164        }
165
166        public static void main(String[] args) {
167                SparqlEndpoint endpoint = SparqlEndpoint.getEndpointDBpedia();
168                Set<String> ignoredProperties = Sets.newHashSet(
169                                "http://dbpedia.org/ontology/abstract",
170                                "http://dbpedia.org/ontology/wikiPageID",
171                                "http://dbpedia.org/ontology/wikiPageRevisionID",
172                                "http://dbpedia.org/ontology/wikiPageID");
173
174                ConciseBoundedDescriptionGenerator cbdGen = new ConciseBoundedDescriptionGeneratorImpl(endpoint);
175//              cbdGen.setIgnoredProperties(ignoredProperties);
176//              cbdGen.setAllowedPropertyNamespaces(Sets.newHashSet("http://dbpedia.org/ontology/"));
177//              cbdGen.setAllowedClassNamespaces(Sets.newHashSet("http://dbpedia.org/ontology/"));
178//              cbdGen.setAllowedObjectNamespaces(Sets.newHashSet("http://dbpedia.org/resource/"));
179                cbdGen = new CachingConciseBoundedDescriptionGenerator(cbdGen);
180//              cbdGen.setRestrictToNamespaces(Arrays.asList(new String[]{"http://dbpedia.org/ontology/", RDF.getURI(), RDFS.getURI()}));
181                Model cbd = cbdGen.getConciseBoundedDescription(Sets.newHashSet("http://dbpedia.org/resource/Leipzig", "http://dbpedia.org/resource/Dresden"),2);
182
183                System.out.println(cbd.size());
184        }
185
186        
187
188}