diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java b/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java index 8ee56d0abe2..5af1e5b06df 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/Bindings.java @@ -70,7 +70,7 @@ public abstract class Bindings implements Iterable { if (vs instanceof ExpressionValueSource) { Expression expr = ((ExpressionValueSource)vs).expression; - for (String external : expr.externals) { + for (String external : expr.variables) { if (chain.contains(external)) { throw new IllegalArgumentException("Recursion Error: Cycle detected originating in (" + external + ")"); } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java b/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java index a769d3c89b1..2d9a7f2b46d 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/Expression.java @@ -16,6 +16,7 @@ package org.apache.lucene.expressions; * limitations under the License. */ +import org.apache.lucene.expressions.js.JavascriptCompiler; // javadocs import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.search.SortField; @@ -38,32 +39,33 @@ import org.apache.lucene.search.SortField; * Query query = new TermQuery(new Term("body", "contents")); * searcher.search(query, null, 10, sort); * + * @see JavascriptCompiler#compile * @lucene.experimental */ public abstract class Expression { - /** The original {@link String} expression, before it was compiled */ - public final String expression; + /** The original source text */ + public final String sourceText; - /** The names of external references found in the expression */ - public final String[] externals; + /** Named variables referred to by this expression */ + public final String[] variables; /** - * Creates a new {@code CompiledExpression}. + * Creates a new {@code Expression}. * - * @param expression The expression that was compiled - * @param externals Names of external references found in the expression + * @param sourceText Source text for the expression: e.g. {@code ln(popularity)} + * @param variables Names of external variables referred to by the expression */ - public Expression(String expression, String[] externals) { - this.expression = expression; - this.externals = externals; + protected Expression(String sourceText, String[] variables) { + this.sourceText = sourceText; + this.variables = variables; } /** * Evaluates the expression for the given document. * * @param document docId of the document to compute a value for - * @param functionValues {@link FunctionValues} for each element of {@link #externals}. + * @param functionValues {@link FunctionValues} for each element of {@link #variables}. * @return The computed value of the expression for the given document. */ public abstract double evaluate(int document, FunctionValues[] functionValues); diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java index 464f577a7b8..425e0396870 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionValueSource.java @@ -47,10 +47,10 @@ final class ExpressionValueSource extends ValueSource { if (valuesCache == null) { throw new NullPointerException(); } - FunctionValues[] externalValues = new FunctionValues[expression.externals.length]; + FunctionValues[] externalValues = new FunctionValues[expression.variables.length]; - for (int i = 0; i < expression.externals.length; ++i) { - String externalName = expression.externals[i]; + for (int i = 0; i < expression.variables.length; ++i) { + String externalName = expression.variables[i]; FunctionValues values = valuesCache.get(externalName); if (values == null) { source = bindings.getValueSource(externalName); @@ -68,12 +68,12 @@ final class ExpressionValueSource extends ValueSource { @Override public SortField getSortField(boolean reverse) { - return new ExpressionSortField(expression.expression, this, reverse); + return new ExpressionSortField(expression.sourceText, this, reverse); } @Override public String description() { - return "ExpressionValueSource(" + expression.expression + ")"; + return "ExpressionValueSource(" + expression.sourceText + ")"; } @Override diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java index 23f6a2b9e5f..0c90d16c27c 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ScoreFunctionValues.java @@ -43,6 +43,6 @@ class ScoreFunctionValues extends FunctionValues { @Override public String toString(int document) { - return "ComputedScorerValues(" + document + ": " + doubleVal(document) + ")"; + return "ScoreFunctionValues(" + document + ": " + doubleVal(document) + ")"; } } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java b/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java index f62a8eb2056..7fede15e3a7 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/SimpleBindings.java @@ -33,7 +33,8 @@ import org.apache.lucene.search.FieldCache.LongParser; import org.apache.lucene.search.SortField; /** - * Simple class that binds expression variable names to {@link SortField}s. + * Simple class that binds expression variable names to {@link SortField}s + * or other {@link Expression}s. * * @lucene.experimental */ diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java b/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java index 5931f0936bc..04dfe034fe9 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CharStream; @@ -91,7 +90,15 @@ import static org.objectweb.asm.Opcodes.V1_7; /** * An expression compiler for javascript expressions. - * + *

+ * Example: + *

+ *   Expression foo = JavascriptCompiler.compile("((0.3*popularity)/10.0)+(0.7*score)");
+ * 
+ *

+ * See the {@link org.apache.lucene.expressions.js package documentation} for + * the supported syntax and functions. + * * @lucene.experimental */ public class JavascriptCompiler { @@ -115,8 +122,7 @@ public class JavascriptCompiler { private static final String COMPILED_EXPRESSION_INTERNAL = Type.getInternalName(Expression.class); private static final String FUNCTION_VALUES_INTERNAL = Type.getInternalName(FunctionValues.class); - private Loader loader; - private AtomicLong counter = new AtomicLong(); + private final Loader loader; private String className; private ClassWriter classWriter; @@ -127,17 +133,17 @@ public class JavascriptCompiler { /** * Constructs a compiler for expressions. */ - public JavascriptCompiler() { - this(null); + private JavascriptCompiler() { + loader = new Loader(getClass().getClassLoader()); } /** * Constructs a compiler for expressions that will be loaded using the given class loader as the parent. * @param parent Class loader to load the dynamically compiled expression */ - public JavascriptCompiler(ClassLoader parent) { + private JavascriptCompiler(ClassLoader parent) { if (parent == null) { - parent = getClass().getClassLoader(); + throw new NullPointerException(); } loader = new Loader(parent); } @@ -145,28 +151,43 @@ public class JavascriptCompiler { /** * Compiles the given expression. * - * @param expression The expression to compile + * @param sourceText The expression to compile * @return A new compiled expression * @throws ParseException on failure to compile */ - public static Expression compile(String expression) throws ParseException { - return new JavascriptCompiler().compileExpression(expression); + public static Expression compile(String sourceText) throws ParseException { + return new JavascriptCompiler().compileExpression(sourceText); + } + + /** + * Compiles the given expression, specifying the parent classloader. + * + * @param sourceText The expression to compile + * @param parent Parent classloader + * @return A new compiled expression + * @throws ParseException on failure to compile + */ + public static Expression compile(String sourceText, ClassLoader parent) throws ParseException { + return new JavascriptCompiler(parent).compileExpression(sourceText); } /** * Compiles the given expression. * - * @param expression The expression to compile + * @param sourceText The expression to compile * @return A new compiled expression * @throws ParseException on failure to compile */ - public Expression compileExpression(String expression) throws ParseException { + private Expression compileExpression(String sourceText) throws ParseException { + if (sourceText == null) { + throw new NullPointerException(); + } try { - this.className = "Expr" + Long.toString(counter.incrementAndGet()); + this.className = "CompiledExpression"; externalsMap = new HashMap(); externalsList = new ArrayList(); - Tree antlrTree = getAntlrComputedExpressionTree(expression); + Tree antlrTree = getAntlrComputedExpressionTree(sourceText); beginCompile(); recursiveCompile(antlrTree, ComputedType.DOUBLE); @@ -174,14 +195,8 @@ public class JavascriptCompiler { Class evaluatorClass = loader.define(EXPRESSION_CLASS_PREFIX + className, classWriter.toByteArray()); Constructor constructor = evaluatorClass.getConstructor(String.class, String[].class); - return constructor.newInstance(expression, externalsList.toArray(new String[externalsList.size()])); - } catch (InstantiationException exception) { - throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception); - } catch (IllegalAccessException exception) { - throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception); - } catch (NoSuchMethodException exception) { - throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception); - } catch (InvocationTargetException exception) { + return constructor.newInstance(sourceText, externalsList.toArray(new String[externalsList.size()])); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) { throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception); } } diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/package.html b/lucene/expressions/src/java/org/apache/lucene/expressions/package.html index 8c895a030c8..d4de75db31b 100644 --- a/lucene/expressions/src/java/org/apache/lucene/expressions/package.html +++ b/lucene/expressions/src/java/org/apache/lucene/expressions/package.html @@ -22,13 +22,13 @@

expressions

{@link org.apache.lucene.expressions.Expression} - result of compiling an expression, which can -evaluate it for a given document. Each expression can have external variables which evaluate -will retrieve from passed FunctionValues. +evaluate it for a given document. Each expression can have external variables are resolved by +{@code Bindings}.

{@link org.apache.lucene.expressions.Bindings} - abstraction for binding external variables -to a way to get a value for those variables for a particular document (ValueSource) +to a way to get a value for those variables for a particular document (ValueSource).