/* * 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 static org.modeshape.jcr.api.query.qom.Operator.GREATER_THAN; import static org.modeshape.jcr.api.query.qom.Operator.GREATER_THAN_OR_EQUAL_TO; import static org.modeshape.jcr.api.query.qom.Operator.LESS_THAN; import static org.modeshape.jcr.api.query.qom.Operator.LESS_THAN_OR_EQUAL_TO; import static org.modeshape.jcr.api.query.qom.Operator.LIKE; import static org.modeshape.jcr.api.query.qom.Operator.NOT_EQUAL_TO; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.BINARY_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.BOOLEAN_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.DATE_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.DECIMAL_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.DOUBLE_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.LONG_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.NAME_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.PATH_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.REF_PROP; import static org.modeshape.jcr.index.lucene.PropertiesTestUtil.STRING_PROP; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.junit.Ignore; import org.junit.Test; import org.modeshape.common.FixFor; import org.modeshape.jcr.api.query.qom.Operator; import org.modeshape.jcr.query.model.Constraint; import org.modeshape.jcr.value.BinaryValue; import org.modeshape.jcr.value.NameFactory; import org.modeshape.jcr.value.PathFactory; import org.modeshape.jcr.value.StringFactory; import org.modeshape.jcr.value.ValueFormatException; import org.modeshape.jcr.value.basic.ModeShapeDateTime; /** * Tests the search behavior of the {@link SingleColumnIndex} * * @author Horia Chiorean (hchiorea@redhat.com) */ public class SingleColumnIndexSearchTest extends AbstractLuceneIndexSearchTest { @Override protected LuceneIndex createIndex( String name ) { return new MultiColumnIndex(name + "-multi-valued", "default", config, PropertiesTestUtil.ALLOWED_PROPERTIES, context); } @Test public void shouldSearchForStringPropertyValueInComparisonConstraint() throws Exception { List<String> nodeKeys = indexNodes(STRING_PROP, "s1", "s2", "s1"); // nodes where value = s1 Constraint constraint = propertyValue(STRING_PROP, EQUAL_TO, "s1"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(0), nodeKeys.get(2)); // nodes where value = s2 constraint = propertyValue(STRING_PROP, EQUAL_TO, "s2"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); //nodes where value = s3 constraint = propertyValue(STRING_PROP, EQUAL_TO, "s3"); validateCardinality(constraint, 0); validateFilterResults(constraint, 0, false); // nodes where value > s1 constraint = propertyValue(STRING_PROP, GREATER_THAN, "s1"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); // nodes where value >= s1 constraint = propertyValue(STRING_PROP, GREATER_THAN_OR_EQUAL_TO, "s1"); validateCardinality(constraint, 3); validateFilterResults(constraint, 3, false, nodeKeys.toArray(new String[3])); // nodes where value < s2 constraint = propertyValue(STRING_PROP, LESS_THAN, "s2"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(0), nodeKeys.get(2)); // nodes where value <= s2 constraint = propertyValue(STRING_PROP, LESS_THAN_OR_EQUAL_TO, "s2"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where values LIKE 's' constraint = propertyValue(STRING_PROP, LIKE, "s%"); validateCardinality(constraint, 3); validateFilterResults(constraint, 1, false, nodeKeys.toArray(new String[3])); constraint = propertyValue(STRING_PROP, LIKE, "%s%"); validateCardinality(constraint, 3); validateFilterResults(constraint, 1, false, nodeKeys.toArray(new String[3])); // nodes where values != s11 constraint = propertyValue(STRING_PROP, NOT_EQUAL_TO, "s1"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); } @Test public void shouldSearchForMultiValueProperty() throws Exception { // validate that for an indexed multi-valued property the queries still work if *at least one* of the value matches // the constraint String nodeKey = UUID.randomUUID().toString(); addValues(nodeKey, STRING_PROP, "a", "ab", "abc"); Constraint equality = propertyValue(STRING_PROP, EQUAL_TO, "a"); validateCardinality(equality, 1); validateFilterResults(equality, 1, false, nodeKey); equality = propertyValue(STRING_PROP, EQUAL_TO, "ab"); validateCardinality(equality, 1); validateFilterResults(equality, 1, false, nodeKey); equality = propertyValue(STRING_PROP, EQUAL_TO, "abc"); validateCardinality(equality, 1); validateFilterResults(equality, 1, false, nodeKey); equality = propertyValue(STRING_PROP, EQUAL_TO, "d"); validateCardinality(equality, 0); Constraint length = length(STRING_PROP, EQUAL_TO, 1); validateCardinality(length, 1); validateFilterResults(length, 1, false, nodeKey); length = length(STRING_PROP, EQUAL_TO, 2); validateCardinality(length, 1); validateFilterResults(length, 1, false, nodeKey); length = length(STRING_PROP, EQUAL_TO, 3); validateCardinality(length, 1); validateFilterResults(length, 1, false, nodeKey); length = length(STRING_PROP, EQUAL_TO, 4); validateCardinality(length, 0); } @Test public void shouldSearchForPathPropertyValueInComparisonConstraint() throws Exception { PathFactory pathFactory = valueFactories.getPathFactory(); List<String> nodeKeys = indexNodes(PropertiesTestUtil.PATH_PROP, pathFactory.create("/a/b"), pathFactory.create("/a/d"), pathFactory.create("/b")); // nodes where value = /a/b Constraint constraint = propertyValue(PropertiesTestUtil.PATH_PROP, EQUAL_TO, "/a/b"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); //nodes where value = /c constraint = propertyValue(PropertiesTestUtil.PATH_PROP, EQUAL_TO, "/c"); validateCardinality(constraint, 0); validateFilterResults(constraint, 0, false); // nodes where value > /a/a constraint = propertyValue(PropertiesTestUtil.PATH_PROP, GREATER_THAN, "/a/a"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where value >= /a/b constraint = propertyValue(PropertiesTestUtil.PATH_PROP, GREATER_THAN_OR_EQUAL_TO, "/a/b"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where value < /z constraint = propertyValue(PropertiesTestUtil.PATH_PROP, LESS_THAN, "/z"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where value <= /b constraint = propertyValue(PropertiesTestUtil.PATH_PROP, LESS_THAN_OR_EQUAL_TO, "/b"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where values LIKE '/a/%' constraint = propertyValue(PropertiesTestUtil.PATH_PROP, LIKE, "/a/%"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodeKeys.get(0), nodeKeys.get(1)); // nodes where values != '/a/b' constraint = propertyValue(PropertiesTestUtil.PATH_PROP, NOT_EQUAL_TO, "/a/b"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(0), nodeKeys.get(1)); } @Test public void shouldSearchForNamePropertyValueInComparisonConstraint() throws Exception { NameFactory nameFactory = valueFactories.getNameFactory(); List<String> nodeKeys = indexNodes(NAME_PROP, nameFactory.create("jcr:name1"), nameFactory.create("jcr:name2"), nameFactory.create("mode:name1")); // nodes where value = jcr:name1 Constraint constraint = propertyValue(NAME_PROP, EQUAL_TO, "jcr:name1"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); //nodes where value = /jcr:name3 constraint = propertyValue(NAME_PROP, EQUAL_TO, "jcr:name3"); validateCardinality(constraint, 0); validateFilterResults(constraint, 0, false); // nodes where value > jcr:name constraint = propertyValue(NAME_PROP, GREATER_THAN, "jcr:name"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where value >= jcr:name1 constraint = propertyValue(NAME_PROP, GREATER_THAN_OR_EQUAL_TO, "jcr:name1"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodeKeys.toArray(new String[3])); // nodes where value < "mode:name1" constraint = propertyValue(NAME_PROP, LESS_THAN, "mode:name1"); validateCardinality(constraint, 1); validateFilterResults(constraint, 2, false, nodeKeys.get(0)); // nodes where value <= mode:name1 constraint = propertyValue(NAME_PROP, LESS_THAN_OR_EQUAL_TO, "mode:name1"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(0), nodeKeys.get(2)); // nodes where values LIKE 'jcr:' constraint = propertyValue(NAME_PROP, LIKE, "jcr:%"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodeKeys.get(0), nodeKeys.get(1)); // nodes where values != 'mode:name1' constraint = propertyValue(NAME_PROP, NOT_EQUAL_TO, "mode:name1"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(1), nodeKeys.get(2)); } @Test public void shouldSearchForDecimalPropertyValueInComparisonConstraint() throws Exception { List<String> nodesWithDecimalProp = indexNodes(DECIMAL_PROP, BigDecimal.valueOf(1.1), BigDecimal.valueOf(1.3), BigDecimal.valueOf(1.5)); // = Constraint constraint = propertyValue(DECIMAL_PROP, EQUAL_TO, "1.1"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithDecimalProp.get(0)); // > constraint = propertyValue(DECIMAL_PROP, GREATER_THAN, "1.1"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodesWithDecimalProp.get(1), nodesWithDecimalProp.get(2)); // >= constraint = propertyValue(DECIMAL_PROP, GREATER_THAN_OR_EQUAL_TO, "1.1"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithDecimalProp.toArray(new String[3])); // < constraint = propertyValue(DECIMAL_PROP, LESS_THAN, "1.3"); validateCardinality(constraint, 1); validateFilterResults(constraint, 2, false, nodesWithDecimalProp.get(0)); // <= constraint = propertyValue(DECIMAL_PROP, LESS_THAN_OR_EQUAL_TO, "1.3"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDecimalProp.get(0), nodesWithDecimalProp.get(1)); // LIKE (should work because big decimals are stored as strings...) constraint = propertyValue(DECIMAL_PROP, LIKE, "1%"); validateCardinality(constraint, 3); validateFilterResults(constraint, 1, false, nodesWithDecimalProp.toArray(new String[3])); // != constraint = propertyValue(DECIMAL_PROP, NOT_EQUAL_TO, "1.3"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDecimalProp.get(0), nodesWithDecimalProp.get(2)); } @Test public void shouldSearchForLongPropertyValueInComparisonConstraint() throws Exception { List<String> nodesWithLongProp = indexNodes(LONG_PROP, 101l, 103l, 105l); // = Constraint constraint = propertyValue(LONG_PROP, EQUAL_TO, "103"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithLongProp.get(1)); // > constraint = propertyValue(LONG_PROP, GREATER_THAN, "101"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodesWithLongProp.get(1), nodesWithLongProp.get(2)); // >= constraint = propertyValue(LONG_PROP, GREATER_THAN_OR_EQUAL_TO, "101"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithLongProp.toArray(new String[3])); // < constraint = propertyValue(LONG_PROP, LESS_THAN, "103"); validateCardinality(constraint, 1); validateFilterResults(constraint, 2, false, nodesWithLongProp.get(0)); // <= constraint = propertyValue(LONG_PROP, LESS_THAN_OR_EQUAL_TO, "103"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithLongProp.get(0), nodesWithLongProp.get(1)); // LIKE (compare as strings) constraint = propertyValue(LONG_PROP, LIKE, "10%"); try { validateCardinality(constraint, 3); fail("Should not be able to use the LIKE operator on LONG values"); } catch (ValueFormatException e) { //expected (can't search using LIKE) } // != constraint = propertyValue(LONG_PROP, NOT_EQUAL_TO, "103"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithLongProp.get(0), nodesWithLongProp.get(2)); } @Test public void shouldSearchForDoublePropertyValueInComparisonConstraint() throws Exception { List<String> nodesWithDoubleProp = indexNodes(DOUBLE_PROP, 10d, 13d, 15d); // = Constraint constraint = propertyValue(DOUBLE_PROP, EQUAL_TO, "15"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithDoubleProp.get(2)); // > constraint = propertyValue(DOUBLE_PROP, GREATER_THAN, "10"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodesWithDoubleProp.get(1), nodesWithDoubleProp.get(2)); // >= constraint = propertyValue(DOUBLE_PROP, GREATER_THAN_OR_EQUAL_TO, "10"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithDoubleProp.toArray(new String[3])); // < constraint = propertyValue(DOUBLE_PROP, LESS_THAN, "13"); validateCardinality(constraint, 1); validateFilterResults(constraint, 2, false, nodesWithDoubleProp.get(0)); // <= constraint = propertyValue(DOUBLE_PROP, LESS_THAN_OR_EQUAL_TO, "13"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDoubleProp.get(0), nodesWithDoubleProp.get(1)); // LIKE (compare as strings) constraint = propertyValue(DOUBLE_PROP, LIKE, "10%"); try { validateCardinality(constraint, 3); fail("Should not be able to use the LIKE operator on DOUBLE values"); } catch (ValueFormatException e) { //expected (can't search using LIKE) } // != constraint = propertyValue(DOUBLE_PROP, NOT_EQUAL_TO, "13"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDoubleProp.get(0), nodesWithDoubleProp.get(2)); } @Test public void shouldSearchForDatePropertyValueInComparisonConstraint() throws Exception { List<String> nodesWithDateProp = indexNodes(DATE_PROP, new ModeShapeDateTime("2015-10-13"), new ModeShapeDateTime("2015-10-15"), new ModeShapeDateTime("2015-10-17")); // = Constraint constraint = propertyValue(DATE_PROP, EQUAL_TO, "2015-10-17"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithDateProp.get(2)); // > constraint = propertyValue(DATE_PROP, GREATER_THAN, "2015-10-13"); validateCardinality(constraint, 2); validateFilterResults(constraint, 1, false, nodesWithDateProp.get(1), nodesWithDateProp.get(2)); // >= constraint = propertyValue(DATE_PROP, GREATER_THAN_OR_EQUAL_TO, "2015-10-13"); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithDateProp.toArray(new String[3])); // < constraint = propertyValue(DATE_PROP, LESS_THAN, "2015-10-15"); validateCardinality(constraint, 1); validateFilterResults(constraint, 2, false, nodesWithDateProp.get(0)); // <= constraint = propertyValue(DATE_PROP, LESS_THAN_OR_EQUAL_TO, "2015-10-15"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDateProp.get(0), nodesWithDateProp.get(1)); // LIKE (compare as strings) constraint = propertyValue(DATE_PROP, LIKE, "2015-10-15%"); try { validateCardinality(constraint, 3); fail("Should not be able to use the LIKE operator on DOUBLE values"); } catch (ValueFormatException e) { //expected (can't search using LIKE) } // != constraint = propertyValue(DATE_PROP, NOT_EQUAL_TO, "2015-10-15"); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithDateProp.get(0), nodesWithDateProp.get(2)); } @Test public void shouldSearchForBooleanPropertyValueInComparisonConstraint() throws Exception { List<String> nodesWithBooleanProp = indexNodes(BOOLEAN_PROP, false, true, false); // = Constraint constraint = propertyValue(BOOLEAN_PROP, EQUAL_TO, true); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithBooleanProp.get(1)); // > constraint = propertyValue(BOOLEAN_PROP, GREATER_THAN, false); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithBooleanProp.get(1)); constraint = propertyValue(BOOLEAN_PROP, GREATER_THAN, true); validateCardinality(constraint, 0); // >= constraint = propertyValue(BOOLEAN_PROP, GREATER_THAN_OR_EQUAL_TO, false); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithBooleanProp.toArray(new String[3])); // < constraint = propertyValue(BOOLEAN_PROP, LESS_THAN, true); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithBooleanProp.get(0), nodesWithBooleanProp.get(1)); constraint = propertyValue(BOOLEAN_PROP, LESS_THAN, false); validateCardinality(constraint, 0); // <= constraint = propertyValue(BOOLEAN_PROP, LESS_THAN_OR_EQUAL_TO, true); validateCardinality(constraint, 3); validateFilterResults(constraint, 2, false, nodesWithBooleanProp.toArray(new String[3])); // LIKE (compare as strings) constraint = propertyValue(BOOLEAN_PROP, LIKE, "true%"); try { validateCardinality(constraint, 3); fail("Should not be able to use the LIKE operator on DOUBLE values"); } catch (LuceneIndexException e) { //expected (can't search using LIKE) } // != constraint = propertyValue(BOOLEAN_PROP, NOT_EQUAL_TO, true); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodesWithBooleanProp.get(0), nodesWithBooleanProp.get(2)); } @Test public void shouldSearchForReferencePropertyValueInComparisonConstraint() throws Exception { String ref1 = UUID.randomUUID().toString(); String ref2 = UUID.randomUUID().toString(); List<String> nodesWithRefProp = indexNodes(REF_PROP, ref1, ref2); // = Constraint constraint = propertyValue(REF_PROP, EQUAL_TO, ref1); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithRefProp.get(0)); // != constraint = propertyValue(REF_PROP, NOT_EQUAL_TO, ref1); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithRefProp.get(1)); } @Test public void shouldSearchForReferenceValueInComparisonConstraint() throws Exception { String ref1 = UUID.randomUUID().toString(); String ref2 = UUID.randomUUID().toString(); List<String> nodesWithRefProp = indexNodes(REF_PROP, ref1, ref2); Constraint constraint = referenceValue(REF_PROP, EQUAL_TO, ref1, true, true); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithRefProp.get(0)); constraint = referenceValue(null, EQUAL_TO, ref1, true, true); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodesWithRefProp.get(0)); } @Test public void shouldSearchForLengthInComparisonConstraint() throws Exception { String stringVal = "1234"; String stringNode = indexNodes(STRING_PROP, stringVal).get(0); NameFactory nameFactory = valueFactories.getNameFactory(); StringFactory stringFactory = valueFactories.getStringFactory(); String nameVal = stringFactory.create(nameFactory.create("jcr:value")); String nameNode = indexNodes(NAME_PROP, nameVal).get(0); PathFactory pathFactory = valueFactories.getPathFactory(); String pathVal = stringFactory.create(pathFactory.create("/a/b")); String pathNode = indexNodes(PATH_PROP, pathVal).get(0); BigDecimal decimal = BigDecimal.valueOf(12.4); String decimalStr = stringFactory.create(decimal); String decimalNode = indexNodes(DECIMAL_PROP, decimal).get(0); long longValue = 1234l; String longStr = stringFactory.create(longValue); String longNode = indexNodes(LONG_PROP, longValue).get(0); double doubleValue = 12346.35d; String doubleStr = stringFactory.create(doubleValue); String doubleNode = indexNodes(DOUBLE_PROP, doubleValue).get(0); org.modeshape.jcr.api.value.DateTime dateValue = valueFactories.getDateFactory().create("2015-10-10"); String dateStr = stringFactory.create(dateValue); String dateNode = indexNodes(DATE_PROP, dateValue).get(0); String ref = UUID.randomUUID().toString(); String refNode = indexNodes(REF_PROP, ref).get(0); boolean booleanValue = true; String booleanStr= stringFactory.create(booleanValue); String booleanNode = indexNodes(BOOLEAN_PROP, booleanValue).get(0); BinaryValue binaryValue = valueFactories.getBinaryFactory().create("some_binary"); String binaryNode = indexNodes(BINARY_PROP, binaryValue).get(0); assertLengthComparisonConstraint(STRING_PROP, stringNode, stringVal.length()); assertLengthComparisonConstraint(NAME_PROP, nameNode, nameVal.length()); assertLengthComparisonConstraint(PATH_PROP, pathNode, pathVal.length()); assertLengthComparisonConstraint(DECIMAL_PROP, decimalNode, decimalStr.length()); assertLengthComparisonConstraint(LONG_PROP, longNode, longStr.length()); assertLengthComparisonConstraint(DOUBLE_PROP, doubleNode, doubleStr.length()); assertLengthComparisonConstraint(DATE_PROP, dateNode, dateStr.length()); assertLengthComparisonConstraint(REF_PROP, refNode, ref.length()); assertLengthComparisonConstraint(BOOLEAN_PROP, booleanNode, booleanStr.length()); assertLengthComparisonConstraint(BINARY_PROP, binaryNode, (int)binaryValue.getSize()); } @Test public void shouldSearchForLowerAndUpperCasesInComparisonConstraint() throws Exception { List<String> nodeKeys = indexNodes(STRING_PROP, "A", "b"); Constraint constraint = lowerCase(STRING_PROP, EQUAL_TO, "a"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); constraint = lowerCase(STRING_PROP, EQUAL_TO, "b"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); constraint = upperCase(STRING_PROP, EQUAL_TO, "B"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); constraint = upperCase(STRING_PROP, EQUAL_TO, "A"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); } @Test public void shouldSearchForAndConstraint() throws Exception { List<String> nodeKeys = indexNodes(LONG_PROP, 1l, 3l, 5l); // >1 && <5 Constraint constraint = and(propertyValue(LONG_PROP, GREATER_THAN, 1), propertyValue(LONG_PROP, LESS_THAN, 5)); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(1)); } @Test public void shouldSearchForOrConstraint() throws Exception { List<String> nodeKeys = indexNodes(LONG_PROP, 1l, 3l, 5l); //>1 || <5 Constraint constraint = or(propertyValue(LONG_PROP, GREATER_THAN, 1), propertyValue(LONG_PROP, LESS_THAN, 5)); validateCardinality(constraint, 3); validateFilterResults(constraint, 3, false, nodeKeys.toArray(new String[nodeKeys.size()])); } @Test public void shouldSearchForNotConstraint() throws Exception { List<String> nodeKeys = indexNodes(LONG_PROP, 1l, 3l, 5l); // >1 Constraint constraint = not(propertyValue(LONG_PROP, GREATER_THAN, 1)); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); } @Test public void shouldSearchForSetConstraint() throws Exception { List<String> nodeKeys = indexNodes(LONG_PROP, 1l, 3l, 5l); // IN (1,2,3,4) Constraint constraint = set(LONG_PROP, 1, 2, 3, 4); validateCardinality(constraint, 2); validateFilterResults(constraint, 2, false, nodeKeys.get(0), nodeKeys.get(1)); } @Test public void shouldSearchForPropertyExistence() throws Exception { String longNode = indexNodes(LONG_PROP, 1l).get(0); Constraint constraint = propertyExistence(LONG_PROP); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, longNode); String stringNode = indexNodes(STRING_PROP, "a").get(0); constraint = propertyExistence(STRING_PROP); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, stringNode); constraint = propertyExistence(DATE_PROP); validateCardinality(constraint, 0); } @Test public void shouldSearchForBetweenConstraint() throws Exception { List<String> nodeKeys = indexNodes(LONG_PROP, 1l, 3l); // (0,2) Constraint between = between(LONG_PROP, 0, false, 2, false); validateCardinality(between, 1); validateFilterResults(between, 1, false, nodeKeys.get(0)); // [1,2) between = between(LONG_PROP, 1, true, 2, false); validateCardinality(between, 1); validateFilterResults(between, 1, false, nodeKeys.get(0)); // (1,2) between = between(LONG_PROP, 1, false, 2, false); validateCardinality(between, 0); // (2,4) between = between(LONG_PROP, 2, false, 4, false); validateCardinality(between, 1); validateFilterResults(between, 1, false, nodeKeys.get(1)); // (3,5) between = between(LONG_PROP, 3, false, 5, false); validateCardinality(between, 0); } @Test public void shouldSearchForRelikeConstraint() throws Exception { String node = indexNodes(STRING_PROP, "string%").get(0); Constraint relike = relike("string-1", STRING_PROP); validateCardinality(relike, 1); validateFilterResults(relike, 1, false, node); } @Test(expected = UnsupportedOperationException.class) public void shouldSupportFTSConstraint() throws Exception { validateCardinality(fullTextSearch(STRING_PROP, "some string"), 1); } @Test @Ignore("perf test") public void stringSearchPerfTest() throws Exception { int nodeCount = 500000; List<String> nodeKeys = new ArrayList<>(nodeCount); List<String> evenKeys = new ArrayList<>(nodeCount / 2); for (int i = 0; i < nodeCount; i++) { boolean even = i % 2 == 0; String value = "string_" + (even ? "even" : "odd"); List<String> keys = indexNodes(STRING_PROP, value); nodeKeys.addAll(keys); if (even) { evenKeys.addAll(keys); } } index.commit(); assertEquals(nodeCount, index.estimateTotalCount()); long start = System.nanoTime(); Constraint constraint = propertyValue(STRING_PROP, Operator.EQUAL_TO, "string_even"); validateCardinality(constraint, nodeCount / 2); validateFilterResults(constraint, 1000, false, evenKeys.toArray(new String[evenKeys.size()])); long duration = System.nanoTime() - start; long searchTime = TimeUnit.MILLISECONDS.convert(duration, TimeUnit.NANOSECONDS); System.out.println(Thread.currentThread().getName() + ": (" + index.getName() + ") Total time to search " + nodeKeys.size() + " nodes: " + searchTime/1000d + " seconds"); } @Test @FixFor( "MODE-2567" ) public void shouldSearchForLikeConstraintContainingSpaceAmpersand() throws Exception { List<String> nodeKeys = indexNodes(STRING_PROP, "Law & Order - S01E01"); // no leading or trailing space Constraint constraint = propertyValue(STRING_PROP, LIKE, "%&%"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); // leading space constraint = propertyValue(STRING_PROP, LIKE, "% &%"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); // trailing space constraint = propertyValue(STRING_PROP, LIKE, "%& %"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); // part of a larger search string constraint = propertyValue(STRING_PROP, LIKE, "%Law & Order%"); validateCardinality(constraint, 1); validateFilterResults(constraint, 1, false, nodeKeys.get(0)); } }