package org.apache.solr.search; import java.util.ArrayList; import java.util.List; import org.apache.lucene.queries.function.FunctionQuery; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.valuesource.ConstValueSource; import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource; import org.apache.lucene.queries.function.valuesource.LiteralValueSource; import org.apache.lucene.queries.function.valuesource.QueryValueSource; import org.apache.lucene.queryparser.flexible.aqp.NestedParseException; import org.apache.lucene.queryparser.flexible.aqp.nodes.AqpFunctionQueryNode; import org.apache.lucene.queryparser.flexible.aqp.processors.AqpQProcessor.OriginalInput; import org.apache.lucene.queryparser.flexible.core.builders.QueryTreeBuilder; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.search.Query; import org.apache.solr.common.params.SolrParams; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.schema.SchemaField; public class AqpFunctionQParser extends FunctionQParser { private static final String TAGID = QueryTreeBuilder.QUERY_TREE_BUILDER_TAGID.toLowerCase(); public AqpFunctionQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); } private int currChild = -1; private AqpFunctionQueryNode qNode = null; protected boolean canConsume() { return currChild+1 <= qNode.getFuncValues().size()-1; } protected OriginalInput consume() { currChild++; try { return qNode.getFuncValues().get(currChild); } catch (Exception e) { throw new NestedParseException("Function tried to get a new argument, but none is available" + qNode.toString()); } } protected String consumeAsString() { OriginalInput qn = consume(); return qn.value; } @Override protected ValueSource parseValueSource(boolean doConsumeDelimiter) throws SyntaxError { // check if there is a query already built inside our node OriginalInput node = consume(); String input = node.value; if (input.substring(0, 1).equals("\"") || input.substring(0, 1).equals("\'")) { return new LiteralValueSource(input); } else if (input.substring(0,1).equals("$")) { String val = getParam(input); if (val == null) { throw new SyntaxError("Missing param " + input + " while parsing function '" + val + "'"); } QParser subParser = subQuery(val, "func"); if (subParser instanceof FunctionQParser) { ((FunctionQParser)subParser).setParseMultipleSources(true); } Query subQuery = subParser.getQuery(); if (subQuery instanceof FunctionQuery) { return ((FunctionQuery) subQuery).getValueSource(); } else { return new QueryValueSource(subQuery, 0.0f); } } else if (req != null && req.getSchema().getField(input) != null) { SchemaField f = req.getSchema().getField(input); return f.getType().getValueSource(f, this); } StrParser p = new StrParser(input); try { Number num = p.getNumber(); if (num instanceof Long) { return new LongConstValueSource(num.longValue()); } else if (num instanceof Double) { return new DoubleConstValueSource(num.doubleValue()); } else { // shouldn't happen return new ConstValueSource(num.floatValue()); } } catch (NumberFormatException e) { return new LiteralValueSource(input); } } public void setQueryNode(AqpFunctionQueryNode node) { this.qNode = node; if (node.getOriginalInput() != null) { sp = new StrParser(node.getOriginalInput().value); } } public QueryNode getQueryNode() { return qNode; } public String parseId() throws SyntaxError { return consumeAsString(); } public int parseInt() { return Integer.valueOf(consumeAsString()); } public Float parseFloat() throws SyntaxError { String str = consumeAsString(); if (argWasQuoted()) throw new SyntaxError("Expected float instead of quoted string:" + str); float value = Float.parseFloat(str); return value; } public double parseDouble() throws SyntaxError { String str = consumeAsString(); if (argWasQuoted()) throw new SyntaxError("Expected double instead of quoted string:" + str); double value = Double.parseDouble(str); return value; } public List<ValueSource> parseValueSourceList() throws SyntaxError { List<ValueSource> sources = new ArrayList<ValueSource>(3); while (canConsume()) { sources.add(parseValueSource(true)); } return sources; } public Query parseNestedQuery() throws SyntaxError { OriginalInput node = consume(); QParser parser = subQuery(node.value, null); // use the default parser return parser.getQuery(); } public boolean hasMoreArguments() { return canConsume(); } }