Index: src/java/org/apache/solr/search/ValueSourceParser.java =================================================================== --- src/java/org/apache/solr/search/ValueSourceParser.java (revision 0) +++ src/java/org/apache/solr/search/ValueSourceParser.java (revision 0) @@ -0,0 +1,269 @@ +/** + * 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.solr.search; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.search.Query; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.search.function.BoostedQuery; +import org.apache.solr.search.function.DivFloatFunction; +import org.apache.solr.search.function.DocValues; +import org.apache.solr.search.function.LinearFloatFunction; +import org.apache.solr.search.function.MaxFloatFunction; +import org.apache.solr.search.function.OrdFieldSource; +import org.apache.solr.search.function.PowFloatFunction; +import org.apache.solr.search.function.ProductFloatFunction; +import org.apache.solr.search.function.QueryValueSource; +import org.apache.solr.search.function.RangeMapFloatFunction; +import org.apache.solr.search.function.ReciprocalFloatFunction; +import org.apache.solr.search.function.ReverseOrdFieldSource; +import org.apache.solr.search.function.ScaleFloatFunction; +import org.apache.solr.search.function.SimpleFloatFunction; +import org.apache.solr.search.function.SumFloatFunction; +import org.apache.solr.search.function.ValueSource; +import org.apache.solr.util.plugin.NamedListInitializedPlugin; + +public abstract class ValueSourceParser implements NamedListInitializedPlugin +{ + public abstract void init( NamedList args ); + + public abstract ValueSource parse(FunctionQParser fp) throws ParseException; + + /* standard functions */ + public static Map standardValueSourceParsers = new HashMap(); + static { + standardValueSourceParsers.put("ord", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + String field = fp.getStrParser().getId(); + return new OrdFieldSource(field); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("rord", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + String field = fp.getStrParser().getId(); + return new ReverseOrdFieldSource(field); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("linear", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + fp.getStrParser().expect(","); + float slope = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float intercept = fp.getStrParser().getFloat(); + return new LinearFloatFunction(source,slope,intercept); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("max", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + fp.getStrParser().expect(","); + float val = fp.getStrParser().getFloat(); + return new MaxFloatFunction(source,val); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("recip", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + fp.getStrParser().expect(","); + float m = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float a = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float b = fp.getStrParser().getFloat(); + return new ReciprocalFloatFunction(source,m,a,b); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("scale", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + fp.getStrParser().expect(","); + float min = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float max = fp.getStrParser().getFloat(); + return new ScaleFloatFunction(source,min,max); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("pow", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource a = fp.parseValSource(); + fp.getStrParser().expect(","); + ValueSource b = fp.parseValSource(); + return new PowFloatFunction(a,b); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("div", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource a = fp.parseValSource(); + fp.getStrParser().expect(","); + ValueSource b = fp.parseValSource(); + return new DivFloatFunction(a,b); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("map", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + fp.getStrParser().expect(","); + float min = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float max = fp.getStrParser().getFloat(); + fp.getStrParser().expect(","); + float target = fp.getStrParser().getFloat(); + return new RangeMapFloatFunction(source,min,max,target); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("sqrt", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + return new SimpleFloatFunction(source) { + protected String name() { + return "sqrt"; + } + protected float func(int doc, DocValues vals) { + return (float)Math.sqrt(vals.floatVal(doc)); + } + }; + } + public void init(NamedList args) { + } + }); + standardValueSourceParsers.put("log", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + return new SimpleFloatFunction(source) { + protected String name() { + return "log"; + } + protected float func(int doc, DocValues vals) { + return (float)Math.log10(vals.floatVal(doc)); + } + }; + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("abs", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + ValueSource source = fp.parseValSource(); + return new SimpleFloatFunction(source) { + protected String name() { + return "abs"; + } + protected float func(int doc, DocValues vals) { + return (float)Math.abs(vals.floatVal(doc)); + } + }; + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("sum", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + List sources = fp.parseValueSourceList(); + return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()])); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("product", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + List sources = fp.parseValueSourceList(); + return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()])); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("query", new ValueSourceParser() { + // boost(query($q),rating) + public ValueSource parse(FunctionQParser fp) throws ParseException { + Query q = fp.getNestedQuery(); + float defVal = 0.0f; + if (fp.getStrParser().opt(",")) { + defVal = fp.getStrParser().getFloat(); + } + return new QueryValueSource(q, defVal); + } + + public void init(NamedList args) { + } + + }); + standardValueSourceParsers.put("boost", new ValueSourceParser() { + public ValueSource parse(FunctionQParser fp) throws ParseException { + Query q = fp.getNestedQuery(); + fp.getStrParser().expect(","); + ValueSource vs = fp.parseValSource(); + BoostedQuery bq = new BoostedQuery(q, vs); +System.out.println("Constructed Boostedquery " + bq); + return new QueryValueSource(bq, 0.0f); + } + + public void init(NamedList args) { + } + + }); + } + +} Index: src/java/org/apache/solr/search/FunctionQParser.java =================================================================== --- src/java/org/apache/solr/search/FunctionQParser.java (revision 611544) +++ src/java/org/apache/solr/search/FunctionQParser.java (working copy) @@ -29,12 +29,17 @@ import java.util.Map; public class FunctionQParser extends QParser { + + private QueryParsing.StrParser sp; + public FunctionQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); } - QueryParsing.StrParser sp; - + public QueryParsing.StrParser getStrParser() { + return sp; + } + public Query parse() throws ParseException { sp = new QueryParsing.StrParser(getString()); ValueSource vs = parseValSource(); @@ -52,167 +57,7 @@ return new FunctionQuery(vs); } - private abstract static class VSParser { - abstract ValueSource parse(FunctionQParser fp) throws ParseException; - } - - private static Map vsParsers = new HashMap(); - static { - vsParsers.put("ord", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - String field = fp.sp.getId(); - return new OrdFieldSource(field); - } - }); - vsParsers.put("rord", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - String field = fp.sp.getId(); - return new ReverseOrdFieldSource(field); - } - }); - vsParsers.put("linear", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - fp.sp.expect(","); - float slope = fp.sp.getFloat(); - fp.sp.expect(","); - float intercept = fp.sp.getFloat(); - return new LinearFloatFunction(source,slope,intercept); - } - }); - vsParsers.put("max", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - fp.sp.expect(","); - float val = fp.sp.getFloat(); - return new MaxFloatFunction(source,val); - } - }); - vsParsers.put("recip", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - fp.sp.expect(","); - float m = fp.sp.getFloat(); - fp.sp.expect(","); - float a = fp.sp.getFloat(); - fp.sp.expect(","); - float b = fp.sp.getFloat(); - return new ReciprocalFloatFunction(source,m,a,b); - } - }); - vsParsers.put("scale", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - fp.sp.expect(","); - float min = fp.sp.getFloat(); - fp.sp.expect(","); - float max = fp.sp.getFloat(); - return new ScaleFloatFunction(source,min,max); - } - }); - vsParsers.put("pow", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource a = fp.parseValSource(); - fp.sp.expect(","); - ValueSource b = fp.parseValSource(); - return new PowFloatFunction(a,b); - } - }); - vsParsers.put("div", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource a = fp.parseValSource(); - fp.sp.expect(","); - ValueSource b = fp.parseValSource(); - return new DivFloatFunction(a,b); - } - }); - vsParsers.put("map", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - fp.sp.expect(","); - float min = fp.sp.getFloat(); - fp.sp.expect(","); - float max = fp.sp.getFloat(); - fp.sp.expect(","); - float target = fp.sp.getFloat(); - return new RangeMapFloatFunction(source,min,max,target); - } - }); - vsParsers.put("sqrt", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - return new SimpleFloatFunction(source) { - protected String name() { - return "sqrt"; - } - protected float func(int doc, DocValues vals) { - return (float)Math.sqrt(vals.floatVal(doc)); - } - }; - } - }); - vsParsers.put("log", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - return new SimpleFloatFunction(source) { - protected String name() { - return "log"; - } - protected float func(int doc, DocValues vals) { - return (float)Math.log10(vals.floatVal(doc)); - } - }; - } - }); - vsParsers.put("abs", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - ValueSource source = fp.parseValSource(); - return new SimpleFloatFunction(source) { - protected String name() { - return "abs"; - } - protected float func(int doc, DocValues vals) { - return (float)Math.abs(vals.floatVal(doc)); - } - }; - } - }); - vsParsers.put("sum", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - List sources = fp.parseValueSourceList(); - return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()])); - } - }); - vsParsers.put("product", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - List sources = fp.parseValueSourceList(); - return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()])); - } - }); - vsParsers.put("query", new VSParser() { - // boost(query($q),rating) - ValueSource parse(FunctionQParser fp) throws ParseException { - Query q = fp.getNestedQuery(); - float defVal = 0.0f; - if (fp.sp.opt(",")) { - defVal = fp.sp.getFloat(); - } - return new QueryValueSource(q, defVal); - } - }); - vsParsers.put("boost", new VSParser() { - ValueSource parse(FunctionQParser fp) throws ParseException { - Query q = fp.getNestedQuery(); - fp.sp.expect(","); - ValueSource vs = fp.parseValSource(); - BoostedQuery bq = new BoostedQuery(q, vs); -System.out.println("Constructed Boostedquery " + bq); - return new QueryValueSource(bq, 0.0f); - } - }); - } - - private List parseValueSourceList() throws ParseException { + public List parseValueSourceList() throws ParseException { List sources = new ArrayList(3); for (;;) { sources.add(parseValSource()); @@ -223,7 +68,7 @@ return sources; } - private ValueSource parseValSource() throws ParseException { + public ValueSource parseValSource() throws ParseException { int ch = sp.peek(); if (ch>='0' && ch<='9' || ch=='.' || ch=='+' || ch=='-') { return new ConstValueSource(sp.getFloat()); @@ -232,7 +77,7 @@ String id = sp.getId(); if (sp.opt("(")) { // a function... look it up. - VSParser argParser = vsParsers.get(id); + ValueSourceParser argParser = req.getCore().getValueSourceParser(id); if (argParser==null) { throw new ParseException("Unknown function " + id + " in FunctionQuery(" + sp + ")"); } @@ -245,7 +90,7 @@ return f.getType().getValueSource(f, this); } - private Query getNestedQuery() throws ParseException { + public Query getNestedQuery() throws ParseException { if (sp.opt("$")) { String param = sp.getId(); sp.pos += param.length(); Index: src/java/org/apache/solr/core/SolrCore.java =================================================================== --- src/java/org/apache/solr/core/SolrCore.java (revision 611544) +++ src/java/org/apache/solr/core/SolrCore.java (working copy) @@ -63,6 +63,7 @@ import org.apache.solr.schema.IndexSchema; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.ValueSourceParser; import org.apache.solr.update.DirectUpdateHandler; import org.apache.solr.update.SolrIndexWriter; import org.apache.solr.update.UpdateHandler; @@ -329,6 +330,7 @@ initWriters(); initQParsers(); + initValueSourceParsers(); this.searchComponents = loadSearchComponents( config ); @@ -1038,6 +1040,36 @@ if (plugin != null) return plugin; throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown query type '"+parserName+"'"); } + + private final HashMap valueSourceParsers = new HashMap(); + + /** Configure the ValueSource (function) plugins */ + private void initValueSourceParsers() { + String xpath = "valueSourceParser"; + NodeList nodes = (NodeList) solrConfig.evaluate(xpath, XPathConstants.NODESET); + + NamedListPluginLoader loader = + new NamedListPluginLoader( "[solrconfig.xml] "+xpath, valueSourceParsers); + + loader.load( solrConfig.getResourceLoader(), nodes ); + + // default value source parsers + for (Map.Entry entry : ValueSourceParser.standardValueSourceParsers.entrySet()) { + try { + String name = entry.getKey(); + ValueSourceParser valueSourceParser = entry.getValue(); + valueSourceParsers.put(name, valueSourceParser); + valueSourceParser.init(null); + } catch (Exception e) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e); + } + } + } + + public ValueSourceParser getValueSourceParser(String parserName) { + return valueSourceParsers.get(parserName); + } + }