/* * ModeShape (http://www.modeshape.org) * * 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. */ package org.modeshape.jcr.index.lucene; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.modeshape.jcr.api.query.qom.Operator.EQUAL_TO; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.jcr.query.qom.JoinCondition; import org.junit.After; import org.junit.Before; import org.modeshape.common.util.FileUtil; import org.modeshape.jcr.ExecutionContext; import org.modeshape.jcr.api.query.qom.Operator; import org.modeshape.jcr.cache.NodeKey; import org.modeshape.jcr.query.model.And; import org.modeshape.jcr.query.model.Between; import org.modeshape.jcr.query.model.Comparison; import org.modeshape.jcr.query.model.Constraint; import org.modeshape.jcr.query.model.FullTextSearch; import org.modeshape.jcr.query.model.Length; import org.modeshape.jcr.query.model.Literal; import org.modeshape.jcr.query.model.LowerCase; import org.modeshape.jcr.query.model.Not; import org.modeshape.jcr.query.model.Or; import org.modeshape.jcr.query.model.PropertyExistence; import org.modeshape.jcr.query.model.PropertyValue; import org.modeshape.jcr.query.model.ReferenceValue; import org.modeshape.jcr.query.model.Relike; import org.modeshape.jcr.query.model.SelectorName; import org.modeshape.jcr.query.model.SetCriteria; import org.modeshape.jcr.query.model.StaticOperand; import org.modeshape.jcr.query.model.UpperCase; import org.modeshape.jcr.spi.index.provider.Filter; import org.modeshape.jcr.value.ValueFactories; /** * Base class for testing the search behavior on the various types of Lucene indexes. * * @author Horia Chiorean (hchiorea@redhat.com) */ public abstract class AbstractLuceneIndexSearchTest { protected static final SelectorName SELECTOR = new SelectorName("node"); protected ExecutionContext context; protected LuceneConfig config; protected LuceneIndex index; protected ValueFactories valueFactories; protected abstract LuceneIndex createIndex( String name ); @Before public void setUp() throws Exception { String dir = "target/lucene-index-search-test"; FileUtil.delete(dir); config = LuceneConfig.onDisk(dir); context = new ExecutionContext(); valueFactories = context.getValueFactories(); index = createIndex("default"); } @After public void tearDown() throws Exception { index.shutdown(false); } protected void assertLengthComparisonConstraint( String propertyName, String expectedNodeKey, int expectedLength ) { Constraint constraint = length(propertyName, EQUAL_TO, expectedLength); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, expectedNodeKey); } protected void validateCardinality(Constraint constraint, long expected) { long actualValue = index.estimateCardinality(Collections.singletonList(constraint), Collections.emptyMap()); assertEquals("Incorrect returned cardinality:", expected, actualValue); } protected void validateFilterResults(Constraint constraint, int batchSize, boolean expectRealScore, String...expectedKeys) { long cardinalityEstimate = expectedKeys != null ? expectedKeys.length : 0; Filter.Results results = index.filter(new TestIndexConstraints(constraint), cardinalityEstimate); Map<String, Float> actualResults = new LinkedHashMap<>(); Filter.ResultBatch nextBatch; while ((nextBatch = results.getNextBatch(batchSize)).size() > 0) { Iterator<NodeKey> keyIterator = nextBatch.keys().iterator(); Iterator<Float> scoresIterator = nextBatch.scores().iterator(); while (keyIterator.hasNext() && scoresIterator.hasNext()) { actualResults.put(keyIterator.next().toString(), scoresIterator.next()); } } assertEquals("Incorrect number of results:", expectedKeys.length, actualResults.size()); for (String expectedKey : actualResults.keySet()) { Float score = actualResults.get(expectedKey); if (score == null) { fail("Expected key: " + expectedKey + " not found among results: " + actualResults.keySet()); } if (expectRealScore && score == 1.0f) { fail("Expected a real score but found: " + score); } } } @SafeVarargs protected final <T> void addValues( String nodeKey, String propertyName, T... values ) { index.add(nodeKey, propertyName, values); } private <T> String insertValue( String propertyName, T value ) { String nodeKey = UUID.randomUUID().toString(); index.add(nodeKey, propertyName, value); return nodeKey; } @SafeVarargs protected final <T> List<String> indexNodes(String propertyName, T...values) { List<String> result = new ArrayList<>(); for (T value : values) { result.add(insertValue(propertyName, value)); } return result; } protected Constraint relike(Object value, String propertyName) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); return new Relike(new Literal(value), propValue); } protected Constraint and(Constraint left, Constraint right) { return new And(left, right); } protected Constraint or(Constraint left, Constraint right) { return new Or(left, right); } protected Constraint not(Constraint constraint) { return new Not(constraint); } protected Constraint set(String propertyName, Object...values) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); Collection<StaticOperand> operands = new ArrayList<>(values.length); for (Object value : values) { operands.add(new Literal(value)); } return new SetCriteria(propValue, operands); } protected Constraint between(String propertyName, Object lowerBound, boolean includeLowerBound, Object upperBound, boolean includeUpperBound) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); return new Between(propValue, new Literal(lowerBound), new Literal(upperBound), includeLowerBound, includeUpperBound); } protected Constraint fullTextSearch(String propertyName, String expression) { return new FullTextSearch(SELECTOR, propertyName, expression); } protected Comparison lowerCase( String propertyName, Operator operator, Object value ) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); LowerCase lowerCase = new LowerCase(propValue); return new Comparison(lowerCase, operator, new Literal(value)); } protected Comparison upperCase( String propertyName, Operator operator, Object value ) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); UpperCase upperCase = new UpperCase(propValue); return new Comparison(upperCase, operator, new Literal(value)); } protected Comparison propertyValue( String propertyName, Operator operator, Object value ) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); return new Comparison(propValue, operator, new Literal(value)); } protected Comparison referenceValue( String propertyName, Operator operator, Object value, boolean includeWeak, boolean includeSimple ) { ReferenceValue referenceValue = new ReferenceValue(SELECTOR, propertyName, includeWeak, includeSimple); return new Comparison(referenceValue, operator, new Literal(value)); } protected Comparison length( String propertyName, Operator operator, Object value ) { PropertyValue propValue = new PropertyValue(SELECTOR, propertyName); Length length = new Length(propValue); return new Comparison(length, operator, new Literal(value)); } protected PropertyExistence propertyExistence(String propertyName) { return new PropertyExistence(SELECTOR, propertyName); } protected class TestIndexConstraints implements org.modeshape.jcr.spi.index.IndexConstraints { private final Constraint constraint; private TestIndexConstraints( Constraint constraint ) { this.constraint = constraint; } @Override public boolean hasConstraints() { return true; } @Override public Collection<javax.jcr.query.qom.Constraint> getConstraints() { return Collections.<javax.jcr.query.qom.Constraint>singleton(constraint); } @Override public Collection<JoinCondition> getJoinConditions() { return Collections.emptySet(); } @Override public Map<String, Object> getVariables() { return Collections.emptyMap(); } @Override public ValueFactories getValueFactories() { return context.getValueFactories(); } @Override public Map<String, Object> getParameters() { return Collections.emptyMap(); } } }