This is an automated email from the ASF dual-hosted git repository. zhouxj pushed a commit to branch feature/GEODE-QueryProvider in repository https://gitbox.apache.org/repos/asf/geode.git
commit 7b5c8280def034e90cb707e4b5c3851eeec9afca Author: zhouxh <gz...@pivotal.io> AuthorDate: Wed Jun 13 10:01:34 2018 -0700 GEODE-QueryProvider: added pointsConfigMap to support numeric query Adds in a test for int range lucene query (#2065) --- .../geode/cache/lucene/FlatFormatSerializer.java | 21 ++ .../lucene/internal/LuceneIndexFactoryImpl.java | 3 +- .../cache/lucene/internal/StringQueryProvider.java | 36 ++ .../serializer/HeterogeneousLuceneSerializer.java | 18 + .../repository/serializer/PdxLuceneSerializer.java | 21 ++ .../serializer/ReflectionLuceneSerializer.java | 25 ++ .../cache/lucene/LuceneNumericQueryDUnitTest.java | 373 +++++++++++++++++++++ .../lucene/LuceneNumericQueryIntegrationTest.java | 142 ++++++++ 8 files changed, 638 insertions(+), 1 deletion(-) diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/FlatFormatSerializer.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/FlatFormatSerializer.java index f94f631..08cfcf4 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/FlatFormatSerializer.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/FlatFormatSerializer.java @@ -16,15 +16,18 @@ package org.apache.geode.cache.lucene; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.text.NumberFormat; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.logging.log4j.Logger; import org.apache.lucene.document.Document; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; import org.apache.geode.cache.lucene.internal.repository.serializer.SerializerUtil; import org.apache.geode.internal.logging.LogService; @@ -59,6 +62,7 @@ public class FlatFormatSerializer implements LuceneSerializer { private final ConcurrentMap<String, List<String>> tokenizedFieldCache = new ConcurrentHashMap<>(); private static final Logger logger = LogService.getLogger(); + private ConcurrentMap<String, PointsConfig> pointsConfigMap = new ConcurrentHashMap(); /** * Recursively serialize each indexed field's value into a field of lucene document. The field @@ -118,6 +122,7 @@ public class FlatFormatSerializer implements LuceneSerializer { Object fieldValue, List<String> tokenizedFields) { if (tokenizedFields.size() == 1) { SerializerUtil.addField(doc, indexedFieldName, fieldValue); + saveNumericFields(indexedFieldName, fieldValue); } else { addFieldValue(doc, indexedFieldName, fieldValue, tokenizedFields.subList(1, tokenizedFields.size())); @@ -147,4 +152,20 @@ public class FlatFormatSerializer implements LuceneSerializer { } } } + + private void saveNumericFields(String fieldName, Object fieldValue) { + Class<?> clazz = fieldValue.getClass(); + if (Integer.class.equals(clazz) || Integer.TYPE.equals(clazz) + || Float.class.equals(clazz) || Float.TYPE.equals(clazz) + || Long.class.equals(clazz) || Long.TYPE.equals(clazz) + || Double.class.equals(clazz) || Double.TYPE.equals(clazz)) { + pointsConfigMap.computeIfAbsent(fieldName, + field -> new PointsConfig(NumberFormat.getInstance(), (Class<? extends Number>) clazz)); + } + } + + public Map<String, PointsConfig> getPointsConfigMap() { + return pointsConfigMap; + } + } diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexFactoryImpl.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexFactoryImpl.java index 45d4fe1..0332954 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexFactoryImpl.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/LuceneIndexFactoryImpl.java @@ -22,11 +22,12 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.geode.cache.lucene.LuceneIndexFactory; import org.apache.geode.cache.lucene.LuceneSerializer; +import org.apache.geode.cache.lucene.internal.repository.serializer.HeterogeneousLuceneSerializer; public class LuceneIndexFactoryImpl implements LuceneIndexFactory { private final LuceneServiceImpl service; private final Map<String, Analyzer> fields = new LinkedHashMap<String, Analyzer>(); - private LuceneSerializer serializer; + private LuceneSerializer serializer = new HeterogeneousLuceneSerializer(); public LuceneIndexFactoryImpl(final LuceneServiceImpl luceneService) { diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/StringQueryProvider.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/StringQueryProvider.java index f0626b5..b8c0008 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/StringQueryProvider.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/StringQueryProvider.java @@ -18,16 +18,22 @@ package org.apache.geode.cache.lucene.internal; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.Arrays; +import java.util.Map; import org.apache.logging.log4j.Logger; import org.apache.lucene.queryparser.flexible.core.QueryNodeException; import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; import org.apache.lucene.search.Query; import org.apache.geode.DataSerializer; +import org.apache.geode.cache.lucene.FlatFormatSerializer; import org.apache.geode.cache.lucene.LuceneIndex; import org.apache.geode.cache.lucene.LuceneQueryException; import org.apache.geode.cache.lucene.LuceneQueryProvider; +import org.apache.geode.cache.lucene.LuceneSerializer; +import org.apache.geode.cache.lucene.internal.repository.serializer.HeterogeneousLuceneSerializer; import org.apache.geode.internal.DataSerializableFixedID; import org.apache.geode.internal.Version; import org.apache.geode.internal.i18n.LocalizedStrings; @@ -66,6 +72,36 @@ public class StringQueryProvider implements LuceneQueryProvider, DataSerializabl if (luceneQuery == null) { LuceneIndexImpl indexImpl = (LuceneIndexImpl) index; StandardQueryParser parser = new StandardQueryParser(indexImpl.getAnalyzer()); + // parser.getQueryConfigHandler().set(StandardQueryConfigHandler.ConfigurationKeys.DATE_RESOLUTION, + // DateTools.Resolution.MILLISECOND); + Map<String, PointsConfig> pointsConfigMap = null; + LuceneSerializer serializer = index.getLuceneSerializer(); + if (serializer instanceof HeterogeneousLuceneSerializer) { + HeterogeneousLuceneSerializer heteroSerializer = (HeterogeneousLuceneSerializer) serializer; + pointsConfigMap = heteroSerializer.getPointsConfigMap(); + } else if (serializer instanceof FlatFormatSerializer) { + FlatFormatSerializer flatFormatSerializer = (FlatFormatSerializer) serializer; + pointsConfigMap = flatFormatSerializer.getPointsConfigMap(); + } + logger.info(serializer + ":" + Arrays.toString(indexImpl.getFieldNames()) + ":" + + parser.getDateResolution()); + // pointsConfigMap.put("revenue", new PointsConfig(NumberFormat.getInstance(), + // Integer.class)); + parser.setPointsConfigMap(pointsConfigMap); + // TODO1: is DateTools.Resolution optional? + // parser.setDateResolution(DateTools.Resolution.MILLISECOND); + + // TODO2: HeterogeneousLuceneSerializer.getPointsConfigMap() calls putAll too often? + // TODO3: PdxLuceneSerializer.toDocuments().saveNumericFields's if (...) then + // computeIfAbsent(). Is it too expensive? + // TODO4: PdxLuceneSerializer and FlatFormatSerializer are using + // toDucuments().saveNumericFields() to get meta data, is it possible + // to move it to higher level for a generic method? + // TODO5: if a member is down, its meta data will be lost. How to recover it? + // TODO6: Short.class is not supported yet + // TODO7: Does PdxLuceneSerializer support nested field? + // TODO8: add example: numeric query __REGION_VALUE_FIELD:123 + // TODO9: Can FlatFormatSerializer support pdx since it does not contain PdxLuceneSerializer parser.setAllowLeadingWildcard(true); try { luceneQuery = parser.parse(query, defaultField); diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/HeterogeneousLuceneSerializer.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/HeterogeneousLuceneSerializer.java index 41281d5..74f05af 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/HeterogeneousLuceneSerializer.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/HeterogeneousLuceneSerializer.java @@ -17,9 +17,12 @@ package org.apache.geode.cache.lucene.internal.repository.serializer; import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.logging.log4j.Logger; import org.apache.lucene.document.Document; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; import org.apache.geode.cache.lucene.LuceneIndex; import org.apache.geode.cache.lucene.LuceneSerializer; @@ -45,6 +48,8 @@ public class HeterogeneousLuceneSerializer implements LuceneSerializer { private Map<Class<?>, LuceneSerializer> mappers = new CopyOnWriteWeakHashMap<Class<?>, LuceneSerializer>(); + private ConcurrentMap<String, PointsConfig> pointsConfigMap = new ConcurrentHashMap(); + private static final Logger logger = LogService.getLogger(); public HeterogeneousLuceneSerializer() { @@ -89,4 +94,17 @@ public class HeterogeneousLuceneSerializer implements LuceneSerializer { } } + public Map<String, PointsConfig> getPointsConfigMap() { + PdxLuceneSerializer pdxSerializer = (PdxLuceneSerializer) pdxMapper; + pointsConfigMap.putAll(pdxSerializer.getPointsConfigMap()); + + for (LuceneSerializer serializer : mappers.values()) { + if (serializer instanceof ReflectionLuceneSerializer) { + ReflectionLuceneSerializer reflectionSerializer = (ReflectionLuceneSerializer) serializer; + pointsConfigMap.putAll(reflectionSerializer.getPointsConfigMap()); + } + } + return pointsConfigMap; + } + } diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/PdxLuceneSerializer.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/PdxLuceneSerializer.java index b644548..38bccca 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/PdxLuceneSerializer.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/PdxLuceneSerializer.java @@ -15,11 +15,16 @@ package org.apache.geode.cache.lucene.internal.repository.serializer; +import java.text.NumberFormat; import java.util.Collection; import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.logging.log4j.Logger; import org.apache.lucene.document.Document; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; import org.apache.geode.cache.lucene.LuceneIndex; import org.apache.geode.cache.lucene.LuceneSerializer; @@ -32,9 +37,24 @@ import org.apache.geode.pdx.PdxInstance; class PdxLuceneSerializer implements LuceneSerializer { private static final Logger logger = LogService.getLogger(); + private ConcurrentMap<String, PointsConfig> pointsConfigMap = new ConcurrentHashMap(); public PdxLuceneSerializer() {} + private void saveNumericFields(String fieldName, Object fieldValue) { + Class<?> clazz = fieldValue.getClass(); + if (Integer.class.equals(clazz) || Integer.TYPE.equals(clazz) || Long.class.equals(clazz) + || Long.TYPE.equals(clazz) || Float.class.equals(clazz) || Float.TYPE.equals(clazz) + || Double.class.equals(clazz) || Double.TYPE.equals(clazz)) { + pointsConfigMap.computeIfAbsent(fieldName, + field -> new PointsConfig(NumberFormat.getInstance(), (Class<? extends Number>) clazz)); + } + } + + public Map<String, PointsConfig> getPointsConfigMap() { + return pointsConfigMap; + } + @Override public Collection<Document> toDocuments(LuceneIndex index, Object value) { Document doc = new Document(); @@ -46,6 +66,7 @@ class PdxLuceneSerializer implements LuceneSerializer { continue; } SerializerUtil.addField(doc, field, fieldValue); + saveNumericFields(field, fieldValue); } } if (logger.isDebugEnabled()) { diff --git a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/ReflectionLuceneSerializer.java b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/ReflectionLuceneSerializer.java index 947b2ad..50bcb9e 100644 --- a/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/ReflectionLuceneSerializer.java +++ b/geode-lucene/src/main/java/org/apache/geode/cache/lucene/internal/repository/serializer/ReflectionLuceneSerializer.java @@ -16,15 +16,20 @@ package org.apache.geode.cache.lucene.internal.repository.serializer; import java.lang.reflect.Field; +import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.logging.log4j.Logger; import org.apache.lucene.document.Document; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; import org.apache.geode.cache.lucene.LuceneIndex; import org.apache.geode.cache.lucene.LuceneSerializer; @@ -37,6 +42,7 @@ import org.apache.geode.internal.logging.LogService; class ReflectionLuceneSerializer implements LuceneSerializer { private Field[] fields; + private ConcurrentMap<String, PointsConfig> pointsConfigMap = new ConcurrentHashMap(); private static final Logger logger = LogService.getLogger(); @@ -54,6 +60,20 @@ class ReflectionLuceneSerializer implements LuceneSerializer { if (fieldSet.contains(field.getName()) && SerializerUtil.isSupported(type)) { field.setAccessible(true); foundFields.add(field); + + if (Integer.class.equals(type) || Integer.TYPE.equals(type)) { + pointsConfigMap.put(field.getName(), + new PointsConfig(NumberFormat.getInstance(), Integer.class)); + } else if (Long.class.equals(type) || Long.TYPE.equals(type)) { + pointsConfigMap.put(field.getName(), + new PointsConfig(NumberFormat.getInstance(), Long.class)); + } else if (Float.class.equals(type) || Float.TYPE.equals(type)) { + pointsConfigMap.put(field.getName(), + new PointsConfig(NumberFormat.getInstance(), Float.class)); + } else if (Double.class.equals(type) || Double.TYPE.equals(type)) { + pointsConfigMap.put(field.getName(), + new PointsConfig(NumberFormat.getInstance(), Double.class)); + } } } @@ -82,4 +102,9 @@ class ReflectionLuceneSerializer implements LuceneSerializer { } return Collections.singleton(doc); } + + public Map<String, PointsConfig> getPointsConfigMap() { + return pointsConfigMap; + } + } diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryDUnitTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryDUnitTest.java new file mode 100644 index 0000000..bc64fc2 --- /dev/null +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryDUnitTest.java @@ -0,0 +1,373 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.cache.lucene; + +import static org.apache.geode.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientRegionShortcut; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.QueryService; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.test.dunit.rules.ClientVM; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; +import org.apache.geode.test.junit.categories.DistributedTest; + +@Category(DistributedTest.class) +public class LuceneNumericQueryDUnitTest { + + @ClassRule + public static ClusterStartupRule clusterStartupRule = new ClusterStartupRule(); + + private static MemberVM locator; + private static MemberVM server1; + private static ClientVM client1; + + private static final SomeDomain someDomain1 = + SomeDomain.newBuilder() + .withStrField("strField1") + .withIntField(110) + .withLongField(10010L) + .withFloatField(101.25F) + .withDoubleField(101.25D) + .withDateField( + Date.from(LocalDateTime.parse("2001-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + private static final SomeDomain someDomain2 = + SomeDomain.newBuilder() + .withStrField("strField2") + .withIntField(120) + .withLongField(10020L) + .withFloatField(201.25F) + .withDoubleField(201.25D) + .withDateField( + Date.from(LocalDateTime.parse("2002-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + private static final SomeDomain someDomain3 = + SomeDomain.newBuilder() + .withStrField("strField3") + .withIntField(130) + .withLongField(10030L) + .withFloatField(301.25F) + .withDoubleField(301.25D) + .withDateField( + Date.from(LocalDateTime.parse("2003-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + + @BeforeClass + public static void beforeAllTests() throws Exception { + locator = clusterStartupRule.startLocatorVM(0); + final int locatorPort = locator.getPort(); + server1 = clusterStartupRule + .startServerVM(1, + r -> r.withPDXPersistent() + .withPDXReadSerialized() + .withConnectionToLocator(locatorPort) + .withProperty(SERIALIZABLE_OBJECT_FILTER, "org.apache.geode.cache.lucene.**")); + + + client1 = + clusterStartupRule.startClientVM(2, new Properties(), + (x -> x + .set(SERIALIZABLE_OBJECT_FILTER, "org.apache.geode.cache.lucene.**") + .addPoolLocator("localhost", locatorPort))); + + server1.invoke(() -> { + InternalCache cache = ClusterStartupRule.getCache(); + LuceneService luceneService = LuceneServiceProvider.get(cache); + luceneService.createIndexFactory() + .setFields("strField", "intField", "dateField", "longField", "floatField", "doubleField") + .create("idx1", + "/sampleregion"); + + cache.<String, SomeDomain>createRegionFactory(RegionShortcut.PARTITION) + .create("sampleregion"); + }); + + client1.invoke(() -> { + ClientCache clientCache = ClusterStartupRule.getClientCache(); + + Region<String, SomeDomain> region = + clientCache.<String, SomeDomain>createClientRegionFactory(ClientRegionShortcut.PROXY) + .create("sampleregion"); + region.put(someDomain1.getStrField(), someDomain1); + region.put(someDomain2.getStrField(), someDomain2); + region.put(someDomain3.getStrField(), someDomain3); + LuceneService luceneService = LuceneServiceProvider.get(clientCache); + luceneService.waitUntilFlushed("idx1", "sampleregion", 30000, TimeUnit.MILLISECONDS); + }); + } + + + @Test + public void testByIntegerRange() { + client1.invoke(() -> { + ClientCache clientCache = ClusterStartupRule.getClientCache(); + + QueryService queryService = clientCache.getQueryService(); + + Query query = queryService.newQuery("select * from /sampleregion"); + SelectResults results = (SelectResults) query.execute(); + assertThat(results).hasSize(3); + + LuceneService luceneService = LuceneServiceProvider.get(clientCache); + + LuceneQuery<String, SomeDomain> luceneQuery1 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+strField=strField* +intField:[110 TO 120]", + "strField"); + + assertThat(luceneQuery1.findKeys()) + .containsExactlyInAnyOrder("strField1", "strField2"); + + assertThat(luceneQuery1.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain1, someDomain2); + + + LuceneQuery<String, SomeDomain> luceneQuery2 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+strField=strField* +intField:[120 TO 130]", + "strField"); + + assertThat(luceneQuery2.findKeys()) + .containsExactlyInAnyOrder("strField2", "strField3"); + + assertThat(luceneQuery2.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain2, someDomain3); + + }); + } + + @Test + public void testByLongRange() { + client1.invoke(() -> { + ClientCache clientCache = ClusterStartupRule.getClientCache(); + + LuceneService luceneService = LuceneServiceProvider.get(clientCache); + + LuceneQuery<String, SomeDomain> luceneQuery1 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+longField:[10010 TO 10025]", "strField"); + + assertThat(luceneQuery1.findKeys()) + .containsExactlyInAnyOrder("strField1", "strField2"); + + assertThat(luceneQuery1.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain1, someDomain2); + + LuceneQuery<String, SomeDomain> luceneQuery2 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+longField:[10011 TO 10030]", "strField"); + + assertThat(luceneQuery2.findKeys()) + .containsExactlyInAnyOrder("strField2", "strField3"); + + assertThat(luceneQuery2.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain2, someDomain3); + }); + } + + @Test + public void testByFloatRange() { + client1.invoke(() -> { + ClientCache clientCache = ClusterStartupRule.getClientCache(); + + LuceneService luceneService = LuceneServiceProvider.get(clientCache); + + LuceneQuery<String, SomeDomain> luceneQuery1 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+floatField:[100 TO 202]", "strField"); + + assertThat(luceneQuery1.findKeys()) + .containsExactlyInAnyOrder("strField1", "strField2"); + + assertThat(luceneQuery1.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain1, someDomain2); + }); + } + + @Test + public void testByDoubleRange() { + client1.invoke(() -> { + ClientCache clientCache = ClusterStartupRule.getClientCache(); + + LuceneService luceneService = LuceneServiceProvider.get(clientCache); + + LuceneQuery<String, SomeDomain> luceneQuery1 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+doubleField:[100 TO 202]", "strField"); + + assertThat(luceneQuery1.findKeys()) + .containsExactlyInAnyOrder("strField1", "strField2"); + + assertThat(luceneQuery1.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain1, someDomain2); + }); + } +} + + +class SomeDomain implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String strField; + private final int intField; + private final long longField; + private final float floatField; + private final double doubleField; + private final Date dateField; + + public SomeDomain(String strField, int intField, long longField, float floatField, + double doubleField, Date dateField) { + this.strField = strField; + this.intField = intField; + this.longField = longField; + this.floatField = floatField; + this.doubleField = doubleField; + this.dateField = dateField; + } + + private SomeDomain(Builder builder) { + strField = builder.strField; + intField = builder.intField; + longField = builder.longField; + floatField = builder.floatField; + doubleField = builder.doubleField; + dateField = builder.dateField; + } + + public String getStrField() { + return strField; + } + + public int getIntField() { + return intField; + } + + public long getLongField() { + return longField; + } + + public float getFloatField() { + return floatField; + } + + public Date getDateField() { + return dateField; + } + + public double getDoubleField() { + return doubleField; + } + + public static Builder newBuilder() { + return new Builder(); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("SomeDomain{"); + sb.append("strField='").append(strField).append('\''); + sb.append(", height=").append(intField); + sb.append(", dateField=").append(dateField); + sb.append('}'); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SomeDomain that = (SomeDomain) o; + return intField == that.intField && + longField == that.longField && + Float.compare(that.floatField, floatField) == 0 && + Double.compare(that.doubleField, doubleField) == 0 && + Objects.equals(strField, that.strField) && + Objects.equals(dateField, that.dateField); + } + + @Override + public int hashCode() { + + return Objects.hash(strField, intField, longField, floatField, doubleField, dateField); + } + + public static final class Builder { + private String strField; + private int intField; + private long longField; + private float floatField; + private double doubleField; + private Date dateField; + + private Builder() {} + + public Builder withStrField(String val) { + strField = val; + return this; + } + + public Builder withIntField(int val) { + intField = val; + return this; + } + + public Builder withLongField(long val) { + longField = val; + return this; + } + + public Builder withFloatField(float val) { + floatField = val; + return this; + } + + public Builder withDoubleField(double val) { + doubleField = val; + return this; + } + + public Builder withDateField(Date val) { + dateField = val; + return this; + } + + public SomeDomain build() { + return new SomeDomain(this); + } + } +} diff --git a/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryIntegrationTest.java b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryIntegrationTest.java new file mode 100644 index 0000000..f841e6d --- /dev/null +++ b/geode-lucene/src/test/java/org/apache/geode/cache/lucene/LuceneNumericQueryIntegrationTest.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.cache.lucene; + +import static org.apache.geode.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.RegionShortcut; +import org.apache.geode.cache.query.Query; +import org.apache.geode.cache.query.QueryService; +import org.apache.geode.cache.query.SelectResults; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.test.junit.categories.DistributedTest; +import org.apache.geode.test.junit.rules.ServerStarterRule; + +@Category(DistributedTest.class) +public class LuceneNumericQueryIntegrationTest { + + @Rule + public ServerStarterRule serverStarterRule = new ServerStarterRule() + .withPDXPersistent() + .withPDXReadSerialized() + .withProperty(SERIALIZABLE_OBJECT_FILTER, "org.apache.geode.cache.lucene.*") + .withAutoStart(); + + private InternalCache cache; + + + + private static final SomeDomain someDomain1 = + SomeDomain.newBuilder() + .withStrField("strField1") + .withIntField(110) + .withLongField(10010L) + .withFloatField(101.25F) + .withDoubleField(101.25D) + .withDateField( + Date.from(LocalDateTime.parse("2001-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + private static final SomeDomain someDomain2 = + SomeDomain.newBuilder() + .withStrField("strField2") + .withIntField(120) + .withLongField(10020L) + .withFloatField(201.25F) + .withDoubleField(201.25D) + .withDateField( + Date.from(LocalDateTime.parse("2002-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + private static final SomeDomain someDomain3 = + SomeDomain.newBuilder() + .withStrField("strField3") + .withIntField(130) + .withLongField(10030L) + .withFloatField(301.25F) + .withDoubleField(301.25D) + .withDateField( + Date.from(LocalDateTime.parse("2003-01-01T00:00:00").toInstant(ZoneOffset.UTC))) + .build(); + + + @Before + public void beforeTest() { + this.cache = serverStarterRule.getCache(); + LuceneService luceneService = LuceneServiceProvider.get(this.cache); + luceneService.createIndexFactory() + .setFields("strField", "intField", "dateField", "longField", "floatField", "doubleField") + .create("idx1", + "/sampleregion"); + + this.cache.<String, SomeDomain>createRegionFactory(RegionShortcut.PARTITION) + .create("sampleregion"); + + Region<String, SomeDomain> region = this.cache.getRegion("/sampleregion"); + + region.put(someDomain1.getStrField(), someDomain1); + region.put(someDomain2.getStrField(), someDomain2); + region.put(someDomain3.getStrField(), someDomain3); + } + + + @Test + public void testByIntegerRange() throws Exception { + + QueryService queryService = this.cache.getQueryService(); + + Query query = queryService.newQuery("select * from /sampleregion"); + SelectResults results = (SelectResults) query.execute(); + assertThat(results).hasSize(3); + + LuceneService luceneService = LuceneServiceProvider.get(cache); + + LuceneQuery<String, SomeDomain> luceneQuery1 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+strField=strField* +intField:[110 TO 120]", + "strField"); + + assertThat(luceneQuery1.findKeys()) + .containsExactlyInAnyOrder("strField1", "strField2"); + + assertThat(luceneQuery1.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain1, someDomain2); + + + LuceneQuery<String, SomeDomain> luceneQuery2 = luceneService.createLuceneQueryFactory() + .create("idx1", "/sampleregion", "+strField=strField* +intField:[120 TO 130]", + "strField"); + + assertThat(luceneQuery2.findKeys()) + .containsExactlyInAnyOrder("strField2", "strField3"); + + assertThat(luceneQuery2.<SomeDomain>findValues()) + .containsExactlyInAnyOrder(someDomain2, someDomain3); + + } + + +}