SOLR-1574: simplify specification of builtin functions, add many from java Math

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@882596 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2009-11-20 15:09:37 +00:00
parent 451034b2e9
commit 1ac71a2aaf
5 changed files with 376 additions and 190 deletions

View File

@ -42,6 +42,8 @@ New Features
fielded queries, improved proximity boosting, and improved stopword
handling. (yonik)
* SOLR-1574: Add many new functions from java Math (e.g. sin, cos) (yonik)
Optimizations
----------------------

View File

@ -19,6 +19,7 @@ package org.apache.solr.search;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.schema.DateField;
@ -47,13 +48,11 @@ import java.util.Map;
* Intented usage is to create pluggable, named functions for use in function queries.
*/
public abstract class ValueSourceParser implements NamedListInitializedPlugin {
/**
* Initialize the plugin.
*/
public void init(NamedList args) {}
/**
* Parse the user input into a ValueSource.
*
@ -72,6 +71,17 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
return standardValueSourceParsers.put(name, p);
}
/** Adds a new parser for the name and returns any existing one that was overriden.
* This is not thread safe.
*/
public static ValueSourceParser addParser(NamedParser p) {
return standardValueSourceParsers.put(p.name(), p);
}
private static void alias(String source, String dest) {
standardValueSourceParsers.put(dest, standardValueSourceParsers.get(source));
}
static {
addParser("ord", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
@ -130,13 +140,6 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
return new TopValueSource(new ScaleFloatFunction(source, min, max));
}
});
addParser("pow", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource a = fp.parseValueSource();
ValueSource b = fp.parseValueSource();
return new PowFloatFunction(a, b);
}
});
addParser("div", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource a = fp.parseValueSource();
@ -154,34 +157,7 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
return new RangeMapFloatFunction(source, min, max, target, def);
}
});
addParser("sqrt", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource source = fp.parseValueSource();
return new SimpleFloatFunction(source) {
protected String name() {
return "sqrt";
}
protected float func(int doc, DocValues vals) {
return (float) Math.sqrt(vals.floatVal(doc));
}
};
}
});
addParser("log", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource source = fp.parseValueSource();
return new SimpleFloatFunction(source) {
protected String name() {
return "log";
}
protected float func(int doc, DocValues vals) {
return (float) Math.log10(vals.floatVal(doc));
}
};
}
});
addParser("abs", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource source = fp.parseValueSource();
@ -191,7 +167,7 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
}
protected float func(int doc, DocValues vals) {
return (float) Math.abs(vals.floatVal(doc));
return Math.abs(vals.floatVal(doc));
}
};
}
@ -202,12 +178,16 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
return new SumFloatFunction(sources.toArray(new ValueSource[sources.size()]));
}
});
alias("sum","add");
addParser("product", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
List<ValueSource> sources = fp.parseValueSourceList();
return new ProductFloatFunction(sources.toArray(new ValueSource[sources.size()]));
}
});
alias("product","mul");
addParser("sub", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
ValueSource a = fp.parseValueSource();
@ -276,16 +256,114 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
}
});
addParser("rad", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new RadianFunction(fp.parseValueSource());
addParser(new DoubleParser("rad") {
public double func(int doc, DocValues vals) {
return Math.toRadians(vals.doubleVal(doc));
}
});
addParser("deg", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new DegreeFunction(fp.parseValueSource());
addParser(new DoubleParser("deg") {
public double func(int doc, DocValues vals) {
return Math.toDegrees(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("sqrt") {
public double func(int doc, DocValues vals) {
return Math.sqrt(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("cbrt") {
public double func(int doc, DocValues vals) {
return Math.cbrt(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("log") {
public double func(int doc, DocValues vals) {
return Math.log10(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("ln") {
public double func(int doc, DocValues vals) {
return Math.log(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("exp") {
public double func(int doc, DocValues vals) {
return Math.exp(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("sin") {
public double func(int doc, DocValues vals) {
return Math.sin(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("cos") {
public double func(int doc, DocValues vals) {
return Math.cos(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("tan") {
public double func(int doc, DocValues vals) {
return Math.tan(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("asin") {
public double func(int doc, DocValues vals) {
return Math.asin(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("acos") {
public double func(int doc, DocValues vals) {
return Math.acos(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("atan") {
public double func(int doc, DocValues vals) {
return Math.atan(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("sinh") {
public double func(int doc, DocValues vals) {
return Math.sinh(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("cosh") {
public double func(int doc, DocValues vals) {
return Math.cosh(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("tanh") {
public double func(int doc, DocValues vals) {
return Math.tanh(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("ceil") {
public double func(int doc, DocValues vals) {
return Math.ceil(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("floor") {
public double func(int doc, DocValues vals) {
return Math.floor(vals.doubleVal(doc));
}
});
addParser(new DoubleParser("rint") {
public double func(int doc, DocValues vals) {
return Math.rint(vals.doubleVal(doc));
}
});
addParser(new Double2Parser("pow") {
public double func(int doc, DocValues a, DocValues b) {
return Math.pow(a.doubleVal(doc), b.doubleVal(doc));
}
});
addParser(new Double2Parser("hypot") {
public double func(int doc, DocValues a, DocValues b) {
return Math.hypot(a.doubleVal(doc), b.doubleVal(doc));
}
});
addParser(new Double2Parser("atan2") {
public double func(int doc, DocValues a, DocValues b) {
return Math.atan2(a.doubleVal(doc), b.doubleVal(doc));
}
});
@ -319,9 +397,21 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
}
});
addParser("ms", new DateValueSourceParser());
addParser("pi", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new DoubleConstValueSource(Math.PI);
}
});
addParser("e", new ValueSourceParser() {
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new DoubleConstValueSource(Math.E);
}
});
}
protected void splitSources(int dim, List<ValueSource> sources, List<ValueSource> dest1, List<ValueSource> dest2) {
private static void splitSources(int dim, List<ValueSource> sources, List<ValueSource> dest1, List<ValueSource> dest2) {
//Get dim value sources for the first vector
for (int i = 0; i < dim; i++) {
dest1.add(sources.get(i));
@ -483,3 +573,193 @@ class LongConstValueSource extends ValueSource {
return this.constant == other.constant;
}
}
// Private for now - we need to revisit how to handle typing in function queries
class DoubleConstValueSource extends ValueSource {
final double constant;
public DoubleConstValueSource(double constant) {
this.constant = constant;
}
public String description() {
return "const(" + constant + ")";
}
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new DocValues() {
public float floatVal(int doc) {
return (float)constant;
}
public int intVal(int doc) {
return (int) constant;
}
public long longVal(int doc) {
return (long)constant;
}
public double doubleVal(int doc) {
return constant;
}
public String strVal(int doc) {
return Double.toString(constant);
}
public String toString(int doc) {
return description();
}
};
}
public int hashCode() {
long bits = Double.doubleToRawLongBits(constant);
return (int)(bits ^ (bits >>> 32));
}
public boolean equals(Object o) {
if (DoubleConstValueSource.class != o.getClass()) return false;
DoubleConstValueSource other = (DoubleConstValueSource) o;
return this.constant == other.constant;
}
}
abstract class NamedParser extends ValueSourceParser {
private final String name;
public NamedParser(String name) {
this.name = name;
}
public String name() {
return name;
}
}
abstract class DoubleParser extends NamedParser {
public DoubleParser(String name) {
super(name);
}
public abstract double func(int doc, DocValues vals);
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new Function(fp.parseValueSource());
}
class Function extends SingleFunction {
public Function(ValueSource source) {
super(source);
}
public String name() {
return DoubleParser.this.name();
}
@Override
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return (float)doubleVal(doc);
}
public int intVal(int doc) {
return (int)doubleVal(doc);
}
public long longVal(int doc) {
return (long)doubleVal(doc);
}
public double doubleVal(int doc) {
return func(doc, vals);
}
public String strVal(int doc) {
return Double.toString(doubleVal(doc));
}
public String toString(int doc) {
return name() + '(' + vals.toString(doc) + ')';
}
};
}
}
}
abstract class Double2Parser extends NamedParser {
public Double2Parser(String name) {
super(name);
}
public abstract double func(int doc, DocValues a, DocValues b);
public ValueSource parse(FunctionQParser fp) throws ParseException {
return new Function(fp.parseValueSource(), fp.parseValueSource());
}
class Function extends ValueSource {
private final ValueSource a;
private final ValueSource b;
/**
* @param a the base.
* @param b the exponent.
*/
public Function(ValueSource a, ValueSource b) {
this.a = a;
this.b = b;
}
public String description() {
return name() + "(" + a.description() + "," + b.description() + ")";
}
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues aVals = a.getValues(context, reader);
final DocValues bVals = b.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return (float)doubleVal(doc);
}
public int intVal(int doc) {
return (int)doubleVal(doc);
}
public long longVal(int doc) {
return (long)doubleVal(doc);
}
public double doubleVal(int doc) {
return func(doc, aVals, bVals);
}
public String strVal(int doc) {
return Double.toString(doubleVal(doc));
}
public String toString(int doc) {
return name() + '(' + aVals.toString(doc) + ',' + bVals.toString(doc) + ')';
}
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
a.createWeight(context,searcher);
b.createWeight(context,searcher);
}
public int hashCode() {
int h = a.hashCode();
h ^= (h << 13) | (h >>> 20);
h += b.hashCode();
h ^= (h << 23) | (h >>> 10);
h += name().hashCode();
return h;
}
public boolean equals(Object o) {
if (this.getClass() != o.getClass()) return false;
Function other = (Function)o;
return this.a.equals(other.a)
&& this.b.equals(other.b);
}
}
}

View File

@ -1,63 +0,0 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import java.util.Map;
import java.io.IOException;
/**
*
*
**/
public class DegreeFunction extends ValueSource{
protected ValueSource valSource;
public DegreeFunction(ValueSource valSource) {
this.valSource = valSource;
}
public String description() {
return "deg(" + valSource.description() + ')';
}
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues dv = valSource.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return (float) doubleVal(doc);
}
public int intVal(int doc) {
return (int) doubleVal(doc);
}
public long longVal(int doc) {
return (long) doubleVal(doc);
}
public double doubleVal(int doc) {
return Math.toDegrees(dv.doubleVal(doc));
}
public String strVal(int doc) {
return Double.toString(doubleVal(doc));
}
public String toString(int doc) {
return description() + '=' + floatVal(doc);
}
};
}
public boolean equals(Object o) {
if (o.getClass() != DegreeFunction.class) return false;
DegreeFunction other = (DegreeFunction) o;
return description().equals(other.description()) && valSource.equals(other.valSource);
}
public int hashCode() {
return description().hashCode() + valSource.hashCode();
};
}

View File

@ -1,79 +0,0 @@
package org.apache.solr.search.function;
/**
* 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 org.apache.lucene.index.IndexReader;
import java.io.IOException;
import java.util.Map;
/**
* Take a ValueSourc and produce convert the number to radians and
* return that value
*/
public class RadianFunction extends ValueSource {
protected ValueSource valSource;
public RadianFunction(ValueSource valSource) {
this.valSource = valSource;
}
public String description() {
return "rad(" + valSource.description() + ')';
}
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues dv = valSource.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return (float) doubleVal(doc);
}
public int intVal(int doc) {
return (int) doubleVal(doc);
}
public long longVal(int doc) {
return (long) doubleVal(doc);
}
public double doubleVal(int doc) {
return Math.toRadians(dv.doubleVal(doc));
}
public String strVal(int doc) {
return Double.toString(doubleVal(doc));
}
public String toString(int doc) {
return description() + '=' + floatVal(doc);
}
};
}
public boolean equals(Object o) {
if (o.getClass() != RadianFunction.class) return false;
RadianFunction other = (RadianFunction) o;
return description().equals(other.description()) && valSource.equals(other.valSource);
}
public int hashCode() {
return description().hashCode() + valSource.hashCode();
};
}

View File

@ -278,7 +278,7 @@ public class TestFunctionQuery extends AbstractSolrTestCase {
}
}
public void testGeneral() {
public void testGeneral() throws Exception {
assertU(adoc("id","1", "a_tdt","2009-08-31T12:10:10.123Z", "b_tdt","2009-08-31T12:10:10.124Z"));
assertU(adoc("id","2"));
assertU(commit()); // create more than one segment
@ -326,9 +326,12 @@ public class TestFunctionQuery extends AbstractSolrTestCase {
q ="{!func}sub(div(sum(0.0,product(1,query($qq))),1),0)";
assertQ(req("fl","*,score","q", q, "qq","text:batman", "fq",fq), "//float[@name='score']<'1.0'");
assertQ(req("fl","*,score","q", q, "qq","text:superman", "fq",fq), "//float[@name='score']>'1.0'");
doTestDegreeRads();
doTestFuncs();
}
public void testDegreeRads() throws Exception {
public void doTestDegreeRads() throws Exception {
assertU(adoc("id", "1", "x_td", "0", "y_td", "0"));
assertU(adoc("id", "2", "x_td", "90", "y_td", String.valueOf(Math.PI / 2)));
assertU(adoc("id", "3", "x_td", "45", "y_td", String.valueOf(Math.PI / 4)));
@ -343,4 +346,47 @@ public class TestFunctionQuery extends AbstractSolrTestCase {
assertQ(req("fl", "*,score", "q", "{!func}deg(y_td)", "fq", "id:2"), "//float[@name='score']='90.0'");
assertQ(req("fl", "*,score", "q", "{!func}deg(y_td)", "fq", "id:3"), "//float[@name='score']='45.0'");
}
public void dofunc(String func, double val) throws Exception {
// String sval = Double.toString(val);
String sval = Float.toString((float)val);
assertQ(req("fl", "*,score", "defType","func", "fq","id:1", "q",func),
"//float[@name='score']='" + sval + "'");
}
public void doTestFuncs() throws Exception {
assertU(adoc("id", "1", "foo_d", "9"));
assertU(commit());
dofunc("1.0", 1.0);
dofunc("e()", Math.E);
dofunc("pi()", Math.PI);
dofunc("add(2,3)", 2+3);
dofunc("mul(2,3)", 2*3);
dofunc("rad(45)", Math.toRadians(45));
dofunc("deg(.5)", Math.toDegrees(.5));
dofunc("sqrt(9)", Math.sqrt(9));
dofunc("cbrt(8)", Math.cbrt(8));
dofunc("log(100)", Math.log10(100));
dofunc("ln(3)", Math.log(3));
dofunc("exp(1)", Math.exp(1));
dofunc("sin(.5)", Math.sin(.5));
dofunc("cos(.5)", Math.cos(.5));
dofunc("tan(.5)", Math.tan(.5));
dofunc("asin(.5)", Math.asin(.5));
dofunc("acos(.5)", Math.acos(.5));
dofunc("atan(.5)", Math.atan(.5));
dofunc("sinh(.5)", Math.sinh(.5));
dofunc("cosh(.5)", Math.cosh(.5));
dofunc("tanh(.5)", Math.tanh(.5));
dofunc("ceil(2.3)", Math.ceil(2.3));
dofunc("floor(2.3)", Math.floor(2.3));
dofunc("rint(2.3)", Math.rint(2.3));
dofunc("pow(2,0.5)", Math.pow(2,0.5));
dofunc("hypot(3,4)", Math.hypot(3,4));
dofunc("atan2(.25,.5)", Math.atan2(.25,.5));
}
}