mirror of https://github.com/apache/lucene.git
LUCENE-1768: Committing new NumericRangeQuery support by Vinicius Barros to flexible StandardQueryParser. This first commit supports all functionality, but still needs some improvements, that will be part of 2nd half of GSoC.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1144744 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cf6fb18337
commit
c178c8eb1a
|
@ -51,5 +51,8 @@ public class QueryParserMessages extends NLS {
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax.Type;
|
|||
/**
|
||||
* 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<CharSequence>, TextableQueryNode {
|
||||
|
||||
/**
|
||||
* The term's field
|
||||
|
@ -180,4 +179,12 @@ public class FieldQueryNode extends QueryNodeImpl implements TextableQueryNode,
|
|||
|
||||
}
|
||||
|
||||
public CharSequence getValue() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
public void setValue(CharSequence value) {
|
||||
setText(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
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.
|
||||
*/
|
||||
|
||||
public interface FieldValuePairQueryNode<T extends Object> extends FieldableNode, ValueQueryNode<T> {
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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.
|
||||
*/
|
||||
|
||||
public interface NumberQueryNode {
|
||||
|
||||
void setNumber(Number number);
|
||||
|
||||
Number getNumber();
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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.
|
||||
*/
|
||||
|
||||
public interface ValueQueryNode<T extends Object> extends QueryNode {
|
||||
|
||||
public void setValue(T value);
|
||||
|
||||
public T getValue();
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ import org.apache.lucene.queryParser.core.QueryParserHelper;
|
|||
import org.apache.lucene.queryParser.core.config.QueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.builders.StandardQueryTreeBuilder;
|
||||
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.ConfigurationKeys;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.Operator;
|
||||
|
@ -138,8 +139,9 @@ public class StandardQueryParser extends QueryParserHelper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "<StandardQueryParser config=\"" + this.getQueryConfigHandler() + "\"/>";
|
||||
public String toString() {
|
||||
return "<StandardQueryParser config=\"" + this.getQueryConfigHandler()
|
||||
+ "\"/>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -320,6 +322,14 @@ public class StandardQueryParser extends QueryParserHelper {
|
|||
|
||||
}
|
||||
|
||||
public void setNumericConfigMap(Map<String,NumericConfig> numericConfigMap) {
|
||||
getQueryConfigHandler().set(ConfigurationKeys.NUMERIC_CONFIG_MAP, numericConfigMap);
|
||||
}
|
||||
|
||||
public Map<String,NumericConfig> getNumericConfigMap() {
|
||||
return getQueryConfigHandler().get(ConfigurationKeys.NUMERIC_CONFIG_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale used by date range parsing.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
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.search.TermQuery;
|
||||
|
||||
/**
|
||||
* Builds a {@link TermQuery} object from a {@link FieldQueryNode} object.
|
||||
*/
|
||||
public class DummyQueryNodeBuilder implements StandardQueryBuilder {
|
||||
|
||||
public DummyQueryNodeBuilder() {
|
||||
// empty constructor
|
||||
}
|
||||
|
||||
public TermQuery build(QueryNode queryNode) throws QueryNodeException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
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;
|
||||
|
||||
public class NumericRangeQueryNodeBuilder implements StandardQueryBuilder {
|
||||
|
||||
public NumericRangeQueryNodeBuilder() {
|
||||
// empty constructor
|
||||
}
|
||||
|
||||
public NumericRangeQuery<? extends Number> 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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -31,8 +31,10 @@ import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
|||
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.TermRangeQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.RegexpQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.StandardBooleanQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode;
|
||||
|
@ -56,6 +58,8 @@ public class StandardQueryTreeBuilder extends QueryTreeBuilder implements
|
|||
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,7 +67,7 @@ public class StandardQueryTreeBuilder extends QueryTreeBuilder implements
|
|||
setBuilder(MatchNoDocsQueryNode.class, new MatchNoDocsQueryNodeBuilder());
|
||||
setBuilder(PrefixWildcardQueryNode.class,
|
||||
new PrefixWildcardQueryNodeBuilder());
|
||||
setBuilder(RangeQueryNode.class, new RangeQueryNodeBuilder());
|
||||
setBuilder(TermRangeQueryNode.class, new TermRangeQueryNodeBuilder());
|
||||
setBuilder(RegexpQueryNode.class, new RegexpQueryNodeBuilder());
|
||||
setBuilder(SlopQueryNode.class, new SlopQueryNodeBuilder());
|
||||
setBuilder(StandardBooleanQueryNode.class,
|
||||
|
|
|
@ -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 = TermRangeQuery.newStringRange(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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
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.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Date;
|
||||
|
||||
public class NumberDateFormat extends NumberFormat {
|
||||
|
||||
private static final long serialVersionUID = 964823936071308283L;
|
||||
|
||||
final private DateFormat dateFormat;
|
||||
|
||||
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) {
|
||||
return dateFormat.parse(source, parsePosition).getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuffer format(Object number, StringBuffer toAppendTo,
|
||||
FieldPosition pos) {
|
||||
return dateFormat.format(number, toAppendTo, pos);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
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;
|
||||
|
||||
public class NumericConfig {
|
||||
|
||||
private int precisionStep;
|
||||
|
||||
private NumberFormat format;
|
||||
|
||||
private NumericField.DataType type;
|
||||
|
||||
public NumericConfig(int precisionStep, NumberFormat format, NumericField.DataType type) {
|
||||
setPrecisionStep(precisionStep);
|
||||
setNumberFormat(format);
|
||||
setType(type);
|
||||
|
||||
}
|
||||
|
||||
public int getPrecisionStep() {
|
||||
return precisionStep;
|
||||
}
|
||||
|
||||
public void setPrecisionStep(int precisionStep) {
|
||||
this.precisionStep = precisionStep;
|
||||
}
|
||||
|
||||
public NumberFormat getNumberFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public NumericField.DataType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(NumericField.DataType type) {
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("type cannot be null!");
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
|
||||
}
|
||||
|
||||
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.format == other.format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
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;
|
||||
|
||||
public class NumericFieldConfigListener implements FieldConfigListener {
|
||||
|
||||
final private QueryConfigHandler config;
|
||||
|
||||
public NumericFieldConfigListener(QueryConfigHandler config) {
|
||||
|
||||
if (config == null) {
|
||||
throw new IllegalArgumentException("config cannot be null!");
|
||||
}
|
||||
|
||||
this.config = config;
|
||||
|
||||
}
|
||||
|
||||
public void buildFieldConfig(FieldConfig fieldConfig) {
|
||||
Map<String,NumericConfig> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -161,6 +161,22 @@ public class StandardQueryConfigHandler extends QueryConfigHandler {
|
|||
*/
|
||||
final public static ConfigurationKey<Float> BOOST = ConfigurationKey.newInstance();
|
||||
|
||||
/**
|
||||
* Key used to set a field to its {@link NumericConfig}.
|
||||
*
|
||||
* @see StandardQueryParser#setNumericConfigMap(Map)
|
||||
* @see StandardQueryParser#getNumericConfigMap()
|
||||
*/
|
||||
final public static ConfigurationKey<NumericConfig> 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<Map<String,NumericConfig>> NUMERIC_CONFIG_MAP = ConfigurationKey.newInstance();
|
||||
|
||||
}
|
||||
|
||||
public static enum Operator {
|
||||
|
@ -171,6 +187,7 @@ public class StandardQueryConfigHandler extends QueryConfigHandler {
|
|||
// 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
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
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.FieldableNode;
|
||||
import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
||||
import org.apache.lucene.queryParser.core.nodes.QueryNodeImpl;
|
||||
import org.apache.lucene.queryParser.core.parser.EscapeQuerySyntax;
|
||||
import org.apache.lucene.queryParser.core.util.StringUtils;
|
||||
|
||||
public abstract class AbstractRangeQueryNode<T extends FieldValuePairQueryNode<?>>
|
||||
extends QueryNodeImpl implements FieldableNode {
|
||||
|
||||
private boolean lowerInclusive, upperInclusive;
|
||||
|
||||
protected AbstractRangeQueryNode() {
|
||||
setLeaf(false);
|
||||
allocate();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
public void setField(CharSequence fieldName) {
|
||||
T lower = getLowerBound();
|
||||
T upper = getUpperBound();
|
||||
|
||||
if (lower != null) {
|
||||
lower.setField(fieldName);
|
||||
}
|
||||
|
||||
if (upper != null) {
|
||||
upper.setField(fieldName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getLowerBound() {
|
||||
return (T) getChildren().get(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getUpperBound() {
|
||||
return (T) getChildren().get(1);
|
||||
}
|
||||
|
||||
public boolean isLowerInclusive() {
|
||||
return lowerInclusive;
|
||||
}
|
||||
|
||||
public boolean isUpperInclusive() {
|
||||
return upperInclusive;
|
||||
}
|
||||
|
||||
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<QueryNode> children = new ArrayList<QueryNode>(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();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
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;
|
||||
|
||||
public class NumericQueryNode extends QueryNodeImpl implements
|
||||
FieldValuePairQueryNode<Number> {
|
||||
|
||||
private NumberFormat numberFormat;
|
||||
|
||||
private CharSequence field;
|
||||
|
||||
private Number value;
|
||||
|
||||
public NumericQueryNode(CharSequence field, Number value,
|
||||
NumberFormat numberFormat) {
|
||||
|
||||
super();
|
||||
|
||||
setNumberFormat(numberFormat);
|
||||
setField(field);
|
||||
setValue(value);
|
||||
|
||||
}
|
||||
|
||||
public CharSequence getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
public void setField(CharSequence fieldName) {
|
||||
this.field = fieldName;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNumberFormat(NumberFormat format) {
|
||||
this.numberFormat = format;
|
||||
}
|
||||
|
||||
public NumberFormat getNumberFormat() {
|
||||
return this.numberFormat;
|
||||
}
|
||||
|
||||
public Number getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Number value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<numeric field='" + this.field + "' number='"
|
||||
+ numberFormat.format(value) + "'/>";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
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.
|
||||
*/
|
||||
|
||||
public class NumericRangeQueryNode extends
|
||||
AbstractRangeQueryNode<NumericQueryNode> {
|
||||
|
||||
public NumericConfig 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
public NumericConfig getNumericConfig() {
|
||||
return this.numericConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("<numericRange lowerInclusive='");
|
||||
|
||||
sb.append(isLowerInclusive()).append("' upperInclusive='").append(
|
||||
isUpperInclusive()).append(
|
||||
"' precisionStep='" + numericConfig.getPrecisionStep()).append(
|
||||
"' type='" + numericConfig.getType()).append("'>\n");
|
||||
|
||||
sb.append(getLowerBound()).append('\n');
|
||||
sb.append(getUpperBound()).append('\n');
|
||||
sb.append("</numericRange>");
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
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.
|
||||
*
|
||||
* @see ParametricRangeQueryNodeProcessor
|
||||
* @see org.apache.lucene.search.TermRangeQuery
|
||||
*/
|
||||
public class TermRangeQueryNode extends AbstractRangeQueryNode<FieldQueryNode> {
|
||||
|
||||
public TermRangeQueryNode(FieldQueryNode lower, FieldQueryNode upper,
|
||||
boolean lowerInclusive, boolean upperInclusive) {
|
||||
setBounds(lower, upper, lowerInclusive, upperInclusive);
|
||||
}
|
||||
|
||||
}
|
|
@ -40,7 +40,6 @@ import org.apache.lucene.queryParser.core.nodes.QuotedFieldQueryNode;
|
|||
import org.apache.lucene.queryParser.core.nodes.TextableQueryNode;
|
||||
import org.apache.lucene.queryParser.core.nodes.TokenizedPhraseQueryNode;
|
||||
import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
import org.apache.lucene.queryParser.standard.nodes.MultiPhraseQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.StandardBooleanQueryNode;
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
|||
import org.apache.lucene.queryParser.core.nodes.SlopQueryNode;
|
||||
import org.apache.lucene.queryParser.core.nodes.TokenizedPhraseQueryNode;
|
||||
import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
import org.apache.lucene.queryParser.standard.nodes.MultiPhraseQueryNode;
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.lucene.queryParser.core.nodes.FuzzyQueryNode;
|
|||
import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
||||
import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
|
||||
import org.apache.lucene.queryParser.standard.config.FuzzyConfig;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
import org.apache.lucene.search.FuzzyQuery;
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
|||
import org.apache.lucene.queryParser.core.nodes.TextableQueryNode;
|
||||
import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
|
||||
import org.apache.lucene.queryParser.core.util.UnescapedCharSequence;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
import org.apache.lucene.queryParser.standard.nodes.RegexpQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode;
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.apache.lucene.queryParser.core.nodes.FieldableNode;
|
|||
import org.apache.lucene.queryParser.core.nodes.GroupQueryNode;
|
||||
import org.apache.lucene.queryParser.core.nodes.QueryNode;
|
||||
import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,10 +19,10 @@ package org.apache.lucene.queryParser.standard.processors;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.queryParser.core.nodes.ParametricRangeQueryNode;
|
||||
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.RegexpQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
|
@ -43,7 +43,7 @@ public class MultiTermRewriteMethodProcessor extends QueryNodeProcessorImpl {
|
|||
// set setMultiTermRewriteMethod for WildcardQueryNode and
|
||||
// PrefixWildcardQueryNode
|
||||
if (node instanceof WildcardQueryNode
|
||||
|| node instanceof ParametricRangeQueryNode || node instanceof RegexpQueryNode) {
|
||||
|| node instanceof AbstractRangeQueryNode || node instanceof RegexpQueryNode) {
|
||||
|
||||
MultiTermQuery.RewriteMethod rewriteMethod = getQueryConfigHandler().get(ConfigurationKeys.MULTI_TERM_REWRITE_METHOD);
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
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;
|
||||
|
||||
public class NumericQueryNodeProcessor extends QueryNodeProcessorImpl {
|
||||
|
||||
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<QueryNode> setChildrenOrder(List<QueryNode> children)
|
||||
throws QueryNodeException {
|
||||
return children;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
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;
|
||||
|
||||
public class NumericRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
|
||||
|
||||
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<QueryNode> setChildrenOrder(List<QueryNode> children)
|
||||
throws QueryNodeException {
|
||||
return children;
|
||||
}
|
||||
|
||||
}
|
|
@ -33,17 +33,16 @@ 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.standard.config.StandardQueryConfigHandler;
|
||||
import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
|
||||
import org.apache.lucene.queryParser.standard.nodes.RangeQueryNode;
|
||||
import org.apache.lucene.queryParser.standard.nodes.TermRangeQueryNode;
|
||||
|
||||
/**
|
||||
* This processor converts {@link ParametricRangeQueryNode} objects to
|
||||
* {@link RangeQueryNode} objects. It reads the lower and upper bounds value
|
||||
* {@link TermRangeQueryNode} objects. It reads the lower and upper bounds value
|
||||
* from the {@link ParametricRangeQueryNode} object and try to parse their
|
||||
* values using a {@link DateFormat}. If the values cannot be parsed to a date
|
||||
* value, it will only create the {@link RangeQueryNode} using the non-parsed
|
||||
* values. <br/>
|
||||
* value, it will only create the {@link TermRangeQueryNode} using the
|
||||
* non-parsed values. <br/>
|
||||
* <br/>
|
||||
* If a {@link ConfigurationKeys#LOCALE} is defined in the {@link QueryConfigHandler} it
|
||||
* will be used to parse the date, otherwise {@link Locale#getDefault()} will be
|
||||
|
@ -55,7 +54,7 @@ import org.apache.lucene.queryParser.standard.nodes.RangeQueryNode;
|
|||
*
|
||||
* @see ConfigurationKeys#DATE_RESOLUTION
|
||||
* @see ConfigurationKeys#LOCALE
|
||||
* @see RangeQueryNode
|
||||
* @see TermRangeQueryNode
|
||||
* @see ParametricRangeQueryNode
|
||||
*/
|
||||
public class ParametricRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
|
||||
|
@ -131,7 +130,9 @@ public class ParametricRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
|
|||
lower.setText(part1);
|
||||
upper.setText(part2);
|
||||
|
||||
return new RangeQueryNode(lower, upper);
|
||||
return new TermRangeQueryNode(lower, upper,
|
||||
lower.getOperator() == CompareOperator.GE,
|
||||
upper.getOperator() == CompareOperator.LE);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ public class StandardQueryNodeProcessorPipeline extends
|
|||
add(new FuzzyQueryNodeProcessor());
|
||||
add(new MatchAllDocsQueryNodeProcessor());
|
||||
add(new LowercaseExpandedTermsQueryNodeProcessor());
|
||||
add(new NumericQueryNodeProcessor());
|
||||
add(new NumericRangeQueryNodeProcessor());
|
||||
add(new ParametricRangeQueryNodeProcessor());
|
||||
add(new AllowLeadingWildcardProcessor());
|
||||
add(new AnalyzerQueryNodeProcessor());
|
||||
|
|
|
@ -46,3 +46,12 @@ TOO_MANY_BOOLEAN_CLAUSES = Too many boolean clauses, the maximum supported is {0
|
|||
|
||||
#<CREATEDBY>Apache Lucene Community</CREATEDBY>
|
||||
LEADING_WILDCARD_NOT_ALLOWED = Leading wildcard is not allowed: {0}
|
||||
|
||||
#<CREATEDBY>Apache Lucene Community</CREATEDBY>
|
||||
COULD_NOT_PARSE_NUMBER = Could not parse text "{0}" using {1}
|
||||
|
||||
#<CREATEDBY>Apache Lucene Community</CREATEDBY>
|
||||
NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY = Number class not supported by NumericRangeQueryNode: {0}
|
||||
|
||||
#<CREATEDBY>Apache Lucene Community</CREATEDBY>
|
||||
UNSUPPORTED_NUMERIC_DATA_TYPE = Unsupported NumericField.DataType: {0}
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
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";
|
||||
final private static Locale LOCALE = randomLocale(random);
|
||||
final private static TimeZone TIMEZONE = randomTimeZone(random);
|
||||
final private static Map<String,Number> RANDOM_NUMBER_MAP;
|
||||
final private static EscapeQuerySyntax ESCAPER = new EscapeQuerySyntaxImpl();
|
||||
final private static String DATE_FIELD_NAME = "date";
|
||||
final private static int DATE_STYLE = randomDateStyle(random);
|
||||
final private static int TIME_STYLE = randomDateStyle(random);
|
||||
|
||||
final private static Analyzer ANALYZER = new MockAnalyzer(random);
|
||||
|
||||
final private static NumberFormat NUMBER_FORMAT = NumberFormat
|
||||
.getNumberInstance(LOCALE);
|
||||
|
||||
final private static StandardQueryParser qp = new StandardQueryParser(
|
||||
ANALYZER);
|
||||
|
||||
final private static NumberDateFormat DATE_FORMAT;
|
||||
|
||||
static {
|
||||
try {
|
||||
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);
|
||||
|
||||
// assumes localized date pattern will have at least year, month, day, hour, minute
|
||||
SimpleDateFormat 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);
|
||||
|
||||
HashMap<String,Number> randomNumberMap = new HashMap<String,Number>();
|
||||
|
||||
double randomDouble;
|
||||
long randomLong;
|
||||
int randomInt;
|
||||
float randomFloat;
|
||||
long randomDate;
|
||||
|
||||
while ((randomLong = normalizeNumber(Math.abs(random.nextLong()))
|
||||
.longValue()) == 0)
|
||||
;
|
||||
while ((randomDouble = normalizeNumber(Math.abs(random.nextDouble()))
|
||||
.doubleValue()) == 0)
|
||||
;
|
||||
while ((randomFloat = normalizeNumber(Math.abs(random.nextFloat()))
|
||||
.floatValue()) == 0)
|
||||
;
|
||||
while ((randomInt = normalizeNumber(Math.abs(random.nextInt()))
|
||||
.intValue()) == 0)
|
||||
;
|
||||
|
||||
// make sure random date is at least one second from 0
|
||||
while ((randomDate = normalizeNumber(Math.abs(random.nextLong()))
|
||||
.longValue()) < 1000)
|
||||
;
|
||||
|
||||
// truncate to second
|
||||
randomDate = (randomDate / 1000) * 1000;
|
||||
|
||||
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);
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Directory directory = null;
|
||||
private static IndexReader reader = null;
|
||||
private static IndexSearcher searcher = null;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
directory = newDirectory();
|
||||
RandomIndexWriter writer = new RandomIndexWriter(random, directory,
|
||||
newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random))
|
||||
.setMaxBufferedDocs(_TestUtil.nextInt(random, 50, 1000))
|
||||
.setMergePolicy(newLogMergePolicy()));
|
||||
|
||||
Document doc = new Document();
|
||||
HashMap<String,NumericConfig> numericConfigMap = new HashMap<String,NumericConfig>();
|
||||
HashMap<String,NumericField> numericFieldMap = new HashMap<String,NumericField>();
|
||||
qp.setNumericConfigMap(numericConfigMap);
|
||||
|
||||
for (NumericField.DataType type : NumericField.DataType.values()) {
|
||||
numericConfigMap.put(type.name(), new NumericConfig(PRECISION_STEP,
|
||||
NUMBER_FORMAT, type));
|
||||
|
||||
NumericField field = new NumericField(type.name(), PRECISION_STEP,
|
||||
Field.Store.YES, true);
|
||||
|
||||
numericFieldMap.put(type.name(), field);
|
||||
doc.add(field);
|
||||
|
||||
}
|
||||
|
||||
numericConfigMap.put(DATE_FIELD_NAME, new NumericConfig(PRECISION_STEP,
|
||||
DATE_FORMAT, NumericField.DataType.LONG));
|
||||
NumericField dateField = new NumericField(DATE_FIELD_NAME, PRECISION_STEP,
|
||||
Field.Store.YES, true);
|
||||
numericFieldMap.put(DATE_FIELD_NAME, dateField);
|
||||
doc.add(dateField);
|
||||
|
||||
for (NumberType numberType : NumberType.values()) {
|
||||
setFieldValues(numberType, numericFieldMap);
|
||||
if (VERBOSE) System.out.println("Indexing document: " + doc);
|
||||
writer.addDocument(doc);
|
||||
}
|
||||
|
||||
reader = writer.getReader();
|
||||
searcher = newSearcher(reader);
|
||||
writer.close();
|
||||
|
||||
|
||||
// SimpleDateFormat df = new SimpleDateFormat(
|
||||
// "yyyy.MM.dd G 'at' HH:mm:ss z", LOCALE.ENGLISH);
|
||||
// assumes localized date pattern will have at least year, month, day, hour, minute
|
||||
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(
|
||||
randomDateStyle(random), randomDateStyle(random), LOCALE.ENGLISH);
|
||||
System.out.println(df.toPattern());
|
||||
// most of date pattern do not include era, so we add it here. Also,
|
||||
// sometimes second is not available, we make sure it's present too
|
||||
df.applyPattern(df.toPattern() + " G s Z yyyy");
|
||||
df.setTimeZone(TIMEZONE);
|
||||
System.out.println(TIMEZONE);
|
||||
System.out.println(TIMEZONE);
|
||||
System.out.println(TIMEZONE);
|
||||
long l1 = 0;
|
||||
long l2 = -30000;
|
||||
String d1 = df.format(new Date(l1));
|
||||
String d2 = df.format(new Date(l2));
|
||||
long newL1 = df.parse(d1).getTime();
|
||||
long newL2 = df.parse(d2).getTime();
|
||||
|
||||
System.out.println(l1 + " => " + d1 + " => " + newL1);
|
||||
System.out.println(l2 + " => " + d2 + " => " + newL2);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static Number getNumberType(NumberType numberType, String fieldName) {
|
||||
|
||||
switch (numberType) {
|
||||
|
||||
case POSITIVE:
|
||||
return RANDOM_NUMBER_MAP.get(fieldName);
|
||||
|
||||
case NEGATIVE:
|
||||
Number number = RANDOM_NUMBER_MAP.get(fieldName);
|
||||
|
||||
if (NumericField.DataType.LONG.name().equals(fieldName)
|
||||
|| DATE_FIELD_NAME.equals(fieldName)) {
|
||||
number = -number.longValue();
|
||||
|
||||
} else if (NumericField.DataType.DOUBLE.name().equals(fieldName)) {
|
||||
number = -number.doubleValue();
|
||||
|
||||
} else if (NumericField.DataType.FLOAT.name().equals(fieldName)) {
|
||||
number = -number.floatValue();
|
||||
|
||||
} else if (NumericField.DataType.INT.name().equals(fieldName)) {
|
||||
number = -number.intValue();
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("field name not found: "
|
||||
+ fieldName);
|
||||
}
|
||||
|
||||
return number;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void setFieldValues(NumberType numberType,
|
||||
HashMap<String,NumericField> numericFieldMap) {
|
||||
|
||||
Number number = getNumberType(numberType, NumericField.DataType.DOUBLE
|
||||
.name());
|
||||
numericFieldMap.get(NumericField.DataType.DOUBLE.name()).setDoubleValue(
|
||||
number.doubleValue());
|
||||
|
||||
number = getNumberType(numberType, NumericField.DataType.INT.name());
|
||||
numericFieldMap.get(NumericField.DataType.INT.name()).setIntValue(
|
||||
number.intValue());
|
||||
|
||||
number = getNumberType(numberType, NumericField.DataType.LONG.name());
|
||||
numericFieldMap.get(NumericField.DataType.LONG.name()).setLongValue(
|
||||
number.longValue());
|
||||
|
||||
number = getNumberType(numberType, NumericField.DataType.FLOAT.name());
|
||||
numericFieldMap.get(NumericField.DataType.FLOAT.name()).setFloatValue(
|
||||
number.floatValue());
|
||||
|
||||
number = getNumberType(numberType, DATE_FIELD_NAME);
|
||||
numericFieldMap.get(DATE_FIELD_NAME).setLongValue(number.longValue());
|
||||
|
||||
}
|
||||
|
||||
private static int randomDateStyle(Random random) {
|
||||
return DATE_STYLES[random.nextInt(DATE_STYLES.length)];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInclusiveNumericRange() throws Exception {
|
||||
assertRangeQuery(NumberType.ZERO, NumberType.ZERO, true, true, 1);
|
||||
assertRangeQuery(NumberType.ZERO, NumberType.POSITIVE, true, true, 2);
|
||||
assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, true, true, 2);
|
||||
assertRangeQuery(NumberType.NEGATIVE, NumberType.POSITIVE, true, true, 3);
|
||||
assertRangeQuery(NumberType.NEGATIVE, NumberType.NEGATIVE, true, true, 1);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// test disabled since standard syntax parser does not work with inclusive and
|
||||
// exclusive at the same time
|
||||
// public void testInclusiveLowerNumericRange() throws Exception {
|
||||
// assertRangeQuery(NumberType.NEGATIVE, NumberType.ZERO, 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, 1);
|
||||
// }
|
||||
|
||||
// @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, 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, 1);
|
||||
// }
|
||||
|
||||
@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
|
||||
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 upperInclusive, boolean lowerInclusive, 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(' ');
|
||||
}
|
||||
|
||||
String lowerDateStr = ESCAPER.escape(
|
||||
DATE_FORMAT.format(new Date(getNumberType(lowerType, DATE_FIELD_NAME)
|
||||
.longValue())), LOCALE, EscapeQuerySyntax.Type.STRING).toString();
|
||||
|
||||
String upperDateStr = ESCAPER.escape(
|
||||
DATE_FORMAT.format(new Date(getNumberType(upperType, DATE_FIELD_NAME)
|
||||
.longValue())), LOCALE, EscapeQuerySyntax.Type.STRING).toString();
|
||||
|
||||
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 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 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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue