/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.queryparser.flexible.standard.processors;

import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.queryparser.flexible.core.QueryNodeException;
import org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler;
import org.apache.lucene.queryparser.flexible.core.nodes.AndQueryNode;
import org.apache.lucene.queryparser.flexible.core.nodes.BooleanQueryNode;
import org.apache.lucene.queryparser.flexible.core.nodes.ModifierQueryNode;
import org.apache.lucene.queryparser.flexible.core.nodes.ModifierQueryNode.Modifier;
import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode;
import org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessor;
import org.apache.lucene.queryparser.flexible.precedence.processors.BooleanModifiersQueryNodeProcessor;
import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler;
import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler.Operator;
import org.apache.lucene.queryparser.flexible.standard.nodes.BooleanModifierNode;
import org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser;

This processor is used to apply the correct ModifierQueryNode to BooleanQueryNodes children. This is a variant of BooleanModifiersQueryNodeProcessor which ignores precedence.

The 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 BooleanQueryNodes. If an AndQueryNode is found, every child, which is not a ModifierQueryNode or the ModifierQueryNode is Modifier.MOD_NONE, becomes a Modifier.MOD_REQ. For default BooleanQueryNode, it checks the default operator is Operator.AND, if it is, the same operation when an AndQueryNode is found is applied to it. Each BooleanQueryNode which direct parent is also a BooleanQueryNode is removed (to ignore the rules of precedence).

See Also:
/** * <p> * 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. * </p> * <p> * The {@link StandardSyntaxParser} knows the rules of precedence, but lucene * does not. e.g. <code>(A AND B OR C AND D)</code> ist treated like * <code>(+A +B +C +D)</code>. * </p> * <p> * 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 precedence). * </p> * * @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<QueryNode> 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<QueryNode> 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; } }