You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by yo...@apache.org on 2009/11/20 16:09:38 UTC

svn commit: r882596 - in /lucene/solr/trunk: ./ src/java/org/apache/solr/search/ src/java/org/apache/solr/search/function/ src/test/org/apache/solr/search/function/

Author: yonik
Date: Fri Nov 20 15:09:37 2009
New Revision: 882596

URL: http://svn.apache.org/viewvc?rev=882596&view=rev
Log:
SOLR-1574: simplify specification of builtin functions, add many from java Math

Removed:
    lucene/solr/trunk/src/java/org/apache/solr/search/function/DegreeFunction.java
    lucene/solr/trunk/src/java/org/apache/solr/search/function/RadianFunction.java
Modified:
    lucene/solr/trunk/CHANGES.txt
    lucene/solr/trunk/src/java/org/apache/solr/search/ValueSourceParser.java
    lucene/solr/trunk/src/test/org/apache/solr/search/function/TestFunctionQuery.java

Modified: lucene/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?rev=882596&r1=882595&r2=882596&view=diff
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Fri Nov 20 15:09:37 2009
@@ -42,6 +42,8 @@
   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
 ----------------------
 

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/ValueSourceParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/ValueSourceParser.java?rev=882596&r1=882595&r2=882596&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/ValueSourceParser.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/ValueSourceParser.java Fri Nov 20 15:09:37 2009
@@ -19,6 +19,7 @@
 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 @@
  * 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 @@
     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 @@
         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 @@
         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 @@
           }
 
           protected float func(int doc, DocValues vals) {
-            return (float) Math.abs(vals.floatVal(doc));
+            return Math.abs(vals.floatVal(doc));
           }
         };
       }
@@ -202,12 +178,16 @@
         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 @@
       }
     });
 
-
-    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 @@
       }
     });
     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 @@
     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);
+    }
+  }
+
+}

Modified: lucene/solr/trunk/src/test/org/apache/solr/search/function/TestFunctionQuery.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/search/function/TestFunctionQuery.java?rev=882596&r1=882595&r2=882596&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/search/function/TestFunctionQuery.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/search/function/TestFunctionQuery.java Fri Nov 20 15:09:37 2009
@@ -278,7 +278,7 @@
     }
   }
 
-  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 @@
     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 @@
     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));
+  }
+
+
 }
\ No newline at end of file