/*
* Copyright 2008 The Topaz Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* Contributions:
*/
package org.mulgara.resolver.lucene;
import org.apache.log4j.Logger;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.jrdf.graph.URIReference;
import org.mulgara.query.Constraint;
import org.mulgara.query.ConstraintExpression;
import org.mulgara.query.ConstraintElement;
import org.mulgara.query.ConstraintConjunction;
import org.mulgara.query.ConstraintOperation;
import org.mulgara.query.QueryException;
import org.mulgara.query.Variable;
import org.mulgara.query.rdf.URIReferenceImpl;
import org.mulgara.resolver.spi.AbstractSymbolicTransformer;
import org.mulgara.resolver.spi.SymbolicTransformationContext;
import org.mulgara.resolver.spi.SymbolicTransformationException;
/**
* A query transformer for lucene. This accept constraints like in the following example:
* <pre>
* select $x $score from ... where
* $x <mulgara:search> $s in <lucene> and
* $s <foo> 'story' in <lucene> and
* $s <mulgara:score> $score in <lucene>
* </pre>
*
* @created 2008-09-28
* @author Ronald Tschalär
* @licence Apache License v2.0
*/
public class LuceneTransformer extends AbstractSymbolicTransformer {
private static final Logger logger = Logger.getLogger(LuceneTransformer.class);
private final URI modelTypeURI;
private final URIReference searchPred;
private final URIReference scorePred;
/**
* Create a new tranformer to rewrite lucene queries.
*
* @param modelTypeURI the uri of the lucene model type
* @param searchPredUri the uri of the search predicate
* @param scorePredUri the uri of the score predicate
*/
public LuceneTransformer(URI modelTypeURI, URI searchPredUri, URI scorePredUri) {
this.modelTypeURI = modelTypeURI;
searchPred = new URIReferenceImpl(searchPredUri);
scorePred = new URIReferenceImpl(scorePredUri);
}
@Override
protected ConstraintExpression transformOperation(SymbolicTransformationContext context,
ConstraintOperation expr)
throws SymbolicTransformationException {
if (expr instanceof ConstraintConjunction)
return transformConj(context, (ConstraintConjunction)expr);
return super.transformOperation(context, expr);
}
@Override
protected ConstraintExpression transformConstraint(SymbolicTransformationContext context,
Constraint c)
throws SymbolicTransformationException {
if (c instanceof LuceneConstraint) return c;
try {
ConstraintElement ce = c.getModel();
if (ce instanceof URIReference) {
URI constraintModelType = context.mapToModelTypeURI(((URIReference)ce).getURI());
if (constraintModelType != null && constraintModelType.equals(modelTypeURI)) {
if (logger.isTraceEnabled()) logger.trace("Creating LC for: " + c);
return new LuceneConstraint(c, searchPred, scorePred);
}
}
return c;
} catch (QueryException eq) {
throw new SymbolicTransformationException("Failed to map model to model-type", eq);
}
}
private ConstraintConjunction transformConj(SymbolicTransformationContext context, ConstraintConjunction cc) throws SymbolicTransformationException {
List<ConstraintExpression> retainedArgs = new ArrayList<ConstraintExpression>();
Map<ConstraintElement, List<LuceneConstraint>> luceneArgs =
new HashMap<ConstraintElement, List<LuceneConstraint>>();
boolean transformed = false;
for (ConstraintExpression arg : cc.getElements()) {
ConstraintExpression trans = transformExpression(context, arg);
if (trans != arg) {
transformed = true;
}
if (trans instanceof LuceneConstraint) {
LuceneConstraint lc = (LuceneConstraint)trans;
Variable b = lc.getBindingVar();
if (b == null && lc.getSubject() instanceof Variable) b = (Variable)lc.getSubject();
if (b == null) {
retainedArgs.add(lc);
continue;
}
List<LuceneConstraint> cumulative = luceneArgs.get(b);
if (cumulative == null) {
cumulative = new ArrayList<LuceneConstraint>();
cumulative.add(lc);
luceneArgs.put(b, cumulative);
} else if (cumulative.size() > 1 ||
cumulative.get(0).getBindingVar() == null &&
cumulative.get(0).getPredicate() != null &&
lc.getBindingVar() == null && lc.getPredicate() != null) {
// backwards compat hack for multiple simple queries
cumulative.add(lc);
} else {
cumulative.iterator().next().conjoinWith(lc);
if (logger.isTraceEnabled()) logger.trace("Updated LC with: " + cumulative.iterator().next() + "; result: " + lc);
transformed = true;
}
if (logger.isTraceEnabled()) logger.trace("Updated LC with: " + lc + "; result: " + cumulative);
} else {
retainedArgs.add(trans);
}
}
if (transformed) {
for (List<LuceneConstraint> c : luceneArgs.values()) {
for (LuceneConstraint lc : c) {
lc.validate();
retainedArgs.add(lc);
}
}
return new ConstraintConjunction(retainedArgs);
} else {
return cc;
}
}
}