Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 1363681) +++ CHANGES.txt (working copy) @@ -3,6 +3,14 @@ For more information on past and future Lucene versions, please see: http://s.apache.org/luceneversions +======================= Lucene 3.6.2 ================ + +Bug Fixes + + * LUCENE-4109: BooleanQueries are not parsed correctly with the + flexible query parser. (Karsten Rauch via Robert Muir) + + ======================= Lucene 3.6.1 ================ Bug Fixes Index: queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java =================================================================== --- queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java (revision 1363681) +++ queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java (working copy) @@ -469,6 +469,15 @@ assertQueryEquals(".NET", a, ".NET"); } + public void testGroup() throws Exception { + assertQueryEquals("!(a AND b) OR c", null, "-(+a +b) c"); + assertQueryEquals("!(a AND b) AND c", null, "-(+a +b) +c"); + assertQueryEquals("((a AND b) AND c)", null, "+(+a +b) +c"); + assertQueryEquals("(a AND b) AND c", null, "+(+a +b) +c"); + assertQueryEquals("b !(a AND b)", null, "b -(+a +b)"); + assertQueryEquals("(a AND b)^4 OR c", null, "((+a +b)^4.0) c"); + } + public void testSlop() throws Exception { assertQueryEquals("\"term germ\"~2", null, "\"term germ\"~2"); Index: queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java =================================================================== --- queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java (revision 1363681) +++ queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java (working copy) @@ -35,6 +35,7 @@ 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.UnescapedCharSequence; import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys; import org.apache.lucene.queryParser.standard.nodes.RangeQueryNode; @@ -60,6 +61,7 @@ * @see ParametricRangeQueryNode */ public class ParametricRangeQueryNodeProcessor extends QueryNodeProcessorImpl { + final public static String OPEN_RANGE_TOKEN = "*"; public ParametricRangeQueryNodeProcessor() { // empty constructor @@ -103,6 +105,8 @@ inclusive = true; } + boolean dateRangeQuery = false; + String part1 = lower.getTextAsString(); String part2 = upper.getTextAsString(); @@ -130,17 +134,36 @@ // pre-1.9 Lucene versions. part1 = DateField.dateToString(d1); part2 = DateField.dateToString(d2); - + dateRangeQuery = true; } else { part1 = DateTools.dateToString(d1, dateRes); part2 = DateTools.dateToString(d2, dateRes); + dateRangeQuery = true; } } catch (Exception e) { // do nothing } + if(dateRangeQuery){ + lower.setText(part1); + upper.setText(part2); + } else { //LUCENE-3338: in Version 4.X there is a Class OpenRangeQueryNodeProcessor for this purpose + CharSequence lowerText = lower.getText(); + CharSequence upperText = upper.getText(); + if (OPEN_RANGE_TOKEN.equals(upper.getTextAsString()) + && (!(upperText instanceof UnescapedCharSequence) || !((UnescapedCharSequence) upperText) + .wasEscaped(0))) { + upperText = null; + } + + if (OPEN_RANGE_TOKEN.equals(lower.getTextAsString()) + && (!(lowerText instanceof UnescapedCharSequence) || !((UnescapedCharSequence) lowerText) + .wasEscaped(0))) { + lowerText = null; + } + lower.setText(lowerText); + upper.setText(upperText); + } - lower.setText(part1); - upper.setText(part2); return new RangeQueryNode(lower, upper, collator); Index: queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java =================================================================== --- queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java (revision 1363681) +++ queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java (working copy) @@ -59,7 +59,7 @@ add(new AllowLeadingWildcardProcessor()); add(new AnalyzerQueryNodeProcessor()); add(new PhraseSlopQueryNodeProcessor()); - add(new GroupQueryNodeProcessor()); + add(new BooleanQuery2ModifierNodeProcessor()); add(new NoChildOptimizationQueryNodeProcessor()); add(new RemoveDeletedQueryNodesProcessor()); add(new RemoveEmptyNonLeafQueryNodeProcessor()); Index: queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java =================================================================== --- queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java (revision 0) +++ queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java (working copy) @@ -0,0 +1,202 @@ +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.util.ArrayList; +import java.util.List; + +import org.apache.lucene.queryParser.core.QueryNodeException; +import org.apache.lucene.queryParser.core.config.QueryConfigHandler; +import org.apache.lucene.queryParser.core.nodes.AndQueryNode; +import org.apache.lucene.queryParser.core.nodes.BooleanQueryNode; +import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode; +import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode.Modifier; +import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.core.processors.QueryNodeProcessor; +import org.apache.lucene.queryParser.precedence.processors.BooleanModifiersQueryNodeProcessor; +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; +import org.apache.lucene.queryParser.standard.nodes.BooleanModifierNode; +import org.apache.lucene.queryParser.standard.parser.StandardSyntaxParser; + +/** + *

+ * This processor is used to apply the correct {@link ModifierQueryNode} to + * {@link BooleanQueryNode}s children. This is a variant of + * {@link BooleanModifiersQueryNodeProcessor} which ignores precedence. + *

+ *

+ * The {@link StandardSyntaxParser} knows the rules of precedence, but lucene + * does not. e.g. (A AND B OR C AND D) ist treated like + * (+A +B +C +D). + *

+ *

+ * This processor walks through the query node tree looking for + * {@link BooleanQueryNode}s. If an {@link AndQueryNode} is found, every child, + * which is not a {@link ModifierQueryNode} or the {@link ModifierQueryNode} is + * {@link Modifier#MOD_NONE}, becomes a {@link Modifier#MOD_REQ}. For default + * {@link BooleanQueryNode}, it checks the default operator is + * {@link Operator#AND}, if it is, the same operation when an + * {@link AndQueryNode} is found is applied to it. Each {@link BooleanQueryNode} + * which direct parent is also a {@link BooleanQueryNode} is removed (to ignore + * the rules of precidence). + *

+ * + * @see ConfigurationKeys#DEFAULT_OPERATOR + * @see BooleanModifiersQueryNodeProcessor + */ +public class BooleanQuery2ModifierNodeProcessor implements QueryNodeProcessor { + final static String TAG_REMOVE = "remove"; + final static String TAG_MODIFIER = "wrapWithModifier"; + final static String TAG_BOOLEAN_ROOT = "booleanRoot"; + + QueryConfigHandler queryConfigHandler; + + private final ArrayList childrenBuffer = new ArrayList(); + + private Boolean usingAnd = false; + + public BooleanQuery2ModifierNodeProcessor() { + // empty constructor + } + + @Override + public QueryNode process(QueryNode queryTree) throws QueryNodeException { + Operator op = getQueryConfigHandler().get( + ConfigurationKeys.DEFAULT_OPERATOR); + + if (op == null) { + throw new IllegalArgumentException( + "StandardQueryConfigHandler.ConfigurationKeys.DEFAULT_OPERATOR should be set on the QueryConfigHandler"); + } + + this.usingAnd = StandardQueryConfigHandler.Operator.AND == op; + + return processIteration(queryTree); + + } + + protected void processChildren(QueryNode queryTree) throws QueryNodeException { + List children = queryTree.getChildren(); + if (children != null && children.size() > 0) { + for (QueryNode child : children) { + child = processIteration(child); + } + } + } + + private QueryNode processIteration(QueryNode queryTree) + throws QueryNodeException { + queryTree = preProcessNode(queryTree); + + processChildren(queryTree); + + queryTree = postProcessNode(queryTree); + + return queryTree; + + } + + protected void fillChildrenBufferAndApplyModifiery(QueryNode parent) { + for (QueryNode node : parent.getChildren()) { + if (node.containsTag(TAG_REMOVE)) { + fillChildrenBufferAndApplyModifiery(node); + } else if (node.containsTag(TAG_MODIFIER)) { + childrenBuffer.add(applyModifier(node, + (Modifier) node.getTag(TAG_MODIFIER))); + } else { + childrenBuffer.add(node); + } + } + } + + protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException { + if (node.containsTag(TAG_BOOLEAN_ROOT)) { + this.childrenBuffer.clear(); + fillChildrenBufferAndApplyModifiery(node); + node.set(childrenBuffer); + } + return node; + + } + + protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException { + QueryNode parent = node.getParent(); + if (node instanceof BooleanQueryNode) { + if (parent instanceof BooleanQueryNode) { + node.setTag(TAG_REMOVE, Boolean.TRUE); // no precedence + } else { + node.setTag(TAG_BOOLEAN_ROOT, Boolean.TRUE); + } + } else if (parent instanceof BooleanQueryNode) { + if ((parent instanceof AndQueryNode) + || (usingAnd && isDefaultBooleanQueryNode(parent))) { + tagModifierButDoNotOverride(node, ModifierQueryNode.Modifier.MOD_REQ); + } + } + return node; + } + + protected boolean isDefaultBooleanQueryNode(QueryNode toTest) { + return toTest != null && BooleanQueryNode.class.equals(toTest.getClass()); + } + + private QueryNode applyModifier(QueryNode node, Modifier mod) { + + // check if modifier is not already defined and is default + if (!(node instanceof ModifierQueryNode)) { + return new BooleanModifierNode(node, mod); + + } else { + ModifierQueryNode modNode = (ModifierQueryNode) node; + + if (modNode.getModifier() == Modifier.MOD_NONE) { + return new ModifierQueryNode(modNode.getChild(), mod); + } + + } + + return node; + + } + + protected void tagModifierButDoNotOverride(QueryNode node, Modifier mod) { + if (node instanceof ModifierQueryNode) { + ModifierQueryNode modNode = (ModifierQueryNode) node; + if (modNode.getModifier() == Modifier.MOD_NONE) { + node.setTag(TAG_MODIFIER, mod); + } + } else { + node.setTag(TAG_MODIFIER, ModifierQueryNode.Modifier.MOD_REQ); + } + } + + @Override + public void setQueryConfigHandler(QueryConfigHandler queryConfigHandler) { + this.queryConfigHandler = queryConfigHandler; + + } + + @Override + public QueryConfigHandler getQueryConfigHandler() { + return queryConfigHandler; + } + +} + Index: queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java =================================================================== --- queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java (revision 1363681) +++ queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java (working copy) @@ -19,12 +19,14 @@ import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode; import org.apache.lucene.queryParser.core.nodes.QueryNode; +import org.apache.lucene.queryParser.standard.processors.BooleanQuery2ModifierNodeProcessor; import org.apache.lucene.queryParser.standard.processors.GroupQueryNodeProcessor; /** * A {@link BooleanModifierNode} has the same behaviour as * {@link ModifierQueryNode}, it only indicates that this modifier was added by - * {@link GroupQueryNodeProcessor} and not by the user.
+ * {@link GroupQueryNodeProcessor} or {@link BooleanQuery2ModifierNodeProcessor} + * and not by the user.
* * @see ModifierQueryNode */