Property changes on: . ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk:r1145885,1146984,1150671,1152892 Property changes on: lucene ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk/lucene:r1145885,1146984,1150671,1152892 Property changes on: lucene\backwards ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk/lucene/backwards:r1145885,1146984,1150671,1152892 Property changes on: lucene\backwards\src\test ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk/lucene/backwards/src/test:r1145885,1146984,1150671,1152892 Property changes on: lucene\backwards\src\test-framework ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk/lucene/backwards/src/test-framework:r1145885,1146984,1150671,1152892 Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/messages/QueryParserMessages.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/messages/QueryParserMessages.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/messages/QueryParserMessages.java (working copy) @@ -50,5 +50,8 @@ public static String WILDCARD_NOT_SUPPORTED; public static String TOO_MANY_BOOLEAN_CLAUSES; public static String LEADING_WILDCARD_NOT_ALLOWED; + public static String COULD_NOT_PARSE_NUMBER; + public static String NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY; + public static String UNSUPPORTED_NUMERIC_DATA_TYPE; } Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldQueryNode.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldQueryNode.java (working copy) @@ -25,8 +25,7 @@ /** * A {@link FieldQueryNode} represents a element that contains field/text tuple */ -public class FieldQueryNode extends QueryNodeImpl implements TextableQueryNode, - FieldableNode { +public class FieldQueryNode extends QueryNodeImpl implements FieldValuePairQueryNode, TextableQueryNode { private static final long serialVersionUID = 3634521145130758265L; @@ -182,4 +181,12 @@ } + public CharSequence getValue() { + return getText(); + } + + public void setValue(CharSequence value) { + setText(value); + } + } Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldValuePairQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldValuePairQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/FieldValuePairQueryNode.java (revision 0) @@ -0,0 +1,30 @@ +package org.apache.lucene.queryParser.core.nodes; + +/** + * 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. + */ + +/** + * This interface should be implemented by {@link QueryNode} that holds a field + * and an arbitrary value. + * + * @see FieldableNode + * @see ValueQueryNode + */ +public interface FieldValuePairQueryNode extends + FieldableNode, ValueQueryNode { + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\core\nodes\FieldValuePairQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/package.html =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/package.html (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/package.html (working copy) @@ -61,6 +61,7 @@
  • FuzzyQueryNode - fuzzy node
  • ParametricRangeQueryNode - used for parametric field:[low_value TO high_value]
  • ProximityQueryNode - used for proximity search
  • +
  • NumericRangeQueryNode - used for numeric range search
  • TokenizedPhraseQueryNode - used by tokenizers/lemmatizers/analyzers for phrases/autophrases
  • @@ -68,6 +69,7 @@ Leaf Nodes:
    • FieldQueryNode - field/value node
    • +
    • NumericQueryNode - used for numeric search
    • PathQueryNode - {@link org.apache.lucene.queryParser.core.nodes.QueryNode} object used with path-like queries
    • OpaqueQueryNode - Used as for part of the query that can be parsed by other parsers. schema/value
    • ParametricQueryNode - used for parametric field [>=|<=|=|<|>] value
    • Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricQueryNode.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricQueryNode.java (working copy) @@ -22,7 +22,11 @@ /** * A {@link ParametricQueryNode} represents LE, LT, GE, GT, EQ, NE query. * Example: date >= "2009-10-10" OR price = 200 + * + * @deprecated this class will be removed in future. {@link FieldQueryNode} + * should be used instead. */ +@Deprecated public class ParametricQueryNode extends FieldQueryNode { private static final long serialVersionUID = -5770038129741218116L; Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricRangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricRangeQueryNode.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ParametricRangeQueryNode.java (working copy) @@ -27,7 +27,7 @@ * Example: date >= "2009-10-10" OR price = 200 */ public class ParametricRangeQueryNode extends QueryNodeImpl implements - FieldableNode { + RangeQueryNode { private static final long serialVersionUID = 7120958816535573935L; @@ -118,4 +118,12 @@ } + public boolean isLowerInclusive() { + return getUpperBound().getOperator() == CompareOperator.GE; + } + + public boolean isUpperInclusive() { + return getLowerBound().getOperator() == CompareOperator.LE; + } + } Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/RangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/RangeQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/RangeQueryNode.java (revision 0) @@ -0,0 +1,36 @@ +package org.apache.lucene.queryParser.core.nodes; + +/** + * 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. + */ + + /** + * This interface should be implemented by a {@link QueryNode} that represents + * some kind of range query. + * + */ +public interface RangeQueryNode> extends + FieldableNode { + + T getLowerBound(); + + T getUpperBound(); + + boolean isLowerInclusive(); + + boolean isUpperInclusive(); + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\core\nodes\RangeQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ValueQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ValueQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/nodes/ValueQueryNode.java (revision 0) @@ -0,0 +1,30 @@ +package org.apache.lucene.queryParser.core.nodes; + +/** + * 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. + */ + +/** + * This interface should be implemented by a {@link QueryNode} that holds an + * arbitrary value. + */ +public interface ValueQueryNode extends QueryNode { + + public void setValue(T value); + + public T getValue(); + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\core\nodes\ValueQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/DummyQueryNodeBuilder.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/DummyQueryNodeBuilder.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/DummyQueryNodeBuilder.java (revision 0) @@ -0,0 +1,46 @@ +package org.apache.lucene.queryParser.standard.builders; + +/** + * 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 org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.search.TermQuery; + +/** + * This builder does nothing. Commonly used for {@link QueryNode} objects that + * are built by its parent's builder. + */ +public class DummyQueryNodeBuilder implements StandardQueryBuilder { + + /** + * Constructs a {@link DummyQueryNodeBuilder} object. + */ + public DummyQueryNodeBuilder() { + // empty constructor + } + + /** + * Always return null. + * + * return null + */ + public TermQuery build(QueryNode queryNode) throws QueryNodeException { + return null; + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\builders\DummyQueryNodeBuilder.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/NumericRangeQueryNodeBuilder.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/NumericRangeQueryNodeBuilder.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/NumericRangeQueryNodeBuilder.java (revision 0) @@ -0,0 +1,102 @@ +package org.apache.lucene.queryParser.standard.builders; + +/** + * 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 org.apache.lucene.document.NumericField; +import org.apache.lucene.messages.MessageImpl; +import org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.messages.QueryParserMessages; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.util.StringUtils; +import org.apache.lucene.queryParser.standard.config.NumericConfig; +import org.apache.lucene.queryParser.standard.nodes.NumericQueryNode; +import org.apache.lucene.queryParser.standard.nodes.NumericRangeQueryNode; +import org.apache.lucene.search.NumericRangeQuery; + +/** + * Builds {@link NumericRangeQuery}s out of {@link NumericRangeQueryNode}s. + * + * @see NumericRangeQuery + * @see NumericRangeQueryNode + */ +public class NumericRangeQueryNodeBuilder implements StandardQueryBuilder { + + /** + * Constructs a {@link NumericRangeQueryNodeBuilder} object. + */ + public NumericRangeQueryNodeBuilder() { + // empty constructor + } + + public NumericRangeQuery build(QueryNode queryNode) + throws QueryNodeException { + NumericRangeQueryNode numericRangeNode = (NumericRangeQueryNode) queryNode; + + NumericQueryNode lowerNumericNode = numericRangeNode.getLowerBound(); + NumericQueryNode upperNumericNode = numericRangeNode.getUpperBound(); + + Number lowerNumber, upperNumber; + + if (lowerNumericNode != null) { + lowerNumber = lowerNumericNode.getValue(); + } else { + lowerNumber = null; + } + + if (upperNumericNode != null) { + upperNumber = upperNumericNode.getValue(); + } else { + upperNumber = null; + } + + NumericConfig numericConfig = numericRangeNode.getNumericConfig(); + NumericField.DataType numberType = numericConfig.getType(); + String field = StringUtils.toString(numericRangeNode.getField()); + boolean minInclusive = numericRangeNode.isLowerInclusive(); + boolean maxInclusive = numericRangeNode.isUpperInclusive(); + int precisionStep = numericConfig.getPrecisionStep(); + + switch (numberType) { + + case LONG: + return NumericRangeQuery.newLongRange(field, precisionStep, + (Long) lowerNumber, (Long) upperNumber, minInclusive, maxInclusive); + + case INT: + return NumericRangeQuery.newIntRange(field, precisionStep, + (Integer) lowerNumber, (Integer) upperNumber, minInclusive, + maxInclusive); + + case FLOAT: + return NumericRangeQuery.newFloatRange(field, precisionStep, + (Float) lowerNumber, (Float) upperNumber, minInclusive, + maxInclusive); + + case DOUBLE: + return NumericRangeQuery.newDoubleRange(field, precisionStep, + (Double) lowerNumber, (Double) upperNumber, minInclusive, + maxInclusive); + + default : + throw new QueryNodeException(new MessageImpl( + QueryParserMessages.UNSUPPORTED_NUMERIC_DATA_TYPE, numberType)); + + } + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\builders\NumericRangeQueryNodeBuilder.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/RangeQueryNodeBuilder.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/RangeQueryNodeBuilder.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/RangeQueryNodeBuilder.java (working copy) @@ -67,5 +67,7 @@ return rangeQuery; } + + } Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/StandardQueryTreeBuilder.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/StandardQueryTreeBuilder.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/StandardQueryTreeBuilder.java (working copy) @@ -31,9 +31,12 @@ import org.apache.lucene.queryParser.core.nodes.SlopQueryNode; import org.apache.lucene.queryParser.core.nodes.TokenizedPhraseQueryNode; import org.apache.lucene.queryParser.standard.nodes.MultiPhraseQueryNode; +import org.apache.lucene.queryParser.standard.nodes.NumericQueryNode; +import org.apache.lucene.queryParser.standard.nodes.NumericRangeQueryNode; import org.apache.lucene.queryParser.standard.nodes.PrefixWildcardQueryNode; import org.apache.lucene.queryParser.standard.nodes.RangeQueryNode; import org.apache.lucene.queryParser.standard.nodes.StandardBooleanQueryNode; +import org.apache.lucene.queryParser.standard.nodes.TermRangeQueryNode; import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode; import org.apache.lucene.queryParser.standard.processors.StandardQueryNodeProcessorPipeline; import org.apache.lucene.search.Query; @@ -49,12 +52,14 @@ */ public class StandardQueryTreeBuilder extends QueryTreeBuilder implements StandardQueryBuilder { - + public StandardQueryTreeBuilder() { setBuilder(GroupQueryNode.class, new GroupQueryNodeBuilder()); setBuilder(FieldQueryNode.class, new FieldQueryNodeBuilder()); setBuilder(BooleanQueryNode.class, new BooleanQueryNodeBuilder()); setBuilder(FuzzyQueryNode.class, new FuzzyQueryNodeBuilder()); + setBuilder(NumericQueryNode.class, new DummyQueryNodeBuilder()); + setBuilder(NumericRangeQueryNode.class, new NumericRangeQueryNodeBuilder()); setBuilder(BoostQueryNode.class, new BoostQueryNodeBuilder()); setBuilder(ModifierQueryNode.class, new ModifierQueryNodeBuilder()); setBuilder(WildcardQueryNode.class, new WildcardQueryNodeBuilder()); @@ -63,6 +68,7 @@ setBuilder(PrefixWildcardQueryNode.class, new PrefixWildcardQueryNodeBuilder()); setBuilder(RangeQueryNode.class, new RangeQueryNodeBuilder()); + setBuilder(TermRangeQueryNode.class, new TermRangeQueryNodeBuilder()); setBuilder(SlopQueryNode.class, new SlopQueryNodeBuilder()); setBuilder(StandardBooleanQueryNode.class, new StandardBooleanQueryNodeBuilder()); Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/TermRangeQueryNodeBuilder.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/TermRangeQueryNodeBuilder.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/builders/TermRangeQueryNodeBuilder.java (revision 0) @@ -0,0 +1,60 @@ +package org.apache.lucene.queryParser.standard.builders; + +/** + * 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 org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.nodes.FieldQueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.util.StringUtils; +import org.apache.lucene.queryParser.standard.nodes.TermRangeQueryNode; +import org.apache.lucene.queryParser.standard.processors.MultiTermRewriteMethodProcessor; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.TermRangeQuery; + +/** + * Builds a {@link TermRangeQuery} object from a {@link TermRangeQueryNode} + * object. + */ +public class TermRangeQueryNodeBuilder implements StandardQueryBuilder { + + public TermRangeQueryNodeBuilder() { + // empty constructor + } + + public TermRangeQuery build(QueryNode queryNode) throws QueryNodeException { + TermRangeQueryNode rangeNode = (TermRangeQueryNode) queryNode; + FieldQueryNode upper = rangeNode.getUpperBound(); + FieldQueryNode lower = rangeNode.getLowerBound(); + + String field = StringUtils.toString(rangeNode.getField()); + + TermRangeQuery rangeQuery = new TermRangeQuery(field, lower + .getTextAsString(), upper.getTextAsString(), rangeNode + .isLowerInclusive(), rangeNode.isUpperInclusive()); + + MultiTermQuery.RewriteMethod method = (MultiTermQuery.RewriteMethod) queryNode + .getTag(MultiTermRewriteMethodProcessor.TAG_ID); + if (method != null) { + rangeQuery.setRewriteMethod(method); + } + + return rangeQuery; + + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\builders\TermRangeQueryNodeBuilder.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumberDateFormat.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumberDateFormat.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumberDateFormat.java (revision 0) @@ -0,0 +1,71 @@ +package org.apache.lucene.queryParser.standard.config; + +/** + * 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.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Date; + +/** + * This {@link Format} parses {@link Long} into date strings and vice-versa. It + * uses the given {@link DateFormat} to parse and format dates, but before, it + * converts {@link Long} to {@link Date} objects or vice-versa. + */ +public class NumberDateFormat extends NumberFormat { + + private static final long serialVersionUID = 964823936071308283L; + + final private DateFormat dateFormat; + + /** + * Constructs a {@link NumberDateFormat} object using the given {@link DateFormat}. + * + * @param dateFormat {@link DateFormat} used to parse and format dates + */ + public NumberDateFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public StringBuffer format(double number, StringBuffer toAppendTo, + FieldPosition pos) { + return dateFormat.format(new Date((long) number), toAppendTo, pos); + } + + @Override + public StringBuffer format(long number, StringBuffer toAppendTo, + FieldPosition pos) { + return dateFormat.format(new Date(number), toAppendTo, pos); + } + + @Override + public Number parse(String source, ParsePosition parsePosition) { + final Date date = dateFormat.parse(source, parsePosition); + return (date == null) ? null : date.getTime(); + } + + @Override + public StringBuffer format(Object number, StringBuffer toAppendTo, + FieldPosition pos) { + return dateFormat.format(number, toAppendTo, pos); + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\config\NumberDateFormat.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericConfig.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericConfig.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericConfig.java (revision 0) @@ -0,0 +1,159 @@ +package org.apache.lucene.queryParser.standard.config; + +/** + * 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.text.NumberFormat; + +import org.apache.lucene.document.NumericField; +import org.apache.lucene.search.NumericRangeQuery; + +/** + * This class holds the configuration used to parse numeric queries and create + * {@link NumericRangeQuery}s. + * + * @see NumericRangeQuery + * @see NumberFormat + */ +public class NumericConfig { + + private int precisionStep; + + private NumberFormat format; + + private NumericField.DataType type; + + /** + * Constructs a {@link NumericConfig} object. + * + * @param precisionStep + * the precision used to index the numeric values + * @param format + * the {@link NumberFormat} used to parse a {@link String} to + * {@link Number} + * @param type + * the numeric type used to index the numeric values + * + * @see NumericConfig#setPrecisionStep(int) + * @see NumericConfig#setNumberFormat(NumberFormat) + * @see #setType(org.apache.lucene.document.NumericField.DataType) + */ + public NumericConfig(int precisionStep, NumberFormat format, + NumericField.DataType type) { + setPrecisionStep(precisionStep); + setNumberFormat(format); + setType(type); + + } + + /** + * Returns the precision used to index the numeric values + * + * @return the precision used to index the numeric values + * + * @see NumericRangeQuery#getPrecisionStep() + */ + public int getPrecisionStep() { + return precisionStep; + } + + /** + * Sets the precision used to index the numeric values + * + * @param precisionStep + * the precision used to index the numeric values + * + * @see NumericRangeQuery#getPrecisionStep() + */ + public void setPrecisionStep(int precisionStep) { + this.precisionStep = precisionStep; + } + + /** + * Returns the {@link NumberFormat} used to parse a {@link String} to + * {@link Number} + * + * @return the {@link NumberFormat} used to parse a {@link String} to + * {@link Number} + */ + public NumberFormat getNumberFormat() { + return format; + } + + /** + * Returns the numeric type used to index the numeric values + * + * @return the numeric type used to index the numeric values + */ + public NumericField.DataType getType() { + return type; + } + + /** + * Sets the numeric type used to index the numeric values + * + * @param type the numeric type used to index the numeric values + */ + public void setType(NumericField.DataType type) { + + if (type == null) { + throw new IllegalArgumentException("type cannot be null!"); + } + + this.type = type; + + } + + /** + * Sets the {@link NumberFormat} used to parse a {@link String} to + * {@link Number} + * + * @param format + * the {@link NumberFormat} used to parse a {@link String} to + * {@link Number}, cannot be null + */ + public void setNumberFormat(NumberFormat format) { + + if (format == null) { + throw new IllegalArgumentException("format cannot be null!"); + } + + this.format = format; + + } + + @Override + public boolean equals(Object obj) { + + if (obj == this) return true; + + if (obj instanceof NumericConfig) { + NumericConfig other = (NumericConfig) obj; + + if (this.precisionStep == other.precisionStep + && this.type == other.type + && (this.format == other.format || (this.format.equals(other.format)))) { + return true; + } + + } + + return false; + + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\config\NumericConfig.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericFieldConfigListener.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericFieldConfigListener.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/NumericFieldConfigListener.java (revision 0) @@ -0,0 +1,73 @@ +package org.apache.lucene.queryParser.standard.config; + +/** + * 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.util.Map; + +import org.apache.lucene.queryParser.core.config.FieldConfig; +import org.apache.lucene.queryParser.core.config.FieldConfigListener; +import org.apache.lucene.queryParser.core.config.QueryConfigHandler; +import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; + +/** + * This listener is used to listen to {@link FieldConfig} requests in + * {@link QueryConfigHandler} and add {@link ConfigurationKeys#NUMERIC_CONFIG} + * based on the {@link ConfigurationKeys#NUMERIC_CONFIG_MAP} set in the + * {@link QueryConfigHandler}. + * + * @see NumericConfig + * @see QueryConfigHandler + * @see ConfigurationKeys#NUMERIC_CONFIG + * @see ConfigurationKeys#NUMERIC_CONFIG_MAP + */ +public class NumericFieldConfigListener implements FieldConfigListener { + + final private QueryConfigHandler config; + + /** + * Construcs a {@link NumericFieldConfigListener} object using the given {@link QueryConfigHandler}. + * + * @param config the {@link QueryConfigHandler} it will listen too + */ + public NumericFieldConfigListener(QueryConfigHandler config) { + + if (config == null) { + throw new IllegalArgumentException("config cannot be null!"); + } + + this.config = config; + + } + + public void buildFieldConfig(FieldConfig fieldConfig) { + Map numericConfigMap = config + .get(ConfigurationKeys.NUMERIC_CONFIG_MAP); + + if (numericConfigMap != null) { + NumericConfig numericConfig = numericConfigMap + .get(fieldConfig.getField()); + + if (numericConfig != null) { + fieldConfig.set(ConfigurationKeys.NUMERIC_CONFIG, numericConfig); + } + + } + + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\config\NumericFieldConfigListener.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/StandardQueryConfigHandler.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/StandardQueryConfigHandler.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/config/StandardQueryConfigHandler.java (working copy) @@ -177,6 +177,26 @@ */ final public static ConfigurationKey BOOST = ConfigurationKey .newInstance(); + + /** + * Key used to set a field to its {@link NumericConfig}. + * + * @see StandardQueryParser#setNumericConfigMap(Map) + * @see StandardQueryParser#getNumericConfigMap() + */ + final public static ConfigurationKey NUMERIC_CONFIG = ConfigurationKey + .newInstance(); + + /** + * Key used to set the {@link NumericConfig} in {@link FieldConfig} for + * numeric fields. + * + * @see StandardQueryParser#setNumericConfigMap(Map) + * @see StandardQueryParser#getNumericConfigMap() + */ + final public static ConfigurationKey> NUMERIC_CONFIG_MAP = ConfigurationKey + .newInstance(); + /** * Key used to set the {@link Collator} used when creating {@link TermRangeQuery}s. @@ -198,6 +218,7 @@ // Add listener that will build the FieldConfig. addFieldConfigListener(new FieldBoostMapFCListener(this)); addFieldConfigListener(new FieldDateResolutionFCListener(this)); + addFieldConfigListener(new NumericFieldConfigListener(this)); // Default Values set(ConfigurationKeys.ALLOW_LEADING_WILDCARD, false); // default in 2.9 Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/AbstractRangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/AbstractRangeQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/AbstractRangeQueryNode.java (revision 0) @@ -0,0 +1,210 @@ +package org.apache.lucene.queryParser.standard.nodes; + +/** + * 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.util.ArrayList; + +import org.apache.lucene.queryParser.core.nodes.FieldValuePairQueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNodeImpl; +import org.apache.lucene.queryParser.core.nodes.RangeQueryNode; +import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax; +import org.apache.lucene.queryParser.core.util.StringUtils; + +/** + * This class should be extended by nodes intending to represent range queries. + * + * @param + * the type of the range query bounds (lower and upper) + */ +public abstract class AbstractRangeQueryNode> + extends QueryNodeImpl implements RangeQueryNode> { + + private static final long serialVersionUID = 4475492120315147792L; + + private boolean lowerInclusive, upperInclusive; + + /** + * Constructs an {@link AbstractRangeQueryNode}, it should be invoked only by + * its extenders. + */ + protected AbstractRangeQueryNode() { + setLeaf(false); + allocate(); + } + + /** + * Returns the field associated with this node. + * + * @return the field associated with this node + * + * @see FieldableNode + */ + public CharSequence getField() { + CharSequence field = null; + T lower = getLowerBound(); + T upper = getUpperBound(); + + if (lower != null) { + field = lower.getField(); + + } else if (upper != null) { + field = upper.getField(); + } + + return field; + + } + + /** + * Sets the field associated with this node. + * + * @param fieldName the field associated with this node + */ + public void setField(CharSequence fieldName) { + T lower = getLowerBound(); + T upper = getUpperBound(); + + if (lower != null) { + lower.setField(fieldName); + } + + if (upper != null) { + upper.setField(fieldName); + } + + } + + /** + * Returns the lower bound node. + * + * @return the lower bound node. + */ + @SuppressWarnings("unchecked") + public T getLowerBound() { + return (T) getChildren().get(0); + } + + /** + * Returns the upper bound node. + * + * @return the upper bound node. + */ + @SuppressWarnings("unchecked") + public T getUpperBound() { + return (T) getChildren().get(1); + } + + /** + * Returns whether the lower bound is inclusive or exclusive. + * + * @return true if the lower bound is inclusive, otherwise, false + */ + public boolean isLowerInclusive() { + return lowerInclusive; + } + + /** + * Returns whether the upper bound is inclusive or exclusive. + * + * @return true if the upper bound is inclusive, otherwise, false + */ + public boolean isUpperInclusive() { + return upperInclusive; + } + + /** + * Sets the lower and upper bounds. + * + * @param lower the lower bound, null if lower bound is open + * @param upper the upper bound, null if upper bound is open + * @param lowerInclusive true if the lower bound is inclusive, otherwise, false + * @param upperInclusive true if the upper bound is inclusive, otherwise, false + * + * @see #getLowerBound() + * @see #getUpperBound() + * @see #isLowerInclusive() + * @see #isUpperInclusive() + */ + public void setBounds(T lower, T upper, boolean lowerInclusive, + boolean upperInclusive) { + + if (lower != null && upper != null) { + String lowerField = StringUtils.toString(lower.getField()); + String upperField = StringUtils.toString(upper.getField()); + + if ((upperField == null && lowerField == null) + || (upperField != null && !upperField.equals(lowerField))) { + throw new IllegalArgumentException( + "lower and upper bounds should have the same field name!"); + } + + this.lowerInclusive = lowerInclusive; + this.upperInclusive = upperInclusive; + + ArrayList children = new ArrayList(2); + children.add(lower); + children.add(upper); + + set(children); + + } + + } + + public CharSequence toQueryString(EscapeQuerySyntax escapeSyntaxParser) { + StringBuilder sb = new StringBuilder(); + + T lower = getLowerBound(); + T upper = getUpperBound(); + + if (lowerInclusive) { + sb.append('['); + + } else { + sb.append('{'); + } + + if (lower != null) { + sb.append(lower.toQueryString(escapeSyntaxParser)); + + } else { + sb.append("..."); + } + + sb.append(' '); + + if (upper != null) { + sb.append(upper.toQueryString(escapeSyntaxParser)); + + } else { + sb.append("..."); + } + + if (upperInclusive) { + sb.append(']'); + + } else { + sb.append('}'); + } + + return sb.toString(); + + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\nodes\AbstractRangeQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericQueryNode.java (revision 0) @@ -0,0 +1,147 @@ +package org.apache.lucene.queryParser.standard.nodes; + +/** + * 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.text.NumberFormat; +import java.util.Locale; + +import org.apache.lucene.queryParser.core.nodes.FieldValuePairQueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNodeImpl; +import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax; +import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax.Type; + +/** + * This query node represents a field query that holds a numeric value. It is + * similar to {@link FieldQueryNode}, however the {@link #getValue()} returns a + * {@link Number}. + * + * @see NumericConfig + */ +public class NumericQueryNode extends QueryNodeImpl implements + FieldValuePairQueryNode { + + private static final long serialVersionUID = -1969102979874574778L; + + private NumberFormat numberFormat; + + private CharSequence field; + + private Number value; + + /** + * Creates a {@link NumericQueryNode} object using the given field, + * {@link Number} value and {@link NumberFormat} used to convert the value to + * {@link String}. + * + * @param field the field associated with this query node + * @param value the value hold by this node + * @param numberFormat the {@link NumberFormat} used to convert the value to {@link String} + */ + public NumericQueryNode(CharSequence field, Number value, + NumberFormat numberFormat) { + + super(); + + setNumberFormat(numberFormat); + setField(field); + setValue(value); + + } + + /** + * Returns the field associated with this node. + * + * @return the field associated with this node + */ + public CharSequence getField() { + return this.field; + } + + /** + * Sets the field associated with this node. + * + * @param fieldName the field associated with this node + */ + public void setField(CharSequence fieldName) { + this.field = fieldName; + } + + /** + * This method is used to get the value converted to {@link String} and + * escaped using the given {@link EscapeQuerySyntax}. + * + * @param escaper the {@link EscapeQuerySyntax} used to escape the value {@link String} + * + * @return the value converte to {@link String} and escaped + */ + protected CharSequence getTermEscaped(EscapeQuerySyntax escaper) { + return escaper.escape(NumberFormat.getNumberInstance().format(this.value), + Locale.ENGLISH, Type.NORMAL); + } + + public CharSequence toQueryString(EscapeQuerySyntax escapeSyntaxParser) { + if (isDefaultField(this.field)) { + return getTermEscaped(escapeSyntaxParser); + } else { + return this.field + ":" + getTermEscaped(escapeSyntaxParser); + } + } + + /** + * Sets the {@link NumberFormat} used to convert the value to {@link String}. + * + * @param format the {@link NumberFormat} used to convert the value to {@link String} + */ + public void setNumberFormat(NumberFormat format) { + this.numberFormat = format; + } + + /** + * Returns the {@link NumberFormat} used to convert the value to {@link String}. + * + * @return the {@link NumberFormat} used to convert the value to {@link String} + */ + public NumberFormat getNumberFormat() { + return this.numberFormat; + } + + /** + * Returns the numeric value as {@link Number}. + * + * @return the numeric value + */ + public Number getValue() { + return value; + } + + /** + * Sets the numeric value. + * + * @param value the numeric value + */ + public void setValue(Number value) { + this.value = value; + } + + @Override + public String toString() { + return ""; + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\nodes\NumericQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericRangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericRangeQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/NumericRangeQueryNode.java (revision 0) @@ -0,0 +1,151 @@ +package org.apache.lucene.queryParser.standard.nodes; + +import org.apache.lucene.document.NumericField; +import org.apache.lucene.messages.MessageImpl; +import org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.messages.QueryParserMessages; +import org.apache.lucene.queryParser.standard.config.NumericConfig; + +/** + * 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. + */ + +/** + * This query node represents a range query composed by {@link NumericQueryNode} + * bounds, which means the bound values are {@link Number}s. + * + * @see NumericQueryNode + * @see AbstractRangeQueryNode + */ +public class NumericRangeQueryNode extends + AbstractRangeQueryNode { + + public NumericConfig numericConfig; + + /** + * Constructs a {@link NumericRangeQueryNode} object using the given + * {@link NumericQueryNode} as its bounds and {@link NumericConfig}. + * + * @param lower the lower bound + * @param upper the upper bound + * @param lowerInclusive true if the lower bound is inclusive, otherwise, false + * @param upperInclusive true if the upper bound is inclusive, otherwise, false + * @param numericConfig the {@link NumericConfig} that represents associated with the upper and lower bounds + * + * @see #setBounds(NumericQueryNode, NumericQueryNode, boolean, boolean, NumericConfig) + */ + public NumericRangeQueryNode(NumericQueryNode lower, NumericQueryNode upper, + boolean lowerInclusive, boolean upperInclusive, NumericConfig numericConfig) throws QueryNodeException { + setBounds(lower, upper, lowerInclusive, upperInclusive, numericConfig); + } + + private static NumericField.DataType getNumericDataType(Number number) throws QueryNodeException { + + if (number instanceof Long) { + return NumericField.DataType.LONG; + } else if (number instanceof Integer) { + return NumericField.DataType.INT; + } else if (number instanceof Double) { + return NumericField.DataType.DOUBLE; + } else if (number instanceof Float) { + return NumericField.DataType.FLOAT; + } else { + throw new QueryNodeException( + new MessageImpl( + QueryParserMessages.NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY, + number.getClass())); + } + + } + + /** + * Sets the upper and lower bounds of this range query node and the + * {@link NumericConfig} associated with these bounds. + * + * @param lower the lower bound + * @param upper the upper bound + * @param lowerInclusive true if the lower bound is inclusive, otherwise, false + * @param upperInclusive true if the upper bound is inclusive, otherwise, false + * @param numericConfig the {@link NumericConfig} that represents associated with the upper and lower bounds + * + */ + public void setBounds(NumericQueryNode lower, NumericQueryNode upper, + boolean lowerInclusive, boolean upperInclusive, NumericConfig numericConfig) throws QueryNodeException { + + if (numericConfig == null) { + throw new IllegalArgumentException("numericConfig cannot be null!"); + } + + NumericField.DataType lowerNumberType, upperNumberType; + + if (lower != null && lower.getValue() != null) { + lowerNumberType = getNumericDataType(lower.getValue()); + } else { + lowerNumberType = null; + } + + if (upper != null && upper.getValue() != null) { + upperNumberType = getNumericDataType(upper.getValue()); + } else { + upperNumberType = null; + } + + if (lowerNumberType != null + && !lowerNumberType.equals(numericConfig.getType())) { + throw new IllegalArgumentException( + "lower value's type should be the same as numericConfig type: " + + lowerNumberType + " != " + numericConfig.getType()); + } + + if (upperNumberType != null + && !upperNumberType.equals(numericConfig.getType())) { + throw new IllegalArgumentException( + "upper value's type should be the same as numericConfig type: " + + upperNumberType + " != " + numericConfig.getType()); + } + + super.setBounds(lower, upper, lowerInclusive, upperInclusive); + this.numericConfig = numericConfig; + + } + + /** + * Returns the {@link NumericConfig} associated with the lower and upper bounds. + * + * @return the {@link NumericConfig} associated with the lower and upper bounds + */ + public NumericConfig getNumericConfig() { + return this.numericConfig; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("\n"); + + sb.append(getLowerBound()).append('\n'); + sb.append(getUpperBound()).append('\n'); + sb.append(""); + + return sb.toString(); + + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\nodes\NumericRangeQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/RangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/RangeQueryNode.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/RangeQueryNode.java (working copy) @@ -19,8 +19,9 @@ import java.text.Collator; +import org.apache.lucene.queryParser.core.nodes.FieldQueryNode; import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode; -import org.apache.lucene.queryParser.core.nodes.ParametricRangeQueryNode; +import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode.CompareOperator; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; import org.apache.lucene.queryParser.standard.processors.ParametricRangeQueryNodeProcessor; @@ -31,8 +32,12 @@ * @see ParametricRangeQueryNodeProcessor * @see ConfigurationKeys#RANGE_COLLATOR * @see org.apache.lucene.search.TermRangeQuery + * + * @deprecated this class will be removed in future, {@link TermRangeQueryNode} should + * be used instead */ -public class RangeQueryNode extends ParametricRangeQueryNode { +@Deprecated +public class RangeQueryNode extends TermRangeQueryNode { private static final long serialVersionUID = 7400866652044314657L; @@ -42,14 +47,62 @@ * @param lower * @param upper */ - public RangeQueryNode(ParametricQueryNode lower, ParametricQueryNode upper, Collator collator) { - super(lower, upper); - + public RangeQueryNode(ParametricQueryNode lower, ParametricQueryNode upper, + Collator collator) { + + super(lower, upper, lower.getOperator() == CompareOperator.LE, upper + .getOperator() == CompareOperator.GE); + this.collator = collator; - + } - + @Override + public ParametricQueryNode getLowerBound() { + return (ParametricQueryNode) super.getLowerBound(); + } + + @Override + public ParametricQueryNode getUpperBound() { + return (ParametricQueryNode) super.getUpperBound(); + } + + /** + * Sets lower and upper bounds. The method signature expects + * {@link FieldQueryNode} objects as lower and upper, however, + * an {@link IllegalArgumentException} will be thrown at runtime + * if a non {@link ParametricQueryNode} is passed as lower and upper. + * + * @param lower a {@link ParametricQueryNode} object + * @param upper a {@link ParametricQueryNode} object + * @param lowerInclusive true if lower bound is inclusive, otherwise, false + * @param upperInclusive true if upper bound is inclusive, otherwise, false + * + * @throws IllegalArgumentException if lower or upper are not instance of {@link ParametricQueryNode} + * + * @see TermRangeQueryNode#setBounds(FieldQueryNode, FieldQueryNode, boolean, boolean) + */ + @Override + public void setBounds(FieldQueryNode lower, FieldQueryNode upper, + boolean lowerInclusive, boolean upperInclusive) { + + if (lower != null && !(lower instanceof ParametricQueryNode)) { + throw new IllegalArgumentException("lower should be an instance of " + + ParametricQueryNode.class.getCanonicalName() + ", but found " + + lower.getClass().getCanonicalName()); + } + + if (upper != null && !(upper instanceof ParametricQueryNode)) { + throw new IllegalArgumentException("upper should be an instance of " + + ParametricQueryNode.class.getCanonicalName() + ", but found " + + lower.getClass().getCanonicalName()); + } + + super.setBounds(lower, upper, lowerInclusive, upperInclusive); + + } + + @Override public String toString() { StringBuilder sb = new StringBuilder("\n\t"); sb.append(this.getUpperBound()).append("\n\t"); @@ -66,5 +119,5 @@ public Collator getCollator() { return this.collator; } - + } Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/TermRangeQueryNode.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/TermRangeQueryNode.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/TermRangeQueryNode.java (revision 0) @@ -0,0 +1,53 @@ +package org.apache.lucene.queryParser.standard.nodes; + +/** + * 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 org.apache.lucene.queryParser.core.nodes.FieldQueryNode; + +/** + * This query node represents a range query composed by {@link FieldQueryNode} +* bounds, which means the bound values are strings. + * + * @see FieldQueryNode + * @see AbstractRangeQueryNode + */ +public class TermRangeQueryNode extends AbstractRangeQueryNode { + + private static final long serialVersionUID = 5470829448078569869L; + + /** + * Constructs a {@link TermRangeQueryNode} object using the given + * {@link FieldQueryNode} as its bounds. + * + * @param lower + * the lower bound + * @param upper + * the upper bound + * @param lowerInclusive + * true if the lower bound is inclusive, otherwise, + * false + * @param upperInclusive + * true if the upper bound is inclusive, otherwise, + * false + */ + public TermRangeQueryNode(FieldQueryNode lower, + FieldQueryNode upper, boolean lowerInclusive, boolean upperInclusive) { + setBounds(lower, upper, lowerInclusive, upperInclusive); + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\nodes\TermRangeQueryNode.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/MultiTermRewriteMethodProcessor.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/MultiTermRewriteMethodProcessor.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/MultiTermRewriteMethodProcessor.java (working copy) @@ -23,6 +23,7 @@ import org.apache.lucene.queryParser.core.nodes.QueryNode; import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; +import org.apache.lucene.queryParser.standard.nodes.AbstractRangeQueryNode; import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode; import org.apache.lucene.search.MultiTermQuery; @@ -42,7 +43,7 @@ // set setMultiTermRewriteMethod for WildcardQueryNode and // PrefixWildcardQueryNode if (node instanceof WildcardQueryNode - || node instanceof ParametricRangeQueryNode) { + || node instanceof AbstractRangeQueryNode) { // read the attribute value and use a TAG to take the value to the Builder Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericQueryNodeProcessor.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericQueryNodeProcessor.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericQueryNodeProcessor.java (revision 0) @@ -0,0 +1,145 @@ +package org.apache.lucene.queryParser.standard.processors; + +/** + * 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.text.NumberFormat; +import java.text.ParseException; +import java.util.List; + +import org.apache.lucene.messages.MessageImpl; +import org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.QueryNodeParseException; +import org.apache.lucene.queryParser.core.config.FieldConfig; +import org.apache.lucene.queryParser.core.config.QueryConfigHandler; +import org.apache.lucene.queryParser.core.messages.QueryParserMessages; +import org.apache.lucene.queryParser.core.nodes.FieldQueryNode; +import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl; +import org.apache.lucene.queryParser.standard.config.NumericConfig; +import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; +import org.apache.lucene.queryParser.standard.nodes.NumericQueryNode; +import org.apache.lucene.queryParser.standard.nodes.NumericRangeQueryNode; + +/** + * This processor is used to convert {@link FieldQueryNode}s to + * {@link NumericRangeQueryNode}s. It looks for + * {@link ConfigurationKeys#NUMERIC_CONFIG} set in the {@link FieldConfig} of + * every {@link FieldQueryNode} found. If + * {@link ConfigurationKeys#NUMERIC_CONFIG} is found, it considers that + * {@link FieldQueryNode} to be a numeric query and convert it to + * {@link NumericRangeQueryNode} with upper and lower inclusive and lower and + * upper equals to the value represented by the {@link FieldQueryNode} converted + * to {@link Number}. It means that field:1 is converted to field:[1 TO + * 1].
      + *
      + * Note that {@link ParametricQueryNode}s are ignored, even being a + * {@link FieldQueryNode}. + * + * @see ConfigurationKeys#NUMERIC_CONFIG + * @see FieldQueryNode + * @see NumericConfig + * @see NumericQueryNode + */ +public class NumericQueryNodeProcessor extends QueryNodeProcessorImpl { + + /** + * Constructs a {@link NumericQueryNodeProcessor} object. + */ + public NumericQueryNodeProcessor() { + // empty constructor + } + + @Override + protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException { + + if (node instanceof FieldQueryNode + && !(node instanceof ParametricQueryNode)) { + + QueryConfigHandler config = getQueryConfigHandler(); + + if (config != null) { + FieldQueryNode fieldNode = (FieldQueryNode) node; + FieldConfig fieldConfig = config.getFieldConfig(fieldNode + .getFieldAsString()); + + if (fieldConfig != null) { + NumericConfig numericConfig = fieldConfig + .get(ConfigurationKeys.NUMERIC_CONFIG); + + if (numericConfig != null) { + + NumberFormat numberFormat = numericConfig.getNumberFormat(); + Number number; + + try { + number = numberFormat.parse(fieldNode.getTextAsString()); + + } catch (ParseException e) { + throw new QueryNodeParseException(new MessageImpl( + QueryParserMessages.COULD_NOT_PARSE_NUMBER, fieldNode + .getTextAsString(), numberFormat.getClass() + .getCanonicalName()), e); + } + + switch (numericConfig.getType()) { + case LONG: + number = number.longValue(); + break; + case INT: + number = number.intValue(); + break; + case DOUBLE: + number = number.doubleValue(); + break; + case FLOAT: + number = number.floatValue(); + } + + NumericQueryNode lowerNode = new NumericQueryNode(fieldNode + .getField(), number, numberFormat); + NumericQueryNode upperNode = new NumericQueryNode(fieldNode + .getField(), number, numberFormat); + + return new NumericRangeQueryNode(lowerNode, upperNode, true, true, + numericConfig); + + } + + } + + } + + } + + return node; + + } + + @Override + protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException { + return node; + } + + @Override + protected List setChildrenOrder(List children) + throws QueryNodeException { + return children; + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\processors\NumericQueryNodeProcessor.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericRangeQueryNodeProcessor.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericRangeQueryNodeProcessor.java (revision 0) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/NumericRangeQueryNodeProcessor.java (revision 0) @@ -0,0 +1,160 @@ +package org.apache.lucene.queryParser.standard.processors; + +/** + * 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.text.NumberFormat; +import java.text.ParseException; +import java.util.List; + +import org.apache.lucene.messages.MessageImpl; +import org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.QueryNodeParseException; +import org.apache.lucene.queryParser.core.config.FieldConfig; +import org.apache.lucene.queryParser.core.config.QueryConfigHandler; +import org.apache.lucene.queryParser.core.messages.QueryParserMessages; +import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode; +import org.apache.lucene.queryParser.core.nodes.ParametricRangeQueryNode; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode.CompareOperator; +import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl; +import org.apache.lucene.queryParser.core.util.StringUtils; +import org.apache.lucene.queryParser.standard.config.NumericConfig; +import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; +import org.apache.lucene.queryParser.standard.nodes.NumericQueryNode; +import org.apache.lucene.queryParser.standard.nodes.NumericRangeQueryNode; + +/** + * This processor is used to convert {@link ParametricRangeQueryNode}s to + * {@link NumericRangeQueryNode}s. It looks for + * {@link ConfigurationKeys#NUMERIC_CONFIG} set in the {@link FieldConfig} of + * every {@link ParametricRangeQueryNode} found. If + * {@link ConfigurationKeys#NUMERIC_CONFIG} is found, it considers that + * {@link ParametricRangeQueryNode} to be a numeric range query and convert it to + * {@link NumericRangeQueryNode}. + * + * @see ConfigurationKeys#NUMERIC_CONFIG + * @see ParametricRangeQueryNode + * @see NumericConfig + * @see NumericRangeQueryNode + */ +public class NumericRangeQueryNodeProcessor extends QueryNodeProcessorImpl { + + /** + * Constructs an empty {@link NumericRangeQueryNode} object. + */ + public NumericRangeQueryNodeProcessor() { + // empty constructor + } + + @Override + protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException { + + if (node instanceof ParametricRangeQueryNode) { + QueryConfigHandler config = getQueryConfigHandler(); + + if (config != null) { + ParametricRangeQueryNode parametricRangeNode = (ParametricRangeQueryNode) node; + FieldConfig fieldConfig = config.getFieldConfig(StringUtils + .toString(parametricRangeNode.getField())); + + if (fieldConfig != null) { + + NumericConfig numericConfig = fieldConfig + .get(ConfigurationKeys.NUMERIC_CONFIG); + + if (numericConfig != null) { + + ParametricQueryNode lower = parametricRangeNode.getLowerBound(); + ParametricQueryNode upper = parametricRangeNode.getUpperBound(); + + NumberFormat numberFormat = numericConfig.getNumberFormat(); + Number lowerNumber, upperNumber; + + try { + lowerNumber = numberFormat.parse(lower.getTextAsString()); + + } catch (ParseException e) { + throw new QueryNodeParseException(new MessageImpl( + QueryParserMessages.COULD_NOT_PARSE_NUMBER, lower + .getTextAsString(), numberFormat.getClass() + .getCanonicalName()), e); + } + + try { + upperNumber = numberFormat.parse(upper.getTextAsString()); + + } catch (ParseException e) { + throw new QueryNodeParseException(new MessageImpl( + QueryParserMessages.COULD_NOT_PARSE_NUMBER, upper + .getTextAsString(), numberFormat.getClass() + .getCanonicalName()), e); + } + + switch (numericConfig.getType()) { + case LONG: + upperNumber = upperNumber.longValue(); + lowerNumber = lowerNumber.longValue(); + break; + case INT: + upperNumber = upperNumber.intValue(); + lowerNumber = lowerNumber.intValue(); + break; + case DOUBLE: + upperNumber = upperNumber.doubleValue(); + lowerNumber = lowerNumber.doubleValue(); + break; + case FLOAT: + upperNumber = upperNumber.floatValue(); + lowerNumber = lowerNumber.floatValue(); + } + + NumericQueryNode lowerNode = new NumericQueryNode( + parametricRangeNode.getField(), lowerNumber, numberFormat); + NumericQueryNode upperNode = new NumericQueryNode( + parametricRangeNode.getField(), upperNumber, numberFormat); + + boolean upperInclusive = upper.getOperator() == CompareOperator.LE; + boolean lowerInclusive = lower.getOperator() == CompareOperator.GE; + + return new NumericRangeQueryNode(lowerNode, upperNode, + lowerInclusive, upperInclusive, numericConfig); + + } + + } + + } + + } + + return node; + + } + + @Override + protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException { + return node; + } + + @Override + protected List setChildrenOrder(List children) + throws QueryNodeException { + return children; + } + +} Property changes on: lucene\contrib\queryparser\src\java\org\apache\lucene\queryParser\standard\processors\NumericRangeQueryNodeProcessor.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/package.html =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/package.html (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/package.html (working copy) @@ -27,7 +27,7 @@ that modifies the query node tree according to the actual Lucene queries.

      -This processors are already assembled correctly in the StandardQueryNodeProcessorPipeline. +These processors are already assembled correctly in the StandardQueryNodeProcessorPipeline.

      Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java (working copy) @@ -56,18 +56,18 @@ * * @see ConfigurationKeys#DATE_RESOLUTION * @see ConfigurationKeys#LOCALE - * @see RangeQueryNode + * @see TermRangeQueryNode * @see ParametricRangeQueryNode */ public class ParametricRangeQueryNodeProcessor extends QueryNodeProcessorImpl { - + public ParametricRangeQueryNodeProcessor() { - // empty constructor + // empty constructor } - + @Override protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException { - + if (node instanceof ParametricRangeQueryNode) { ParametricRangeQueryNode parametricRangeNode = (ParametricRangeQueryNode) node; ParametricQueryNode upper = parametricRangeNode.getUpperBound(); Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java (working copy) @@ -52,6 +52,8 @@ add(new MultiFieldQueryNodeProcessor()); add(new FuzzyQueryNodeProcessor()); add(new MatchAllDocsQueryNodeProcessor()); + add(new NumericQueryNodeProcessor()); + add(new NumericRangeQueryNodeProcessor()); add(new LowercaseExpandedTermsQueryNodeProcessor()); add(new ParametricRangeQueryNodeProcessor()); add(new AllowLeadingWildcardProcessor()); Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/StandardQueryParser.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/StandardQueryParser.java (revision 1158551) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/StandardQueryParser.java (working copy) @@ -31,6 +31,7 @@ import org.apache.lucene.queryParser.standard.builders.StandardQueryTreeBuilder; import org.apache.lucene.queryParser.standard.config.DefaultOperatorAttribute; import org.apache.lucene.queryParser.standard.config.FuzzyConfig; +import org.apache.lucene.queryParser.standard.config.NumericConfig; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.Operator; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; @@ -112,7 +113,7 @@ * @see StandardQueryTreeBuilder */ public class StandardQueryParser extends QueryParserHelper { - + /** * Constructs a {@link StandardQueryParser} object. */ @@ -121,7 +122,7 @@ new StandardQueryNodeProcessorPipeline(null), new StandardQueryTreeBuilder()); } - + /** * Constructs a {@link StandardQueryParser} object and sets an * {@link Analyzer} to it. The same as: @@ -136,15 +137,16 @@ */ public StandardQueryParser(Analyzer analyzer) { this(); - + this.setAnalyzer(analyzer); } @Override - public String toString(){ - return ""; + public String toString() { + return ""; } - + /** * Overrides {@link QueryParserHelper#parse(String, String)} so it casts the * return object to {@link Query}. For more reference about this method, check @@ -163,11 +165,11 @@ @Override public Query parse(String query, String defaultField) throws QueryNodeException { - + return (Query) super.parse(query, defaultField); - + } - + /** * Gets implicit operator setting, which will be either {@link Operator#AND} * or {@link Operator#OR}. @@ -175,7 +177,7 @@ public StandardQueryConfigHandler.Operator getDefaultOperator() { return getQueryConfigHandler().get(ConfigurationKeys.DEFAULT_OPERATOR); } - + /** * Sets the collator used to determine index term inclusion in ranges for * RangeQuerys. @@ -235,7 +237,7 @@ public void setDefaultOperator(org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.Operator operator) { getQueryConfigHandler().set(ConfigurationKeys.DEFAULT_OPERATOR, operator); } - + /** * Set to true to allow leading wildcard characters. *

      @@ -248,7 +250,7 @@ public void setLowercaseExpandedTerms(boolean lowercaseExpandedTerms) { getQueryConfigHandler().set(ConfigurationKeys.LOWERCASE_EXPANDED_TERMS, lowercaseExpandedTerms); } - + /** * @see #setLowercaseExpandedTerms(boolean) */ @@ -263,7 +265,7 @@ } } - + /** * Set to true to allow leading wildcard characters. *

      @@ -276,7 +278,7 @@ public void setAllowLeadingWildcard(boolean allowLeadingWildcard) { getQueryConfigHandler().set(ConfigurationKeys.ALLOW_LEADING_WILDCARD, allowLeadingWildcard); } - + /** * Set to true to enable position increments in result query. *

      @@ -289,7 +291,7 @@ public void setEnablePositionIncrements(boolean enabled) { getQueryConfigHandler().set(ConfigurationKeys.ENABLE_POSITION_INCREMENTS, enabled); } - + /** * @see #setEnablePositionIncrements(boolean) */ @@ -304,7 +306,7 @@ } } - + /** * By default, it uses * {@link MultiTermQuery#CONSTANT_SCORE_AUTO_REWRITE_DEFAULT} when creating a @@ -318,14 +320,14 @@ public void setMultiTermRewriteMethod(MultiTermQuery.RewriteMethod method) { getQueryConfigHandler().set(ConfigurationKeys.MULTI_TERM_REWRITE_METHOD, method); } - + /** * @see #setMultiTermRewriteMethod(org.apache.lucene.search.MultiTermQuery.RewriteMethod) */ public MultiTermQuery.RewriteMethod getMultiTermRewriteMethod() { return getQueryConfigHandler().get(ConfigurationKeys.MULTI_TERM_REWRITE_METHOD); } - + /** * Set the fields a query should be expanded to when the field is * null @@ -333,15 +335,15 @@ * @param fields the fields used to expand the query */ public void setMultiFields(CharSequence[] fields) { - + if (fields == null) { fields = new CharSequence[0]; } getQueryConfigHandler().set(ConfigurationKeys.MULTI_FIELDS, fields); - + } - + /** * Returns the fields used to expand the query when the field for a * certain query is null @@ -370,21 +372,29 @@ fuzzyConfig.setPrefixLength(fuzzyPrefixLength); } - + + public void setNumericConfigMap(Map numericConfigMap) { + getQueryConfigHandler().set(ConfigurationKeys.NUMERIC_CONFIG_MAP, numericConfigMap); + } + + public Map getNumericConfigMap() { + return getQueryConfigHandler().get(ConfigurationKeys.NUMERIC_CONFIG_MAP); + } + /** * Set locale used by date range parsing. */ public void setLocale(Locale locale) { getQueryConfigHandler().set(ConfigurationKeys.LOCALE, locale); } - + /** * Returns current locale, allowing access by subclasses. */ public Locale getLocale() { return getQueryConfigHandler().get(ConfigurationKeys.LOCALE); } - + /** * Sets the default slop for phrases. If zero, then exact phrase matches are * required. Default value is zero. @@ -408,10 +418,10 @@ getQueryConfigHandler().set(ConfigurationKeys.ANALYZER, analyzer); } - public Analyzer getAnalyzer() { + public Analyzer getAnalyzer() { return getQueryConfigHandler().get(ConfigurationKeys.ANALYZER); } - + /** * @see #setAllowLeadingWildcard(boolean) */ @@ -425,7 +435,7 @@ return allowLeadingWildcard; } } - + /** * Get the minimal similarity for fuzzy queries. */ @@ -438,7 +448,7 @@ return fuzzyConfig.getMinSimilarity(); } } - + /** * Get the prefix length for fuzzy queries. * @@ -453,7 +463,7 @@ return fuzzyConfig.getPrefixLength(); } } - + /** * Gets the default slop for phrases. */ @@ -467,7 +477,7 @@ return phraseSlop; } } - + /** * Set the minimum similarity for fuzzy queries. Default is defined on * {@link FuzzyQuery#defaultMinSimilarity}. Index: lucene/contrib/queryparser/src/resources/org/apache/lucene/queryParser/core/messages/QueryParserMessages.properties =================================================================== --- lucene/contrib/queryparser/src/resources/org/apache/lucene/queryParser/core/messages/QueryParserMessages.properties (revision 1158551) +++ lucene/contrib/queryparser/src/resources/org/apache/lucene/queryParser/core/messages/QueryParserMessages.properties (working copy) @@ -43,3 +43,12 @@ #Apache Lucene Community LEADING_WILDCARD_NOT_ALLOWED = Leading wildcard is not allowed: {0} + +#Apache Lucene Community +COULD_NOT_PARSE_NUMBER = Could not parse text "{0}" using {1} + +#Apache Lucene Community +NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY = Number class not supported by NumericRangeQueryNode: {0} + +#Apache Lucene Community +UNSUPPORTED_NUMERIC_DATA_TYPE = Unsupported NumericField.DataType: {0} Index: lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestNumericQueryParser.java =================================================================== --- lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestNumericQueryParser.java (revision 0) +++ lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestNumericQueryParser.java (revision 0) @@ -0,0 +1,503 @@ +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 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 randomNumberMap = new HashMap(); + + 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 numericConfigMap = new HashMap(); + HashMap numericFieldMap = new HashMap(); + 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 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; + } + +} Property changes on: lucene\contrib\queryparser\src\test\org\apache\lucene\queryParser\standard\TestNumericQueryParser.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Property changes on: solr ___________________________________________________________________ Modified: svn:mergeinfo Merged /lucene/dev/trunk/solr:r1145885,1146984,1150671,1152892