package org.deri.grefine.reconcile.rdf.factories; import org.deri.grefine.reconcile.model.ReconciliationRequest; import org.deri.grefine.reconcile.model.ReconciliationRequestContext.PropertyContext; import org.deri.grefine.reconcile.util.StringUtils; import org.json.JSONException; import org.json.JSONWriter; import com.google.common.collect.ImmutableList; public class VirtuosoSparqlQueryFactory extends AbstractSparqlQueryFactory{ /* (non-Javadoc) * * * a similar implementation to Jena turns out to run very slowly in Virtuoso so we used a different SPARQL style * * ?entity ?p ?label. * ?label bif:contains 'query' OPTION(score ?score1). * FILTER (?p IN(rdfs:label, skos:prefLabel) ) * * The slow version *NOT USED* * OPTIONAL{ ?entity rdfs:label ?label1. * ?label1 bif:contains 'query' OPTION(score ?score1) * } * OPTIONAL{ ?entity skos:prefLabel ?label2. * ?label2 bif:contains 'query' OPTION(score ?score2) * } */ @Override public String getReconciliationSparqlQuery(ReconciliationRequest request, ImmutableList<String> searchPropertyUris){ String propertiesList = StringUtils.join(searchPropertyUris, "> || ", "?p=<", "FILTER (", ">)"); String typeFilter = ""; String typesList = prepareSparqlList(request.getTypes()); if(!typesList.isEmpty()){ typeFilter = TYPE_FILTER.replace("[[TYPE_URIS_LIST]]",typesList); } int limit = getQueryLimit(request.getLimit()); String formattedQuery = formatTextQueryString(request.getQueryString()); //prepare context filter StringBuilder contextFilter = new StringBuilder(); for(PropertyContext prop : request.getContext().getProperties()){ contextFilter.append(PROPERTY_FILTER.replace("[[PROPERTY_URI]]", prop.getPid()).replace("[[VALUE]]", prop.getV().asSparqlValue())); } return RECONCILE_QUERY_TEMPLATE.replace("[[QUERY]]", formattedQuery) .replace("[[PROPERTY_URIS_FILTER]]", propertiesList) .replace("[[TYPE_FILTER]]", typeFilter) .replace("[[CONTEXT_FILTER]]", contextFilter.toString()) .replace("[[LIMIT]]",String.valueOf(limit)); } @Override public String getTypeSuggestSparqlQuery(String prefix, int limit) { //We couldn't use parameterised query because ARQ 2.8.7 does not support QuerySolutionMap with sparqlService i.e. can't be used with remote SPARQL endpoints :-( #shame String formattedQuery = formatTextQueryString(prefix) + "*"; return SUGGEST_TYPE_QUERY_TEMPLATE.replace("[[QUERY]]", formattedQuery).replace("[[LIMIT]]",String.valueOf(limit)); } @Override public String getPropertySuggestSparqlQuery(String prefix, String typeUri, int limit) { String formattedQuery = formatTextQueryString(prefix) + "*"; return SUGGEST_PROPERTY_WITH_SPECIFIC_SUBJECT_TYPE_QUERY_TEMPLATE.replace("[[QUERY]]", formattedQuery) .replace("[[TYPE_URI]]", typeUri) .replace("[[LIMIT]]",String.valueOf(limit)); } @Override public String getPropertySuggestSparqlQuery(String prefix, int limit) { String formattedQuery = formatTextQueryString(prefix) + "*"; return SUGGEST_PROPERTY_QUERY_TEMPLATE.replace("[[QUERY]]", formattedQuery).replace("[[LIMIT]]",String.valueOf(limit)); } @Override public String getEntitySearchSparqlQuery(String prefix, ImmutableList<String> searchPropertyUris, int limit) { String propertiesList = StringUtils.join(searchPropertyUris, "> || ", "?p=<", "FILTER (", ">)"); String formattedQuery = formatTextQueryString(prefix); return SEARCH_ENTITY_QUERY_TEMPLATE.replace("[[QUERY]]", formattedQuery) .replace("[[PROPERTY_URIS_FILTER]]", propertiesList) .replace("[[LIMIT]]",String.valueOf(limit)); } @Override public void write(JSONWriter writer) throws JSONException { writer.object(); writer.key("type"); writer.value("virtuoso"); writer.endObject(); } protected int getQueryLimit(int limit){ //if each result has an average of 2 labels, we need limit*2 to get limit **unique** resource return limit * AVERAGE_NUM_OF_LABELS; } /** * @param query * @return formatted string for text search the format is based on lucene basic query syntax and will add '+' before each word * signaling that this term is mandatory. tokenization is based on spaces */ protected String formatTextQueryString(String query){ return "+" + query.trim().replaceAll("[\\s]+", " +").replaceAll("'", "\\\\\\\\'"); } private String prepareSparqlList(String[] uris){ StringBuilder sparqlList = new StringBuilder(); for(int i=0;i<uris.length;i++){ String uri = uris[i]; if(!uri.isEmpty()){ sparqlList.append("<").append(uri).append(">,"); } } if(sparqlList.length()>0){ sparqlList.setLength(sparqlList.length()-1); } return sparqlList.toString(); } //TODO accept as a parameter private static final int AVERAGE_NUM_OF_LABELS =2; private static final String RECONCILE_QUERY_TEMPLATE = "SELECT DISTINCT ?entity ?label ?score1 " + "WHERE" + "{" + "?entity ?p ?label. " + "?label <bif:contains> \"'[[QUERY]]'\" OPTION(score ?score1). " + "[[PROPERTY_URIS_FILTER]]. " + "[[TYPE_FILTER]]" + "[[CONTEXT_FILTER]]" + "FILTER isIRI(?entity). } ORDER BY desc(?score1) LIMIT [[LIMIT]]"; private static final String TYPE_FILTER = "?entity a ?type. " + "FILTER (?type IN ([[TYPE_URIS_LIST]])). "; private static final String SUGGEST_TYPE_QUERY_TEMPLATE = "SELECT DISTINCT ?type ?label ?score1 " + "WHERE " + "{" + "[] a ?type. " + "?type ?label_prop ?label. " + "FILTER (?label_prop=<http://www.w3.org/2000/01/rdf-schema#label> || ?label_prop=<http://www.w3.org/2004/02/skos/core#prefLabel>). " + "?label <bif:contains> \"'[[QUERY]]'\" OPTION(score ?score1). " + "} ORDER BY desc(?score1) LIMIT [[LIMIT]]"; private static final String SUGGEST_PROPERTY_QUERY_TEMPLATE = "SELECT DISTINCT ?p ?label ?score1 " + "WHERE " + "{" + "[] ?p ?v. " + "?p ?label_prop ?label. " + "FILTER (?label_prop=<http://www.w3.org/2000/01/rdf-schema#label> || ?label_prop=<http://www.w3.org/2004/02/skos/core#prefLabel>). " + "?label <bif:contains> \"'[[QUERY]]'\" OPTION(score ?score1). " + "} ORDER BY desc(?score1) LIMIT [[LIMIT]]"; private static final String SUGGEST_PROPERTY_WITH_SPECIFIC_SUBJECT_TYPE_QUERY_TEMPLATE = "SELECT DISTINCT ?p ?label ?score1 " + "WHERE " + "{" + "[] a <[[TYPE_URI]]>; " + "?p ?v. " + "?p ?label_prop ?label. " + "FILTER (?label_prop=<http://www.w3.org/2000/01/rdf-schema#label> || ?label_prop=<http://www.w3.org/2004/02/skos/core#prefLabel>). " + "?label <bif:contains> \"'[[QUERY]]'\" OPTION(score ?score1). " + "} ORDER BY desc(?score1) LIMIT [[LIMIT]]"; private static final String SEARCH_ENTITY_QUERY_TEMPLATE = "SELECT DISTINCT ?entity ?label " + "WHERE" + "{" + "?entity ?p ?label. " + "?label <bif:contains> \"'[[QUERY]]'\" OPTION(score ?score1). " + "[[PROPERTY_URIS_FILTER]]. " + "FILTER isIRI(?entity). } ORDER BY desc(?score1) LIMIT [[LIMIT]]"; private static final String PROPERTY_FILTER = "?entity <[[PROPERTY_URI]]> [[VALUE]]. "; }