+++ /dev/null
-package org.apache.lucene.queryParser.standard;
-
-/**
- * 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.
- */
-
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.NumberFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Random;
-import java.util.TimeZone;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.NumericField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.queryParser.core.QueryNodeException;
-import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax;
-import org.apache.lucene.queryParser.standard.config.NumberDateFormat;
-import org.apache.lucene.queryParser.standard.config.NumericConfig;
-import org.apache.lucene.queryParser.standard.parser.EscapeQuerySyntaxImpl;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util._TestUtil;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class TestNumericQueryParser extends LuceneTestCase {
-
- private static enum NumberType {
- NEGATIVE, ZERO, POSITIVE;
- }
-
- final private static int[] DATE_STYLES = {DateFormat.FULL, DateFormat.LONG,
- DateFormat.MEDIUM, DateFormat.SHORT};
-
- final private static int PRECISION_STEP = 8;
- final private static String FIELD_NAME = "field";
- private static Locale LOCALE;
- private static TimeZone TIMEZONE;
- private static Map<String,Number> RANDOM_NUMBER_MAP;
- final private static EscapeQuerySyntax ESCAPER = new EscapeQuerySyntaxImpl();
- final private static String DATE_FIELD_NAME = "date";
- private static int DATE_STYLE;
- private static int TIME_STYLE;
-
- private static Analyzer ANALYZER;
-
- private static NumberFormat NUMBER_FORMAT;
-
- private static StandardQueryParser qp;
-
- private static NumberDateFormat DATE_FORMAT;
-
- private static Directory directory = null;
- private static IndexReader reader = null;
- private static IndexSearcher searcher = null;
-
- private static boolean checkDateFormatSanity(DateFormat dateFormat, long date)
- throws ParseException {
- return date == dateFormat.parse(dateFormat.format(new Date(date)))
- .getTime();
- }
-
- @BeforeClass
- public static void beforeClass() throws Exception {
- ANALYZER = new MockAnalyzer(random);
-
- qp = new StandardQueryParser(ANALYZER);
-
- final HashMap<String,Number> randomNumberMap = new HashMap<String,Number>();
-
- SimpleDateFormat dateFormat;
- long randomDate;
- boolean dateFormatSanityCheckPass;
- int count = 0;
- do {
- if (count > 100) {
- fail("This test has problems to find a sane random DateFormat/NumberFormat. Stopped trying after 100 iterations.");
- }
-
- dateFormatSanityCheckPass = true;
- LOCALE = randomLocale(random);
- TIMEZONE = randomTimeZone(random);
- DATE_STYLE = randomDateStyle(random);
- TIME_STYLE = randomDateStyle(random);
-
- // assumes localized date pattern will have at least year, month, day,
- // hour, minute
- dateFormat = (SimpleDateFormat) DateFormat.getDateTimeInstance(
- DATE_STYLE, TIME_STYLE, LOCALE);
-
- // not all date patterns includes era, full year, timezone and second,
- // so we add them here
- dateFormat.applyPattern(dateFormat.toPattern() + " G s Z yyyy");
- dateFormat.setTimeZone(TIMEZONE);
-
- DATE_FORMAT = new NumberDateFormat(dateFormat);
-
- do {
- randomDate = random.nextLong();
-
- // prune date value so it doesn't pass in insane values to some
- // calendars.
- randomDate = randomDate % 3400000000000l;
-
- // truncate to second
- randomDate = (randomDate / 1000L) * 1000L;
-
- // only positive values
- randomDate = Math.abs(randomDate);
- } while (randomDate == 0L);
-
- dateFormatSanityCheckPass &= checkDateFormatSanity(dateFormat, randomDate);
-
- dateFormatSanityCheckPass &= checkDateFormatSanity(dateFormat, 0);
-
- dateFormatSanityCheckPass &= checkDateFormatSanity(dateFormat,
- -randomDate);
-
- count++;
- } while (!dateFormatSanityCheckPass);
-
- NUMBER_FORMAT = NumberFormat.getNumberInstance(LOCALE);
- NUMBER_FORMAT.setMaximumFractionDigits((random.nextInt() & 20) + 1);
- NUMBER_FORMAT.setMinimumFractionDigits((random.nextInt() & 20) + 1);
- NUMBER_FORMAT.setMaximumIntegerDigits((random.nextInt() & 20) + 1);
- NUMBER_FORMAT.setMinimumIntegerDigits((random.nextInt() & 20) + 1);
-
- double randomDouble;
- long randomLong;
- int randomInt;
- float randomFloat;
-
- while ((randomLong = normalizeNumber(Math.abs(random.nextLong()))
- .longValue()) == 0L)
- ;
- while ((randomDouble = normalizeNumber(Math.abs(random.nextDouble()))
- .doubleValue()) == 0.0)
- ;
- while ((randomFloat = normalizeNumber(Math.abs(random.nextFloat()))
- .floatValue()) == 0.0f)
- ;
- while ((randomInt = normalizeNumber(Math.abs(random.nextInt())).intValue()) == 0)
- ;
-
- randomNumberMap.put(NumericField.DataType.LONG.name(), randomLong);
- randomNumberMap.put(NumericField.DataType.INT.name(), randomInt);
- randomNumberMap.put(NumericField.DataType.FLOAT.name(), randomFloat);
- randomNumberMap.put(NumericField.DataType.DOUBLE.name(), randomDouble);
- randomNumberMap.put(DATE_FIELD_NAME, randomDate);
-
- RANDOM_NUMBER_MAP = Collections.unmodifiableMap(randomNumberMap);
-
- directory = newDirectory();
- RandomIndexWriter writer = new RandomIndexWriter(random, directory,
- newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random))
- .setMaxBufferedDocs(_TestUtil.nextInt(random, 50, 1000))
- .setMergePolicy(newLogMergePolicy()));
-
- Document doc = new Document();
- HashMap<String,NumericConfig> numericConfigMap = new HashMap<String,NumericConfig>();
- HashMap<String,NumericField> numericFieldMap = new HashMap<String,NumericField>();
- qp.setNumericConfigMap(numericConfigMap);
-
- for (NumericField.DataType type : NumericField.DataType.values()) {
- numericConfigMap.put(type.name(), new NumericConfig(PRECISION_STEP,
- NUMBER_FORMAT, type));
-
- NumericField field = new NumericField(type.name(), PRECISION_STEP,
- Field.Store.YES, true);
-
- numericFieldMap.put(type.name(), field);
- doc.add(field);
-
- }
-
- numericConfigMap.put(DATE_FIELD_NAME, new NumericConfig(PRECISION_STEP,
- DATE_FORMAT, NumericField.DataType.LONG));
- NumericField dateField = new NumericField(DATE_FIELD_NAME, PRECISION_STEP,
- Field.Store.YES, true);
- numericFieldMap.put(DATE_FIELD_NAME, dateField);
- doc.add(dateField);
-
- for (NumberType numberType : NumberType.values()) {
- setFieldValues(numberType, numericFieldMap);
- if (VERBOSE) System.out.println("Indexing document: " + doc);
- writer.addDocument(doc);
- }
-
- reader = writer.getReader();
- searcher = newSearcher(reader);
- writer.close();
-
- }
-
- private static Number getNumberType(NumberType numberType, String fieldName) {
-
- if (numberType == null) {
- return null;
- }
-
- switch (numberType) {
-
- case POSITIVE:
- return RANDOM_NUMBER_MAP.get(fieldName);
-
- case NEGATIVE:
- Number number = RANDOM_NUMBER_MAP.get(fieldName);
-
- if (NumericField.DataType.LONG.name().equals(fieldName)
- || DATE_FIELD_NAME.equals(fieldName)) {
- number = -number.longValue();
-
- } else if (NumericField.DataType.DOUBLE.name().equals(fieldName)) {
- number = -number.doubleValue();
-
- } else if (NumericField.DataType.FLOAT.name().equals(fieldName)) {
- number = -number.floatValue();
-
- } else if (NumericField.DataType.INT.name().equals(fieldName)) {
- number = -number.intValue();
-
- } else {
- throw new IllegalArgumentException("field name not found: "
- + fieldName);
- }
-
- return number;
-
- default:
- return 0;
-
- }
-
- }
-
- private static void setFieldValues(NumberType numberType,
- HashMap<String,NumericField> numericFieldMap) {
-
- Number number = getNumberType(numberType, NumericField.DataType.DOUBLE
- .name());
- numericFieldMap.get(NumericField.DataType.DOUBLE.name()).setDoubleValue(
- number.doubleValue());
-
- number = getNumberType(numberType, NumericField.DataType.INT.name());
- numericFieldMap.get(NumericField.DataType.INT.name()).setIntValue(
- number.intValue());
-
- number = getNumberType(numberType, NumericField.DataType.LONG.name());
- numericFieldMap.get(NumericField.DataType.LONG.name()).setLongValue(
- number.longValue());
-
- number = getNumberType(numberType, NumericField.DataType.FLOAT.name());
- numericFieldMap.get(NumericField.DataType.FLOAT.name()).setFloatValue(
- number.floatValue());
-
- number = getNumberType(numberType, DATE_FIELD_NAME);
- numericFieldMap.get(DATE_FIELD_NAME).setLongValue(number.longValue());
-
- }
-
- private static int randomDateStyle(Random random) {
- return DATE_STYLES[random.nextInt(DATE_STYLES.length)];
- }
-
- @Test
- public void testInclusiveNumericRange() throws Exception {
- assertRangeQuery(NumberType.ZERO, NumberType.ZERO, true, true, 1);
- assertRangeQuery(NumberType.ZERO, NumberType.POSITIVE, true, true, 2);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, true, true, 2);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.POSITIVE, true, true, 3);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.NEGATIVE, true, true, 1);
- }
-
-// @Test
-// // test disabled since standard syntax parser does not work with inclusive and
-// // exclusive at the same time
-// public void testInclusiveLowerNumericRange() throws Exception {
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, false, true, 1);
-// assertRangeQuery(NumberType.ZERO, NumberType.POSITIVE, false, true, 1);
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.POSITIVE, false, true, 2);
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.NEGATIVE, false, true, 0);
-// }
-//
-// @Test
-// // test disabled since standard syntax parser does not work with inclusive and
-// // exclusive at the same time
-// public void testInclusiveUpperNumericRange() throws Exception {
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, true, false, 1);
-// assertRangeQuery(NumberType.ZERO, NumberType.POSITIVE, true, false, 1);
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.POSITIVE, true, false, 2);
-// assertRangeQuery(NumberType.NEGATIVE, NumberType.NEGATIVE, true, false, 0);
-// }
-
- @Test
- public void testExclusiveNumericRange() throws Exception {
- assertRangeQuery(NumberType.ZERO, NumberType.ZERO, false, false, 0);
- assertRangeQuery(NumberType.ZERO, NumberType.POSITIVE, false, false, 0);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, false, false, 0);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.POSITIVE, false, false, 1);
- assertRangeQuery(NumberType.NEGATIVE, NumberType.NEGATIVE, false, false, 0);
- }
-
-// @Test
-//// test disabled since standard syntax parser does not work with open range
-// public void testOpenRangeNumericQuery() throws Exception {
-// assertOpenRangeQuery(NumberType.ZERO, "<", 1);
-// assertOpenRangeQuery(NumberType.POSITIVE, "<", 2);
-// assertOpenRangeQuery(NumberType.NEGATIVE, "<", 0);
-//
-// assertOpenRangeQuery(NumberType.ZERO, "<=", 2);
-// assertOpenRangeQuery(NumberType.POSITIVE, "<=", 3);
-// assertOpenRangeQuery(NumberType.NEGATIVE, "<=", 1);
-//
-// assertOpenRangeQuery(NumberType.ZERO, ">", 1);
-// assertOpenRangeQuery(NumberType.POSITIVE, ">", 0);
-// assertOpenRangeQuery(NumberType.NEGATIVE, ">", 2);
-//
-// assertOpenRangeQuery(NumberType.ZERO, ">=", 2);
-// assertOpenRangeQuery(NumberType.POSITIVE, ">=", 1);
-// assertOpenRangeQuery(NumberType.NEGATIVE, ">=", 3);
-//
-// assertOpenRangeQuery(NumberType.NEGATIVE, "=", 1);
-// assertOpenRangeQuery(NumberType.ZERO, "=", 1);
-// assertOpenRangeQuery(NumberType.POSITIVE, "=", 1);
-//
-// assertRangeQuery(NumberType.NEGATIVE, null, true, true, 3);
-// assertRangeQuery(NumberType.NEGATIVE, null, false, true, 2);
-// assertRangeQuery(NumberType.POSITIVE, null, true, false, 1);
-// assertRangeQuery(NumberType.ZERO, null, false, false, 1);
-//
-// assertRangeQuery(null, NumberType.POSITIVE, true, true, 3);
-// assertRangeQuery(null, NumberType.POSITIVE, true, false, 2);
-// assertRangeQuery(null, NumberType.NEGATIVE, false, true, 1);
-// assertRangeQuery(null, NumberType.ZERO, false, false, 1);
-//
-// assertRangeQuery(null, null, false, false, 3);
-// assertRangeQuery(null, null, true, true, 3);
-//
-// }
-
- @Test
- public void testSimpleNumericQuery() throws Exception {
- assertSimpleQuery(NumberType.ZERO, 1);
- assertSimpleQuery(NumberType.POSITIVE, 1);
- assertSimpleQuery(NumberType.NEGATIVE, 1);
- }
-
- public void assertRangeQuery(NumberType lowerType, NumberType upperType,
- boolean lowerInclusive, boolean upperInclusive, int expectedDocCount)
- throws QueryNodeException, IOException {
-
- StringBuilder sb = new StringBuilder();
-
- String lowerInclusiveStr = (lowerInclusive ? "[" : "{");
- String upperInclusiveStr = (upperInclusive ? "]" : "}");
-
- for (NumericField.DataType type : NumericField.DataType.values()) {
- String lowerStr = numberToString(getNumberType(lowerType, type.name()));
- String upperStr = numberToString(getNumberType(upperType, type.name()));
-
- sb.append("+").append(type.name()).append(':').append(lowerInclusiveStr)
- .append('"').append(lowerStr).append("\" TO \"").append(upperStr)
- .append('"').append(upperInclusiveStr).append(' ');
- }
-
- Number lowerDateNumber = getNumberType(lowerType, DATE_FIELD_NAME);
- Number upperDateNumber = getNumberType(upperType, DATE_FIELD_NAME);
- String lowerDateStr;
- String upperDateStr;
-
- if (lowerDateNumber != null) {
- lowerDateStr = ESCAPER.escape(
- DATE_FORMAT.format(new Date(lowerDateNumber.longValue())), LOCALE,
- EscapeQuerySyntax.Type.STRING).toString();
-
- } else {
- lowerDateStr = "*";
- }
-
- if (upperDateNumber != null) {
- upperDateStr = ESCAPER.escape(
- DATE_FORMAT.format(new Date(upperDateNumber.longValue())), LOCALE,
- EscapeQuerySyntax.Type.STRING).toString();
-
- } else {
- upperDateStr = "*";
- }
-
- sb.append("+").append(DATE_FIELD_NAME).append(':')
- .append(lowerInclusiveStr).append('"').append(lowerDateStr).append(
- "\" TO \"").append(upperDateStr).append('"').append(
- upperInclusiveStr);
-
- testQuery(sb.toString(), expectedDocCount);
-
- }
-
- public void assertOpenRangeQuery(NumberType boundType, String operator, int expectedDocCount)
- throws QueryNodeException, IOException {
-
- StringBuilder sb = new StringBuilder();
-
- for (NumericField.DataType type : NumericField.DataType.values()) {
- String boundStr = numberToString(getNumberType(boundType, type.name()));
-
- sb.append("+").append(type.name()).append(operator).append('"').append(boundStr).append('"').append(' ');
- }
-
- String boundDateStr = ESCAPER.escape(
- DATE_FORMAT.format(new Date(getNumberType(boundType, DATE_FIELD_NAME)
- .longValue())), LOCALE, EscapeQuerySyntax.Type.STRING).toString();
-
- sb.append("+").append(DATE_FIELD_NAME).append(operator).append('"').append(boundDateStr).append('"');
-
- testQuery(sb.toString(), expectedDocCount);
- }
-
- public void assertSimpleQuery(NumberType numberType, int expectedDocCount)
- throws QueryNodeException, IOException {
- StringBuilder sb = new StringBuilder();
-
- for (NumericField.DataType type : NumericField.DataType.values()) {
- String numberStr = numberToString(getNumberType(numberType, type.name()));
- sb.append('+').append(type.name()).append(":\"").append(numberStr)
- .append("\" ");
- }
-
- String dateStr = ESCAPER.escape(
- DATE_FORMAT.format(new Date(getNumberType(numberType, DATE_FIELD_NAME)
- .longValue())), LOCALE, EscapeQuerySyntax.Type.STRING).toString();
-
- sb.append('+').append(DATE_FIELD_NAME).append(":\"").append(dateStr)
- .append('"');
-
- testQuery(sb.toString(), expectedDocCount);
-
- }
-
- private void testQuery(String queryStr, int expectedDocCount)
- throws QueryNodeException, IOException {
- if (VERBOSE) System.out.println("Parsing: " + queryStr);
-
- Query query = qp.parse(queryStr, FIELD_NAME);
- if (VERBOSE) System.out.println("Querying: " + query);
- TopDocs topDocs = searcher.search(query, 1000);
-
- String msg = "Query <" + queryStr + "> retrieved " + topDocs.totalHits
- + " document(s), " + expectedDocCount + " document(s) expected.";
-
- if (VERBOSE) System.out.println(msg);
-
- assertEquals(msg, expectedDocCount, topDocs.totalHits);
- }
-
- private static String numberToString(Number number) {
- return number == null ? "*" : ESCAPER.escape(NUMBER_FORMAT.format(number),
- LOCALE, EscapeQuerySyntax.Type.STRING).toString();
- }
-
- private static Number normalizeNumber(Number number) throws ParseException {
- return NUMBER_FORMAT.parse(NUMBER_FORMAT.format(number));
- }
-
- @AfterClass
- public static void afterClass() throws Exception {
- searcher.close();
- searcher = null;
- reader.close();
- reader = null;
- directory.close();
- directory = null;
- }
-
-}