Skip to content

Commit

Permalink
Add constant subject native SPARQL support through jcr/sql2 conversion.
Browse files Browse the repository at this point in the history
  • Loading branch information
lsitu authored and Andrew Woods committed May 26, 2014
1 parent bbb23a5 commit b3319b7
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 5 deletions.
Expand Up @@ -317,4 +317,9 @@ protected void resetTranslationChain() {

private static final List<InternalIdentifierConverter> minimalTranslationChain =
singletonList((InternalIdentifierConverter) new NamespaceConverter());

@Override
public String getBaseUri() {
return uris.getBaseUri().toString();
}
}
Expand Up @@ -57,4 +57,11 @@ public interface IdentifierTranslator {
* @return Resource
*/
Resource getContext();

/**
* Get baseUri that help for testing the constant subject SPARQLto jcr/sql2 conversion
* with the DefaultIdentifierTranslator and the HttpIdentifierTranslator
* @return
*/
String getBaseUri();
}
Expand Up @@ -85,4 +85,8 @@ public boolean isFedoraGraphSubject(final Resource subject) {
return subject.isURIResource() && subject.getURI().startsWith(RESOURCE_NAMESPACE);
}

@Override
public String getBaseUri() {
return RESOURCE_NAMESPACE;
}
}
Expand Up @@ -83,7 +83,7 @@ public String getStatement() throws RepositoryException {

private QueryObjectModel getQuery() throws RepositoryException {
final QueryManager queryManager = session.getWorkspace().getQueryManager();
final JQLQueryVisitor jqlVisitor = new JQLQueryVisitor(session, jcrTools, queryManager);
final JQLQueryVisitor jqlVisitor = new JQLQueryVisitor(session, jcrTools, queryManager, subjects);
query.visit(jqlVisitor);
return jqlVisitor.getQuery();
}
Expand Down
Expand Up @@ -58,6 +58,7 @@
import com.hp.hpl.jena.sparql.syntax.ElementVisitor;

import org.apache.commons.lang.NotImplementedException;
import org.fcrepo.kernel.rdf.IdentifierTranslator;
import org.fcrepo.kernel.rdf.JcrRdfTools;
import org.fcrepo.transform.exception.JQLParsingException;
import org.modeshape.common.collection.Collections;
Expand Down Expand Up @@ -135,6 +136,7 @@ public class JQLQueryVisitor implements QueryVisitor, ElementVisitor, ExprVisito
private Map<String, Source> joins;
private Map<String, String> joinTypes;
private Map<String, JoinCondition> joinConditions;
private IdentifierTranslator subjects;

/**
* Create a new query
Expand All @@ -144,7 +146,12 @@ public class JQLQueryVisitor implements QueryVisitor, ElementVisitor, ExprVisito
*/
public JQLQueryVisitor(final Session session,
final JcrRdfTools jcrTools,
final QueryManager queryManager) {
final QueryManager queryManager,
final IdentifierTranslator subjects) {
if (null == subjects) {
throw new IllegalArgumentException("IdentifierTranslator is null!");
}

this.session = session;
this.jcrTools = jcrTools;
this.queryFactory = queryManager.getQOMFactory();
Expand All @@ -153,6 +160,7 @@ public JQLQueryVisitor(final Session session,
this.joins = new HashMap<>();
this.joinTypes = new HashMap<>();
this.joinConditions = new HashMap<>();
this.subjects = subjects;
}

/**
Expand All @@ -168,6 +176,7 @@ public JQLQueryVisitor(final JQLQueryVisitor jqlQueryVisitor) {
this.variables = jqlQueryVisitor.variables;
this.joins = jqlQueryVisitor.joins;
this.joinConditions = jqlQueryVisitor.joinConditions;
this.subjects = jqlQueryVisitor.subjects;
}

/**
Expand Down Expand Up @@ -545,9 +554,7 @@ public void visit(final ElementPathBlock el) {
throw new NotImplementedException("Element path with constant subject and variable predicate");

} else if (object.isVariable()) {
throw new NotImplementedException(
"Element path with constant subject and predicate, and a variable object");

convertConstantSubjectElementBlock(subject, predicate, object, defaultModel);
} else {
throw new NotImplementedException("Element path with constant subject/predicate/object");
}
Expand All @@ -558,6 +565,76 @@ public void visit(final ElementPathBlock el) {
}
}

private void convertConstantSubjectElementBlock(final Node subject,
final Node predicate,
final Node object,
final Model model)
throws RepositoryException {

final String subjectUri = subject.getURI();

// Go through the IdentifierConverter for potential transparent hierarchy path conversion.
final String path = subjects.getPathFromSubject(model.createResource(subjectUri));

final String subjectSelector = "fedoraResource_" + path.replace("/", "_");

joins.put(subjectUri, queryFactory.selector(FEDORA_RESOURCE, subjectSelector));

// Trick to add constraint for the constant subject through the jcr internal property jcr:path
addPathConstraint(subjectSelector, model, path);

if (predicate.isVariable()) {
throw new NotImplementedException("Element path may not contain a variable predicate");
}

final String propertyName = jcrTools.getPropertyNameFromPredicate(model.createProperty(predicate.getURI()));
if (propertyName.equals("rdf:type") && object.isURI()) {
final String mixinName = jcrTools.getPropertyNameFromPredicate(model.createProperty(object.getURI()));

if (session.getWorkspace().getNodeTypeManager().hasNodeType(mixinName)) {
final String selectorName = "ref_type_" + mixinName.replace(":", "_");

joins.put(selectorName, queryFactory.selector(mixinName, selectorName));

joinTypes.put(selectorName, JCR_JOIN_TYPE_INNER);
joinConditions.put(selectorName,
queryFactory.sameNodeJoinCondition(subject.getURI(), selectorName, "."));
return;
}
}

final Column objectColumn;
final int propertyType = jcrTools.getPropertyType(FEDORA_RESOURCE, propertyName);
if ((propertyType == REFERENCE || propertyType == WEAKREFERENCE || propertyType == URI)
&& variables.containsKey(object.getName())) {

objectColumn = variables.get(object.getName());

final String joinPropertyName;

if (propertyType == URI) {
joinPropertyName = getReferencePropertyName(propertyName);
} else {
joinPropertyName = propertyName;
}

joinConditions.put(object.getName(), queryFactory.equiJoinCondition(
subjectSelector, joinPropertyName, objectColumn.getSelectorName(), "jcr:uuid"));
} else {
objectColumn = queryFactory.column(subjectSelector, propertyName, object.getName());

variables.put(object.getName(), objectColumn);

if (resultsVars.contains(object.getName())) {
columns.add(objectColumn);
}
}

if (!inOptional) {
appendConstraint(queryFactory.propertyExistence(subjectSelector, propertyName));
}
}

@Override
public void visit(final ElementFilter el) {
LOGGER.trace("VISIT FILTER: {}", el);
Expand Down Expand Up @@ -846,4 +923,17 @@ private void appendConstraint(final Constraint c) throws RepositoryException {
constraint = queryFactory.and(constraint, c);
}
}

/*
* jcr:path property constraint for a constant subject
*/
private void addPathConstraint(final String selector, final Model model, final String path)
throws RepositoryException {
final PropertyValue fieldPath = queryFactory.propertyValue(selector, "jcr:path");
final Value jcrValuePath = jcrTools.createValue(model.createLiteral(path),
jcrTools.getPropertyType(FEDORA_RESOURCE, "jcr:path"));
final Literal literalPath = queryFactory.literal(jcrValuePath);
appendConstraint(queryFactory.comparison(fieldPath, JCR_OPERATOR_EQUAL_TO, literalPath));
}

}
Expand Up @@ -284,4 +284,62 @@ public void testComplexQuery() throws RepositoryException {

assertEquals(expectedQuery.replaceAll("ns001", namespacePrefix), statement);
}

@Test
public void testConstantSubjectQuery() throws RepositoryException {
final String path = "/foo";
final String selector = "fedoraResource_" + path.replace("/", "_");
final String baseUri = subjects.getBaseUri();
final String subjectUri = (baseUri.endsWith("/") ? baseUri.substring(0, baseUri.length() - 1) : baseUri) + path;
final String sparql = "PREFIX fcrepo: <http://fedora.info/definitions/v4/repository#> "
+ "select ?date where { <" + subjectUri + "> fcrepo:created ?date }";
final String expectedQuery =
"SELECT [" + selector + "].[jcr:created] AS date " +
"FROM [fedora:resource] AS [" + selector + "] " +
"WHERE ([" + selector + "].[jcr:path] = '" + path + "' AND " +
"[" + selector + "].[jcr:created] IS NOT NULL)";
final JQLConverter testObj = new JQLConverter(session, subjects, sparql);

assertEquals(expectedQuery, testObj.getStatement());
}

@Test
public void testConstantSubjectSimpleReferenceQuery() throws RepositoryException {
final String path = "/foo";
final String selector = "fedoraResource_" + path.replace("/", "_");
final String baseUri = subjects.getBaseUri();
final String subjectUri = (baseUri.endsWith("/") ? baseUri.substring(0, baseUri.length() - 1) : baseUri) + path;
final String sparql =
"PREFIX fedorarelsext: <http://fedora.info/definitions/v4/rels-ext#> " +
"SELECT ?part WHERE { <" + subjectUri + "> fedorarelsext:hasPart ?part }";
final String expectedQuery =
"SELECT [" + selector + "].[fedorarelsext:hasPart] AS part " +
"FROM [fedora:resource] AS [" + selector + "] " +
"WHERE ([" + selector + "].[jcr:path] = '" + path + "' AND " +
"[" + selector + "].[fedorarelsext:hasPart] IS NOT NULL)";
final JQLConverter testObj = new JQLConverter(session, subjects, sparql);
assertEquals(expectedQuery, testObj.getStatement());

}

@Test
public void testConstantSubjectReferenceQuery() throws RepositoryException {
final String path = "/foo";
final String selector = "fedoraResource_" + path.replace("/", "_");
final String baseUri = subjects.getBaseUri();
final String subjectUri = (baseUri.endsWith("/") ? baseUri.substring(0, baseUri.length() - 1) : baseUri) + path;
final String sparql = "PREFIX dc: <http://purl.org/dc/elements/1.1/>" +
"PREFIX fedorarelsext: <http://fedora.info/definitions/v4/rels-ext#>" +
"SELECT ?title WHERE { <" + subjectUri + "> fedorarelsext:hasPart ?part . " +
"?part dc:title ?title }";
final String expectedQuery =
"SELECT [fedoraResource_part].[dc:title] AS title FROM [fedora:resource] AS " +
"[" + selector + "] LEFT OUTER JOIN [fedora:resource] AS [fedoraResource_part] ON " +
"[" + selector + "].[fedorarelsext:hasPart] = [fedoraResource_part].[jcr:uuid] " +
"WHERE (([" + selector + "].[jcr:path] = '" + path + "' AND " +
"[" + selector + "].[fedorarelsext:hasPart] IS NOT NULL) AND " +
"[fedoraResource_part].[dc:title] IS NOT NULL)";
final JQLConverter testObj = new JQLConverter(session, subjects, sparql);
assertEquals(expectedQuery, testObj.getStatement());
}
}

0 comments on commit b3319b7

Please sign in to comment.