package org.apache.lucene.queryparser.flexible.aqp.builders; import java.util.ArrayList; import java.util.List; import org.apache.lucene.queries.function.FunctionQuery; import org.apache.lucene.queryparser.flexible.messages.MessageImpl; import org.apache.lucene.queryparser.flexible.core.QueryNodeException; import org.apache.lucene.queryparser.flexible.core.builders.QueryTreeBuilder; import org.apache.lucene.queryparser.flexible.core.nodes.OpaqueQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuerySyntaxImpl; import org.apache.lucene.search.Query; import org.apache.solr.search.AqpFunctionQParser; import org.apache.solr.search.SyntaxError; import org.apache.solr.search.ValueSourceParser; import org.apache.lucene.queryparser.flexible.aqp.nodes.AqpANTLRNode; import org.apache.lucene.queryparser.flexible.aqp.nodes.AqpFunctionQueryNode; public class AqpFunctionQueryTreeBuilder extends QueryTreeBuilder implements AqpFunctionQueryBuilder { private ValueSourceParser valueSourceProvider; private AqpFunctionQParser functionQueryParser; public AqpFunctionQueryTreeBuilder(ValueSourceParser provider, AqpFunctionQParser queryParser) { valueSourceProvider = provider; functionQueryParser = queryParser; } public Query build(QueryNode node) throws QueryNodeException { try { return new FunctionQuery(getValueSourceParser().parse(getParser(node))); } catch (SyntaxError e) { throw new QueryNodeException(new MessageImpl(e.getLocalizedMessage())); } } public static void XflattenChildren(QueryNode node) throws QueryNodeException { // we know that for solr functions, the literal values are enough (so we can safely replace // nodes with their literal values - just the nested functions will remain as functions) QueryNode valueNode = node.getChildren().get(1); try { List<String> inputVals = AqpFunctionQueryTreeBuilder.harvestInput(valueNode); List<QueryNode> children = valueNode.getChildren(); for (int i=0; i<inputVals.size();i++) { if (!inputVals.get(i).equals("#QFUNC")) { children.set(i, new OpaqueQueryNode("child#" + i, inputVals.get(i))); } } } catch (Exception e) { throw new QueryNodeException(e); } //valueNode.set(children); } public static void XsimplifyValueNode(QueryNode node) throws QueryNodeException { List<QueryNode> children = node.getChildren(); List<QueryNode> valChildren = children.get(1).getChildren(); children.clear(); children.addAll(valChildren); } public static void XremoveFuncName(QueryNode node) throws QueryNodeException { List<QueryNode> children = node.getChildren(); children.remove(0); } /** * get the raw input from the children, we do not go * into nested QFUNCs, that is intentional, * we see only the immediate level */ public static List<String> harvestInput(QueryNode node) { List<String> rawInput = new ArrayList<String>(); swimDeep(rawInput, node); return rawInput; } public static void swimDeep(List<String> rawInput, QueryNode node) { if (node instanceof AqpANTLRNode) { AqpANTLRNode a = (AqpANTLRNode) node; if (a.getTokenInput() != null) { try { rawInput.add( EscapeQuerySyntaxImpl.discardEscapeChar(a.getTokenInput()).toString() ); } catch (Exception e) { rawInput.add(a.getTokenInput()); } } } else if (node instanceof AqpFunctionQueryNode) { rawInput.add("#QFUNC"); return; } if (!node.isLeaf()) { for (QueryNode child: node.getChildren()) { swimDeep(rawInput, child); } } } private AqpFunctionQParser getParser(QueryNode node) { functionQueryParser.setQueryNode((AqpFunctionQueryNode) node); return functionQueryParser; } private ValueSourceParser getValueSourceParser() { return valueSourceProvider; } public boolean canBeAnalyzed() { return false; } }