Scripting: Cache script results if deterministic (#50106) (#50329)

Cache results from queries that use scripts if they use only
deterministic API calls.  Nondeterministic API calls are marked in the
whitelist with the `@nondeterministic` annotation.  Examples are
`Math.random()` and `new Date()`.

Refs: #49466
This commit is contained in:
Stuart Tettemer 2019-12-18 13:00:42 -07:00 committed by GitHub
parent 246d926412
commit 06a24f09cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1075 additions and 354 deletions

View File

@ -0,0 +1,29 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.painless.spi.annotation;
public class NonDeterministicAnnotation {
public static final String NAME = "nondeterministic";
public static final NonDeterministicAnnotation INSTANCE = new NonDeterministicAnnotation();
private NonDeterministicAnnotation() {}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.painless.spi.annotation;
import java.util.Map;
public class NonDeterministicAnnotationParser implements WhitelistAnnotationParser {
public static final NonDeterministicAnnotationParser INSTANCE = new NonDeterministicAnnotationParser();
private NonDeterministicAnnotationParser() {}
@Override
public Object parse(Map<String, String> arguments) {
if (arguments.isEmpty() == false) {
throw new IllegalArgumentException(
"unexpected parameters for [@" + NonDeterministicAnnotation.NAME + "] annotation, found " + arguments
);
}
return NonDeterministicAnnotation.INSTANCE;
}
}

View File

@ -34,7 +34,8 @@ public interface WhitelistAnnotationParser {
Map<String, WhitelistAnnotationParser> BASE_ANNOTATION_PARSERS = Collections.unmodifiableMap(
Stream.of(
new AbstractMap.SimpleEntry<>(NoImportAnnotation.NAME, NoImportAnnotationParser.INSTANCE),
new AbstractMap.SimpleEntry<>(DeprecatedAnnotation.NAME, DeprecatedAnnotationParser.INSTANCE)
new AbstractMap.SimpleEntry<>(DeprecatedAnnotation.NAME, DeprecatedAnnotationParser.INSTANCE),
new AbstractMap.SimpleEntry<>(NonDeterministicAnnotation.NAME, NonDeterministicAnnotationParser.INSTANCE)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
);

View File

@ -26,7 +26,6 @@ import org.elasticsearch.painless.node.SClass;
import org.elasticsearch.painless.spi.Whitelist;
import org.objectweb.asm.util.Printer;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
@ -205,13 +204,14 @@ final class Compiler {
* @param name The name of the script.
* @param source The source code for the script.
* @param settings The CompilerSettings to be used during the compilation.
* @return An executable script that implements both a specified interface and is a subclass of {@link PainlessScript}
* @return The ScriptRoot used to compile
*/
Constructor<?> compile(Loader loader, Set<String> extractedVariables, String name, String source, CompilerSettings settings) {
ScriptRoot compile(Loader loader, Set<String> extractedVariables, String name, String source,
CompilerSettings settings) {
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass);
SClass root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, painlessLookup, null);
root.extractVariables(extractedVariables);
root.analyze(painlessLookup, settings);
ScriptRoot scriptRoot = root.analyze(painlessLookup, settings);
Map<String, Object> statics = root.write();
try {
@ -225,8 +225,9 @@ final class Compiler {
clazz.getField(statik.getKey()).set(null, statik.getValue());
}
return clazz.getConstructors()[0];
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
return scriptRoot;
} catch (Exception exception) {
// Catch everything to let the user know this is something caused internally.
throw new IllegalStateException("An internal error occurred attempting to define the script [" + name + "].", exception);
}
}

View File

@ -143,12 +143,13 @@ public final class PainlessScriptEngine implements ScriptEngine {
});
Set<String> extractedVariables = new HashSet<>();
compile(contextsToCompilers.get(context), loader, extractedVariables, scriptName, scriptSource, params);
ScriptRoot scriptRoot = compile(contextsToCompilers.get(context), loader, extractedVariables, scriptName, scriptSource, params);
if (context.statefulFactoryClazz != null) {
return generateFactory(loader, context, extractedVariables, generateStatefulFactory(loader, context, extractedVariables));
return generateFactory(loader, context, extractedVariables, generateStatefulFactory(loader, context, extractedVariables),
scriptRoot);
} else {
return generateFactory(loader, context, extractedVariables, WriterConstants.CLASS_TYPE);
return generateFactory(loader, context, extractedVariables, WriterConstants.CLASS_TYPE, scriptRoot);
}
}
@ -270,6 +271,7 @@ public final class PainlessScriptEngine implements ScriptEngine {
* @param context The {@link ScriptContext}'s semantics are used to define the factory class.
* @param classType The type to be instaniated in the newFactory or newInstance method. Depends
* on whether a {@link ScriptContext#statefulFactoryClazz} is specified.
* @param scriptRoot the {@link ScriptRoot} used to do the compilation
* @param <T> The factory class.
* @return A factory class that will return script instances.
*/
@ -277,7 +279,8 @@ public final class PainlessScriptEngine implements ScriptEngine {
Loader loader,
ScriptContext<T> context,
Set<String> extractedVariables,
Type classType
Type classType,
ScriptRoot scriptRoot
) {
int classFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER| Opcodes.ACC_FINAL;
@ -330,8 +333,19 @@ public final class PainlessScriptEngine implements ScriptEngine {
adapter.endMethod();
writeNeedsMethods(context.factoryClazz, writer, extractedVariables);
writer.visitEnd();
String methodName = "isResultDeterministic";
org.objectweb.asm.commons.Method isResultDeterministic = new org.objectweb.asm.commons.Method(methodName,
MethodType.methodType(boolean.class).toMethodDescriptorString());
GeneratorAdapter deterAdapter = new GeneratorAdapter(Opcodes.ASM5, isResultDeterministic,
writer.visitMethod(Opcodes.ACC_PUBLIC, methodName, isResultDeterministic.getDescriptor(), null, null));
deterAdapter.visitCode();
deterAdapter.push(scriptRoot.deterministic);
deterAdapter.returnValue();
deterAdapter.endMethod();
writer.visitEnd();
Class<?> factory = loader.defineFactory(className.replace('/', '.'), writer.toByteArray());
try {
@ -363,19 +377,17 @@ public final class PainlessScriptEngine implements ScriptEngine {
}
}
void compile(Compiler compiler, Loader loader, Set<String> extractedVariables,
ScriptRoot compile(Compiler compiler, Loader loader, Set<String> extractedVariables,
String scriptName, String source, Map<String, String> params) {
final CompilerSettings compilerSettings = buildCompilerSettings(params);
try {
// Drop all permissions to actually compile the code itself.
AccessController.doPrivileged(new PrivilegedAction<Void>() {
return AccessController.doPrivileged(new PrivilegedAction<ScriptRoot>() {
@Override
public Void run() {
public ScriptRoot run() {
String name = scriptName == null ? source : scriptName;
compiler.compile(loader, extractedVariables, name, source, compilerSettings);
return null;
return compiler.compile(loader, extractedVariables, name, source, compilerSettings);
}
}, COMPILATION_CONTEXT);
// Note that it is safe to catch any of the following errors since Painless is stateless.

View File

@ -38,6 +38,7 @@ public class ScriptRoot {
protected final FunctionTable functionTable = new FunctionTable();
protected int syntheticCounter = 0;
protected boolean deterministic = true;
public ScriptRoot(PainlessLookup painlessLookup, CompilerSettings compilerSettings, ScriptClassInfo scriptClassInfo, SClass classRoot) {
this.painlessLookup = Objects.requireNonNull(painlessLookup);
@ -72,4 +73,6 @@ public class ScriptRoot {
public String getNextSyntheticName(String prefix) {
return prefix + "$synthetic$" + syntheticCounter++;
}
public void markNonDeterministic(boolean nondeterministic) { this.deterministic &= !nondeterministic; }
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.painless.lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class PainlessClassBinding {
@ -31,13 +32,16 @@ public class PainlessClassBinding {
public final Class<?> returnType;
public final List<Class<?>> typeParameters;
public final Map<Class<?>, Object> annotations;
PainlessClassBinding(Constructor<?> javaConstructor, Method javaMethod, Class<?> returnType, List<Class<?>> typeParameters) {
PainlessClassBinding(Constructor<?> javaConstructor, Method javaMethod, Class<?> returnType, List<Class<?>> typeParameters,
Map<Class<?>, Object> annotations) {
this.javaConstructor = javaConstructor;
this.javaMethod = javaMethod;
this.returnType = returnType;
this.typeParameters = typeParameters;
this.annotations = annotations;
}
@Override

View File

@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class PainlessConstructor {
@ -31,12 +32,15 @@ public class PainlessConstructor {
public final List<Class<?>> typeParameters;
public final MethodHandle methodHandle;
public final MethodType methodType;
public final Map<Class<?>, Object> annotations;
PainlessConstructor(Constructor<?> javaConstructor, List<Class<?>> typeParameters, MethodHandle methodHandle, MethodType methodType) {
PainlessConstructor(Constructor<?> javaConstructor, List<Class<?>> typeParameters, MethodHandle methodHandle, MethodType methodType,
Map<Class<?>, Object> annotations) {
this.javaConstructor = javaConstructor;
this.typeParameters = typeParameters;
this.methodHandle = methodHandle;
this.methodType = methodType;
this.annotations = annotations;
}
@Override
@ -53,11 +57,12 @@ public class PainlessConstructor {
return Objects.equals(javaConstructor, that.javaConstructor) &&
Objects.equals(typeParameters, that.typeParameters) &&
Objects.equals(methodType, that.methodType);
Objects.equals(methodType, that.methodType) &&
Objects.equals(annotations, that.annotations);
}
@Override
public int hashCode() {
return Objects.hash(javaConstructor, typeParameters, methodType);
return Objects.hash(javaConstructor, typeParameters, methodType, annotations);
}
}

View File

@ -50,6 +50,7 @@ import java.security.PrivilegedAction;
import java.security.SecureClassLoader;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -132,7 +133,8 @@ public final class PainlessLookupBuilder {
for (WhitelistConstructor whitelistConstructor : whitelistClass.whitelistConstructors) {
origin = whitelistConstructor.origin;
painlessLookupBuilder.addPainlessConstructor(
targetCanonicalClassName, whitelistConstructor.canonicalTypeNameParameters);
targetCanonicalClassName, whitelistConstructor.canonicalTypeNameParameters,
whitelistConstructor.painlessAnnotations);
}
for (WhitelistMethod whitelistMethod : whitelistClass.whitelistMethods) {
@ -140,7 +142,7 @@ public final class PainlessLookupBuilder {
painlessLookupBuilder.addPainlessMethod(
whitelist.classLoader, targetCanonicalClassName, whitelistMethod.augmentedCanonicalClassName,
whitelistMethod.methodName, whitelistMethod.returnCanonicalTypeName,
whitelistMethod.canonicalTypeNameParameters);
whitelistMethod.canonicalTypeNameParameters, whitelistMethod.painlessAnnotations);
}
for (WhitelistField whitelistField : whitelistClass.whitelistFields) {
@ -155,14 +157,16 @@ public final class PainlessLookupBuilder {
painlessLookupBuilder.addImportedPainlessMethod(
whitelist.classLoader, whitelistStatic.augmentedCanonicalClassName,
whitelistStatic.methodName, whitelistStatic.returnCanonicalTypeName,
whitelistStatic.canonicalTypeNameParameters);
whitelistStatic.canonicalTypeNameParameters,
whitelistStatic.painlessAnnotations);
}
for (WhitelistClassBinding whitelistClassBinding : whitelist.whitelistClassBindings) {
origin = whitelistClassBinding.origin;
painlessLookupBuilder.addPainlessClassBinding(
whitelist.classLoader, whitelistClassBinding.targetJavaClassName, whitelistClassBinding.methodName,
whitelistClassBinding.returnCanonicalTypeName, whitelistClassBinding.canonicalTypeNameParameters);
whitelistClassBinding.returnCanonicalTypeName, whitelistClassBinding.canonicalTypeNameParameters,
whitelistClassBinding.painlessAnnotations);
}
for (WhitelistInstanceBinding whitelistInstanceBinding : whitelist.whitelistInstanceBindings) {
@ -313,7 +317,8 @@ public final class PainlessLookupBuilder {
}
}
public void addPainlessConstructor(String targetCanonicalClassName, List<String> canonicalTypeNameParameters) {
public void addPainlessConstructor(String targetCanonicalClassName, List<String> canonicalTypeNameParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(targetCanonicalClassName);
Objects.requireNonNull(canonicalTypeNameParameters);
@ -337,10 +342,10 @@ public final class PainlessLookupBuilder {
typeParameters.add(typeParameter);
}
addPainlessConstructor(targetClass, typeParameters);
addPainlessConstructor(targetClass, typeParameters, annotations);
}
public void addPainlessConstructor(Class<?> targetClass, List<Class<?>> typeParameters) {
public void addPainlessConstructor(Class<?> targetClass, List<Class<?>> typeParameters, Map<Class<?>, Object> annotations) {
Objects.requireNonNull(targetClass);
Objects.requireNonNull(typeParameters);
@ -390,7 +395,8 @@ public final class PainlessLookupBuilder {
String painlessConstructorKey = buildPainlessConstructorKey(typeParametersSize);
PainlessConstructor existingPainlessConstructor = painlessClassBuilder.constructors.get(painlessConstructorKey);
PainlessConstructor newPainlessConstructor = new PainlessConstructor(javaConstructor, typeParameters, methodHandle, methodType);
PainlessConstructor newPainlessConstructor = new PainlessConstructor(javaConstructor, typeParameters, methodHandle, methodType,
annotations);
if (existingPainlessConstructor == null) {
newPainlessConstructor = painlessConstructorCache.computeIfAbsent(newPainlessConstructor, key -> key);
@ -403,7 +409,8 @@ public final class PainlessLookupBuilder {
}
public void addPainlessMethod(ClassLoader classLoader, String targetCanonicalClassName, String augmentedCanonicalClassName,
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(classLoader);
Objects.requireNonNull(targetCanonicalClassName);
@ -449,11 +456,11 @@ public final class PainlessLookupBuilder {
"[[" + targetCanonicalClassName + "], [" + methodName + "], " + canonicalTypeNameParameters + "]");
}
addPainlessMethod(targetClass, augmentedClass, methodName, returnType, typeParameters);
addPainlessMethod(targetClass, augmentedClass, methodName, returnType, typeParameters, annotations);
}
public void addPainlessMethod(Class<?> targetClass, Class<?> augmentedClass,
String methodName, Class<?> returnType, List<Class<?>> typeParameters) {
String methodName, Class<?> returnType, List<Class<?>> typeParameters, Map<Class<?>, Object> annotations) {
Objects.requireNonNull(targetClass);
Objects.requireNonNull(methodName);
@ -562,7 +569,7 @@ public final class PainlessLookupBuilder {
painlessClassBuilder.staticMethods.get(painlessMethodKey) :
painlessClassBuilder.methods.get(painlessMethodKey);
PainlessMethod newPainlessMethod =
new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType);
new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType, annotations);
if (existingPainlessMethod == null) {
newPainlessMethod = painlessMethodCache.computeIfAbsent(newPainlessMethod, key -> key);
@ -708,7 +715,8 @@ public final class PainlessLookupBuilder {
}
public void addImportedPainlessMethod(ClassLoader classLoader, String targetJavaClassName,
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(classLoader);
Objects.requireNonNull(targetJavaClassName);
@ -751,10 +759,11 @@ public final class PainlessLookupBuilder {
"[[" + targetCanonicalClassName + "], [" + methodName + "], " + canonicalTypeNameParameters + "]");
}
addImportedPainlessMethod(targetClass, methodName, returnType, typeParameters);
addImportedPainlessMethod(targetClass, methodName, returnType, typeParameters, annotations);
}
public void addImportedPainlessMethod(Class<?> targetClass, String methodName, Class<?> returnType, List<Class<?>> typeParameters) {
public void addImportedPainlessMethod(Class<?> targetClass, String methodName, Class<?> returnType, List<Class<?>> typeParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(targetClass);
Objects.requireNonNull(methodName);
Objects.requireNonNull(returnType);
@ -841,7 +850,7 @@ public final class PainlessLookupBuilder {
PainlessMethod existingImportedPainlessMethod = painlessMethodKeysToImportedPainlessMethods.get(painlessMethodKey);
PainlessMethod newImportedPainlessMethod =
new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType);
new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType, annotations);
if (existingImportedPainlessMethod == null) {
newImportedPainlessMethod = painlessMethodCache.computeIfAbsent(newImportedPainlessMethod, key -> key);
@ -859,7 +868,8 @@ public final class PainlessLookupBuilder {
}
public void addPainlessClassBinding(ClassLoader classLoader, String targetJavaClassName,
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(classLoader);
Objects.requireNonNull(targetJavaClassName);
@ -896,10 +906,11 @@ public final class PainlessLookupBuilder {
"[[" + targetCanonicalClassName + "], [" + methodName + "], " + canonicalTypeNameParameters + "]");
}
addPainlessClassBinding(targetClass, methodName, returnType, typeParameters);
addPainlessClassBinding(targetClass, methodName, returnType, typeParameters, annotations);
}
public void addPainlessClassBinding(Class<?> targetClass, String methodName, Class<?> returnType, List<Class<?>> typeParameters) {
public void addPainlessClassBinding(Class<?> targetClass, String methodName, Class<?> returnType, List<Class<?>> typeParameters,
Map<Class<?>, Object> annotations) {
Objects.requireNonNull(targetClass);
Objects.requireNonNull(methodName);
Objects.requireNonNull(returnType);
@ -1036,7 +1047,7 @@ public final class PainlessLookupBuilder {
PainlessClassBinding existingPainlessClassBinding = painlessMethodKeysToPainlessClassBindings.get(painlessMethodKey);
PainlessClassBinding newPainlessClassBinding =
new PainlessClassBinding(javaConstructor, javaMethod, returnType, typeParameters);
new PainlessClassBinding(javaConstructor, javaMethod, returnType, typeParameters, annotations);
if (existingPainlessClassBinding == null) {
newPainlessClassBinding = painlessClassBindingCache.computeIfAbsent(newPainlessClassBinding, key -> key);
@ -1444,7 +1455,7 @@ public final class PainlessLookupBuilder {
painlessMethod.javaMethod.getName(), bridgeTypeParameters.toArray(new Class<?>[0]));
MethodHandle bridgeHandle = MethodHandles.publicLookup().in(bridgeClass).unreflect(bridgeClass.getMethods()[0]);
bridgePainlessMethod = new PainlessMethod(bridgeMethod, bridgeClass,
painlessMethod.returnType, bridgeTypeParameters, bridgeHandle, bridgeMethodType);
painlessMethod.returnType, bridgeTypeParameters, bridgeHandle, bridgeMethodType, Collections.emptyMap());
painlessClassBuilder.runtimeMethods.put(painlessMethodKey.intern(), bridgePainlessMethod);
painlessBridgeCache.put(painlessMethod, bridgePainlessMethod);
} catch (Exception exception) {

View File

@ -25,6 +25,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class PainlessMethod {
@ -35,9 +36,10 @@ public class PainlessMethod {
public final List<Class<?>> typeParameters;
public final MethodHandle methodHandle;
public final MethodType methodType;
public final Map<Class<?>, Object> annotations;
public PainlessMethod(Method javaMethod, Class<?> targetClass, Class<?> returnType, List<Class<?>> typeParameters,
MethodHandle methodHandle, MethodType methodType) {
MethodHandle methodHandle, MethodType methodType, Map<Class<?>, Object> annotations) {
this.javaMethod = javaMethod;
this.targetClass = targetClass;
@ -45,6 +47,7 @@ public class PainlessMethod {
this.typeParameters = typeParameters.isEmpty() ? Collections.emptyList() : Arrays.asList(typeParameters.toArray(new Class<?>[0]));
this.methodHandle = methodHandle;
this.methodType = methodType;
this.annotations = annotations;
}
@Override
@ -63,11 +66,12 @@ public class PainlessMethod {
Objects.equals(targetClass, that.targetClass) &&
Objects.equals(returnType, that.returnType) &&
Objects.equals(typeParameters, that.typeParameters) &&
Objects.equals(methodType, that.methodType);
Objects.equals(methodType, that.methodType) &&
Objects.equals(annotations, that.annotations);
}
@Override
public int hashCode() {
return Objects.hash(javaMethod, targetClass, returnType, typeParameters, methodType);
return Objects.hash(javaMethod, targetClass, returnType, typeParameters, methodType, annotations);
}
}

View File

@ -28,6 +28,7 @@ import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessClassBinding;
import org.elasticsearch.painless.lookup.PainlessInstanceBinding;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.spi.annotation.NonDeterministicAnnotation;
import org.elasticsearch.painless.symbol.FunctionTable;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
@ -127,9 +128,11 @@ public final class ECallLocal extends AExpression {
typeParameters = new ArrayList<>(localFunction.getTypeParameters());
actual = localFunction.getReturnType();
} else if (importedMethod != null) {
scriptRoot.markNonDeterministic(importedMethod.annotations.containsKey(NonDeterministicAnnotation.class));
typeParameters = new ArrayList<>(importedMethod.typeParameters);
actual = importedMethod.returnType;
} else if (classBinding != null) {
scriptRoot.markNonDeterministic(classBinding.annotations.containsKey(NonDeterministicAnnotation.class));
typeParameters = new ArrayList<>(classBinding.typeParameters);
actual = classBinding.returnType;
bindingName = scriptRoot.getNextSyntheticName("class_binding");

View File

@ -37,7 +37,7 @@ import java.util.Objects;
import java.util.Set;
/**
* Represents a capturing function reference.
* Represents a capturing function reference. For member functions that require a this reference, ie not static.
*/
public final class ECapturingFunctionRef extends AExpression implements ILambda {
private final String variable;

View File

@ -32,7 +32,7 @@ import java.util.Objects;
import java.util.Set;
/**
* Represents a cast that is inserted into the tree replacing other casts. (Internal only.)
* Represents a cast that is inserted into the tree replacing other casts. (Internal only.) Casts are inserted during semantic checking.
*/
final class ECast extends AExpression {

View File

@ -27,6 +27,7 @@ import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessConstructor;
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
import org.elasticsearch.painless.spi.annotation.NonDeterministicAnnotation;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
@ -75,6 +76,8 @@ public final class ENewObj extends AExpression {
"constructor [" + typeToCanonicalTypeName(actual) + ", <init>/" + arguments.size() + "] not found"));
}
scriptRoot.markNonDeterministic(constructor.annotations.containsKey(NonDeterministicAnnotation.class));
Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
constructor.typeParameters.toArray(types);

View File

@ -27,6 +27,7 @@ import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.ScriptRoot;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.def;
import org.elasticsearch.painless.spi.annotation.NonDeterministicAnnotation;
import java.util.List;
import java.util.Objects;
@ -79,6 +80,8 @@ public final class PCallInvoke extends AExpression {
"method [" + typeToCanonicalTypeName(prefix.actual) + ", " + name + "/" + arguments.size() + "] not found"));
}
scriptRoot.markNonDeterministic(method.annotations.containsKey(NonDeterministicAnnotation.class));
sub = new PSubCallInvoke(location, method, prefix.actual, arguments);
}

View File

@ -130,7 +130,7 @@ public final class SClass extends AStatement {
extractedVariables.addAll(variables);
}
public void analyze(PainlessLookup painlessLookup, CompilerSettings settings) {
public ScriptRoot analyze(PainlessLookup painlessLookup, CompilerSettings settings) {
this.settings = settings;
table = new ScriptRoot(painlessLookup, settings, scriptClassInfo, this);
@ -148,6 +148,7 @@ public final class SClass extends AStatement {
Locals locals = Locals.newProgramScope();
analyze(table, locals);
return table;
}
@Override

View File

@ -636,7 +636,7 @@ class java.lang.Math {
double nextDown(double)
double nextUp(double)
double pow(double,double)
double random()
double random() @nondeterministic
double rint(double)
long round(double)
double scalb(double,int)
@ -729,7 +729,7 @@ class java.lang.StrictMath {
double nextDown(double)
double nextUp(double)
double pow(double,double)
double random()
double random() @nondeterministic
double rint(double)
long round(double)
double scalb(double,int)
@ -844,8 +844,8 @@ class java.lang.StringBuilder {
class java.lang.System {
void arraycopy(Object,int,Object,int,int)
long currentTimeMillis()
long nanoTime()
long currentTimeMillis() @nondeterministic
long nanoTime() @nondeterministic
}
# Thread: skipped

View File

@ -26,11 +26,11 @@
class java.time.Clock {
Clock fixed(Instant,ZoneId)
ZoneId getZone()
Instant instant()
long millis()
Clock offset(Clock,Duration)
Clock tick(Clock,Duration)
ZoneId getZone() @nondeterministic
Instant instant() @nondeterministic
long millis() @nondeterministic
Clock offset(Clock,Duration) @nondeterministic
Clock tick(Clock,Duration) @nondeterministic
}
class java.time.Duration {

View File

@ -490,9 +490,9 @@ class java.util.Calendar {
Map getDisplayNames(int,int,Locale)
int getFirstDayOfWeek()
int getGreatestMinimum(int)
Calendar getInstance()
Calendar getInstance(TimeZone)
Calendar getInstance(TimeZone,Locale)
Calendar getInstance() @nondeterministic
Calendar getInstance(TimeZone) @nondeterministic
Calendar getInstance(TimeZone,Locale) @nondeterministic
int getLeastMaximum(int)
int getMaximum(int)
int getMinimalDaysInFirstWeek()
@ -574,7 +574,7 @@ class java.util.Collections {
Comparator reverseOrder()
Comparator reverseOrder(Comparator)
void rotate(List,int)
void shuffle(List)
void shuffle(List) @nondeterministic
void shuffle(List,Random)
Set singleton(def)
List singletonList(def)
@ -605,7 +605,7 @@ class java.util.Currency {
}
class java.util.Date {
()
() @nondeterministic
(long)
boolean after(Date)
boolean before(Date)
@ -910,22 +910,22 @@ class java.util.PriorityQueue {
}
class java.util.Random {
()
() @nondeterministic
(long)
DoubleStream doubles(long)
DoubleStream doubles(long,double,double)
IntStream ints(long)
IntStream ints(long,int,int)
LongStream longs(long)
LongStream longs(long,long,long)
boolean nextBoolean()
void nextBytes(byte[])
double nextDouble()
float nextFloat()
double nextGaussian()
int nextInt()
int nextInt(int)
long nextLong()
DoubleStream doubles(long) @nondeterministic
DoubleStream doubles(long,double,double) @nondeterministic
IntStream ints(long) @nondeterministic
IntStream ints(long,int,int) @nondeterministic
LongStream longs(long) @nondeterministic
LongStream longs(long,long,long) @nondeterministic
boolean nextBoolean() @nondeterministic
void nextBytes(byte[]) @nondeterministic
double nextDouble() @nondeterministic
float nextFloat() @nondeterministic
double nextGaussian() @nondeterministic
int nextInt() @nondeterministic
int nextInt(int) @nondeterministic
long nextLong() @nondeterministic
void setSeed(long)
}
@ -1031,7 +1031,7 @@ class java.util.UUID {
UUID fromString(String)
long getLeastSignificantBits()
long getMostSignificantBits()
UUID randomUUID()
UUID randomUUID() @nondeterministic
UUID nameUUIDFromBytes(byte[])
long node()
long timestamp()

View File

@ -162,6 +162,32 @@ public class FactoryTests extends ScriptTestCase {
assertEquals(false, factory.needsNothing());
}
public void testDeterministic() {
FactoryTestScript.Factory factory =
scriptEngine.compile("deterministic_test", "Integer.parseInt('123')",
FactoryTestScript.CONTEXT, Collections.emptyMap());
assertTrue(factory.isResultDeterministic());
assertEquals(123, factory.newInstance(Collections.emptyMap()).execute(0));
}
public void testNotDeterministic() {
FactoryTestScript.Factory factory =
scriptEngine.compile("not_deterministic_test", "Math.random()",
FactoryTestScript.CONTEXT, Collections.emptyMap());
assertFalse(factory.isResultDeterministic());
Double d = (Double)factory.newInstance(Collections.emptyMap()).execute(0);
assertTrue(d >= 0.0 && d <= 1.0);
}
public void testMixedDeterministicIsNotDeterministic() {
FactoryTestScript.Factory factory =
scriptEngine.compile("not_deterministic_test", "Integer.parseInt('123') + Math.random()",
FactoryTestScript.CONTEXT, Collections.emptyMap());
assertFalse(factory.isResultDeterministic());
Double d = (Double)factory.newInstance(Collections.emptyMap()).execute(0);
assertTrue(d >= 123.0 && d <= 124.0);
}
public abstract static class EmptyTestScript {
public static final String[] PARAMETERS = {};
public abstract Object execute();

View File

@ -360,6 +360,8 @@ public class QueryShardContext extends QueryRewriteContext {
/** Compile script using script service */
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
FactoryType factory = scriptService.compile(script, context);
// TODO(stu): can check `factory instanceof ScriptFactory && ((ScriptFactory) factory).isResultDeterministic() == false`
// to avoid being so intrusive
if (factory.isResultDeterministic() == false) {
failIfFrozen();
}

View File

@ -82,7 +82,11 @@ public class MultiValuesSourceFieldConfig implements Writeable, ToXContentObject
}
public MultiValuesSourceFieldConfig(StreamInput in) throws IOException {
this.fieldName = in.readString();
if (in.getVersion().onOrAfter(Version.V_7_6_0)) {
this.fieldName = in.readOptionalString();
} else {
this.fieldName = in.readString();
}
this.missing = in.readGenericValue();
this.script = in.readOptionalWriteable(Script::new);
if (in.getVersion().before(Version.V_7_0_0)) {
@ -110,7 +114,11 @@ public class MultiValuesSourceFieldConfig implements Writeable, ToXContentObject
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(fieldName);
if (out.getVersion().onOrAfter(Version.V_7_6_0)) {
out.writeOptionalString(fieldName);
} else {
out.writeString(fieldName);
}
out.writeGenericValue(missing);
out.writeOptionalWriteable(script);
if (out.getVersion().before(Version.V_7_0_0)) {

View File

@ -23,6 +23,7 @@ import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESTestCase;
import java.util.HashMap;
import java.util.Map;
@ -116,4 +117,13 @@ public class AggregationTestScriptsPlugin extends MockScriptPlugin {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> ESTestCase.randomDouble());
return scripts;
}
}

View File

@ -1466,10 +1466,10 @@ public class DateHistogramIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=date")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -1484,10 +1484,21 @@ public class DateHistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "d");
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(dateHistogram("histo").field("d")
.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.CURRENT_DATE, params))
.dateHistogramInterval(DateHistogramInterval.MONTH)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(dateHistogram("histo").field("d")
.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params))
.dateHistogramInterval(DateHistogramInterval.MONTH)).get();
assertSearchResponse(r);
@ -1495,10 +1506,9 @@ public class DateHistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(1L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(dateHistogram("histo").field("d").dateHistogramInterval(DateHistogramInterval.MONTH)).get();
assertSearchResponse(r);
@ -1506,7 +1516,7 @@ public class DateHistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception {

View File

@ -884,10 +884,10 @@ public class DateRangeIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "date", "type=date")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -903,11 +903,11 @@ public class DateRangeIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(dateRange("foo").field("date")
.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.DOUBLE_PLUS_ONE_MONTH, params))
.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.CURRENT_DATE, params))
.addRange(ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
ZonedDateTime.of(2013, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)))
.get();
@ -918,9 +918,9 @@ public class DateRangeIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(dateRange("foo").field("date")
.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.DOUBLE_PLUS_ONE_MONTH, params))
.addRange(ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
ZonedDateTime.of(2013, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)))
.get();
@ -930,6 +930,18 @@ public class DateRangeIT extends ESIntegTestCase {
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(dateRange("foo").field("date")
.addRange(ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
ZonedDateTime.of(2013, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
/**

View File

@ -38,6 +38,7 @@ public class DateScriptMocksPlugin extends MockScriptPlugin {
static final String EXTRACT_FIELD = "extract_field";
static final String DOUBLE_PLUS_ONE_MONTH = "double_date_plus_1_month";
static final String LONG_PLUS_ONE_MONTH = "long_date_plus_1_month";
static final String CURRENT_DATE = "current_date";
@Override
public Map<String, Function<Map<String, Object>, Object>> pluginScripts() {
@ -53,4 +54,11 @@ public class DateScriptMocksPlugin extends MockScriptPlugin {
new DateTime((long) params.get("_value"), DateTimeZone.UTC).plusMonths(1).getMillis());
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put(CURRENT_DATE, params -> new DateTime().getMillis());
return scripts;
}
}

View File

@ -112,6 +112,15 @@ public class DoubleTermsIT extends AbstractTermsTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> DoubleTermsIT.randomDouble());
return scripts;
}
}
private static final int NUM_DOCS = 5; // TODO: randomize the size?
@ -918,10 +927,10 @@ public class DoubleTermsIT extends AbstractTermsTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=float")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -934,10 +943,10 @@ public class DoubleTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -945,14 +954,24 @@ public class DoubleTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(terms("terms").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -109,6 +109,15 @@ public class HistogramIT extends ESIntegTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> HistogramIT.randomDouble());
return scripts;
}
}
@Override
@ -1101,10 +1110,10 @@ public class HistogramIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=float")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -1117,9 +1126,10 @@ public class HistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(histogram("histo").field("d")
.script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())).interval(0.7).offset(0.05)).get();
.script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", emptyMap())).interval(0.7).offset(0.05))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -1127,8 +1137,17 @@ public class HistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(histogram("histo").field("d")
.script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())).interval(0.7).offset(0.05)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05))
.get();
assertSearchResponse(r);
@ -1136,7 +1155,7 @@ public class HistogramIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception {

View File

@ -99,6 +99,15 @@ public class LongTermsIT extends AbstractTermsTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> LongTermsIT.randomDouble());
return scripts;
}
}
private static final int NUM_DOCS = 5; // TODO randomize the size?
@ -894,10 +903,10 @@ public class LongTermsIT extends AbstractTermsTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -910,10 +919,10 @@ public class LongTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -921,14 +930,24 @@ public class LongTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(terms("terms").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(terms("terms").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
}

View File

@ -92,6 +92,15 @@ public class RangeIT extends ESIntegTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> RangeIT.randomDouble());
return scripts;
}
}
@Override
@ -945,10 +954,10 @@ public class RangeIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "i", "type=integer")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -962,12 +971,12 @@ public class RangeIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
range("foo").field("i").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())).addRange(0, 10))
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())).addRange(0, 10))
.get();
assertSearchResponse(r);
@ -976,15 +985,26 @@ public class RangeIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(range("foo").field("i").addRange(0, 10)).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
range("foo").field("i").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())).addRange(0, 10))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(range("foo").field("i").addRange(0, 10)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
public void testFieldAlias() {

View File

@ -198,6 +198,15 @@ public class SignificantTermsSignificanceScoreIT extends ESIntegTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> SignificantTermsSignificanceScoreIT.randomDouble());
return scripts;
}
private static long longValue(Object value) {
return ((ScriptHeuristic.LongAccessor) value).longValue();
}
@ -683,10 +692,10 @@ public class SignificantTermsSignificanceScoreIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -699,8 +708,10 @@ public class SignificantTermsSignificanceScoreIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
ScriptHeuristic scriptHeuristic = getScriptSignificanceHeuristic();
// Test that a request using a nondeterministic script does not get cached
ScriptHeuristic scriptHeuristic = new ScriptHeuristic(
new Script(ScriptType.INLINE, "mockscript", "Math.random()", Collections.emptyMap())
);
boolean useSigText = randomBoolean();
SearchResponse r;
if (useSigText) {
@ -717,8 +728,24 @@ public class SignificantTermsSignificanceScoreIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Test that a request using a deterministic script gets cached
scriptHeuristic = getScriptSignificanceHeuristic();
useSigText = randomBoolean();
if (useSigText) {
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(significantText("foo", "s").significanceHeuristic(scriptHeuristic)).get();
} else {
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(significantTerms("foo").field("s").significanceHeuristic(scriptHeuristic)).get();
}
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
if (useSigText) {
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(significantText("foo", "s")).get();
} else {
@ -729,9 +756,6 @@ public class SignificantTermsSignificanceScoreIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -119,6 +119,15 @@ public class StringTermsIT extends AbstractTermsTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> StringTermsIT.randomDouble());
return scripts;
}
}
@Override
@ -1115,10 +1124,10 @@ public class StringTermsIT extends AbstractTermsTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=keyword")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -1131,8 +1140,21 @@ public class StringTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(
terms("terms").field("d").script(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())))
@ -1142,16 +1164,15 @@ public class StringTermsIT extends AbstractTermsTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(1L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(terms("terms").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -83,6 +83,9 @@ public class AvgAggregatorTests extends AggregatorTestCase {
/** Script to return the {@code _value} provided by aggs framework. */
public static final String VALUE_SCRIPT = "_value";
/** Script to return a random double */
public static final String RANDOM_SCRIPT = "Math.random()";
@Override
protected ScriptService getMockScriptService() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
@ -115,8 +118,12 @@ public class AvgAggregatorTests extends AggregatorTestCase {
return ((Number) vars.get("_value")).doubleValue() + inc;
});
Map<String, Function<Map<String, Object>, Object>> nonDeterministicScripts = new HashMap<>();
nonDeterministicScripts.put(RANDOM_SCRIPT, vars -> AvgAggregatorTests.randomDouble());
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
scripts,
nonDeterministicScripts,
Collections.emptyMap());
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
@ -638,9 +645,10 @@ public class AvgAggregatorTests extends AggregatorTestCase {
}
/**
* Make sure that an aggregation using a script does not get cached.
* Make sure that an aggregation using a deterministic script does gets cached while
* one using a nondeterministic script does not.
*/
public void testDontCacheScripts() throws IOException {
public void testScriptCaching() throws IOException {
Directory directory = newDirectory();
RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory);
final int numDocs = 10;
@ -675,7 +683,26 @@ public class AvgAggregatorTests extends AggregatorTestCase {
assertEquals("avg", avg.getName());
assertTrue(AggregationInspectionHelper.hasValue(avg));
// Test that an aggregation using a script does not get cached
// Test that an aggregation using a deterministic script gets cached
assertTrue(aggregator.context().getQueryShardContext().isCacheable());
aggregationBuilder = new AvgAggregationBuilder("avg")
.field("value")
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, RANDOM_SCRIPT, Collections.emptyMap()));
aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType);
aggregator.preCollection();
indexSearcher.search(new MatchAllDocsQuery(), aggregator);
aggregator.postCollection();
avg = (InternalAvg) aggregator.buildAggregation(0L);
assertTrue(avg.getValue() >= 0.0);
assertTrue(avg.getValue() <= 1.0);
assertEquals("avg", avg.getName());
assertTrue(AggregationInspectionHelper.hasValue(avg));
// Test that an aggregation using a nondeterministic script does not get cached
assertFalse(aggregator.context().getQueryShardContext().isCacheable());
multiReader.close();

View File

@ -90,6 +90,15 @@ public class CardinalityIT extends ESIntegTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> CardinalityIT.randomDouble());
return scripts;
}
}
@Override
@ -449,10 +458,10 @@ public class CardinalityIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -465,10 +474,11 @@ public class CardinalityIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(
cardinality("foo").field("d").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value", emptyMap())))
cardinality("foo").field("d").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()",
emptyMap())))
.get();
assertSearchResponse(r);
@ -477,14 +487,25 @@ public class CardinalityIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(cardinality("foo").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(
cardinality("foo").field("d").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value", emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(cardinality("foo").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
}

View File

@ -639,10 +639,10 @@ public class ExtendedStatsIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -655,10 +655,10 @@ public class ExtendedStatsIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(extendedStats("foo").field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())))
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap())))
.get();
assertSearchResponse(r);
@ -667,15 +667,26 @@ public class ExtendedStatsIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(extendedStats("foo").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(extendedStats("foo").field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(extendedStats("foo").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
}

View File

@ -558,10 +558,10 @@ public class HDRPercentileRanksIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -574,8 +574,22 @@ public class HDRPercentileRanksIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client()
.prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentileRanks("foo", new double[]{50.0})
.method(PercentilesMethod.HDR).field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script gets cached
r = client()
.prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentileRanks("foo", new double[]{50.0})
.method(PercentilesMethod.HDR).field("d")
@ -586,10 +600,9 @@ public class HDRPercentileRanksIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(1L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentileRanks("foo", new double[]{50.0}).method(PercentilesMethod.HDR).field("d")).get();
assertSearchResponse(r);
@ -597,7 +610,7 @@ public class HDRPercentileRanksIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -523,10 +523,10 @@ public class HDRPercentilesIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -539,10 +539,10 @@ public class HDRPercentilesIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentiles("foo").method(PercentilesMethod.HDR).field("d").percentiles(50.0)
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())))
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())))
.get();
assertSearchResponse(r);
@ -551,8 +551,19 @@ public class HDRPercentilesIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentiles("foo").method(PercentilesMethod.HDR).field("d").percentiles(50.0)
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentiles("foo").method(PercentilesMethod.HDR).field("d").percentiles(50.0)).get();
assertSearchResponse(r);
@ -560,7 +571,7 @@ public class HDRPercentilesIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -110,6 +110,9 @@ public class MaxAggregatorTests extends AggregatorTestCase {
/** Script to return the {@code _value} provided by aggs framework. */
public static final String VALUE_SCRIPT = "_value";
/** Script to return a random double */
public static final String RANDOM_SCRIPT = "Math.random()";
@Override
protected ScriptService getMockScriptService() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
@ -143,8 +146,12 @@ public class MaxAggregatorTests extends AggregatorTestCase {
return ((Number) vars.get("_value")).doubleValue() + inc;
});
Map<String, Function<Map<String, Object>, Object>> nonDeterministicScripts = new HashMap<>();
nonDeterministicScripts.put(RANDOM_SCRIPT, vars -> MaxAggregatorTests.randomDouble());
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
scripts,
nonDeterministicScripts,
Collections.emptyMap());
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
@ -948,9 +955,10 @@ public class MaxAggregatorTests extends AggregatorTestCase {
}
/**
* Make sure that an aggregation using a script does not get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws IOException {
public void testScriptCaching() throws Exception {
Directory directory = newDirectory();
RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory);
final int numDocs = 10;
@ -968,7 +976,6 @@ public class MaxAggregatorTests extends AggregatorTestCase {
MultiReader multiReader = new MultiReader(indexReader, unamappedIndexReader);
IndexSearcher indexSearcher = newSearcher(multiReader, true, true);
MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER);
fieldType.setName("value");
MaxAggregationBuilder aggregationBuilder = new MaxAggregationBuilder("max")
@ -987,6 +994,24 @@ public class MaxAggregatorTests extends AggregatorTestCase {
assertTrue(AggregationInspectionHelper.hasValue(max));
// Test that an aggregation using a script does not get cached
assertTrue(aggregator.context().getQueryShardContext().isCacheable());
aggregationBuilder = new MaxAggregationBuilder("max")
.field("value")
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, RANDOM_SCRIPT, Collections.emptyMap()));
aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType);
aggregator.preCollection();
indexSearcher.search(new MatchAllDocsQuery(), aggregator);
aggregator.postCollection();
max = (InternalMax) aggregator.buildAggregation(0L);
assertTrue(max.getValue() >= 0.0);
assertTrue(max.getValue() <= 1.0);
assertEquals("max", max.getName());
assertTrue(AggregationInspectionHelper.hasValue(max));
// Test that an aggregation using a nondeterministic script does not get cached
assertFalse(aggregator.context().getQueryShardContext().isCacheable());
multiReader.close();

View File

@ -556,10 +556,10 @@ public class MedianAbsoluteDeviationIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(
prepareCreate("cache_test_idx")
.addMapping("type", "d", "type=long")
@ -579,8 +579,20 @@ public class MedianAbsoluteDeviationIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(randomBuilder()
.field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(randomBuilder()
.field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap()))).get();
@ -589,16 +601,15 @@ public class MedianAbsoluteDeviationIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(1L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(randomBuilder().field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -28,6 +28,7 @@ import java.util.function.Function;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.search.lookup.LeafDocLookup;
import org.elasticsearch.test.ESTestCase;
/**
* Provides a number of dummy scripts for tests.
@ -52,6 +53,9 @@ public class MetricAggScriptPlugin extends MockScriptPlugin {
/** Script to return the {@code _value} provided by aggs framework. */
public static final String VALUE_SCRIPT = "_value";
/** Script to return a random double */
public static final String RANDOM_SCRIPT = "Math.random()";
@Override
public String pluginScriptLang() {
return METRIC_SCRIPT_ENGINE;
@ -88,4 +92,13 @@ public class MetricAggScriptPlugin extends MockScriptPlugin {
});
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("Math.random()", vars -> ESTestCase.randomDouble());
return scripts;
}
}

View File

@ -127,6 +127,8 @@ public class MinAggregatorTests extends AggregatorTestCase {
private static final String INVERT_SCRIPT = "invert";
private static final String RANDOM_SCRIPT = "random";
@Override
protected ScriptService getMockScriptService() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
@ -161,8 +163,12 @@ public class MinAggregatorTests extends AggregatorTestCase {
});
scripts.put(INVERT_SCRIPT, vars -> -((Number) vars.get("_value")).doubleValue());
Map<String, Function<Map<String, Object>, Object>> nonDeterministicScripts = new HashMap<>();
nonDeterministicScripts.put(RANDOM_SCRIPT, vars -> AggregatorTestCase.randomDouble());
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
scripts,
nonDeterministicScripts,
Collections.emptyMap());
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
@ -649,7 +655,7 @@ public class MinAggregatorTests extends AggregatorTestCase {
}
}
public void testNoCachingWithScript() throws IOException {
public void testScriptCaching() throws IOException {
MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER);
fieldType.setName("number");
@ -657,6 +663,10 @@ public class MinAggregatorTests extends AggregatorTestCase {
.field("number")
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, INVERT_SCRIPT, Collections.emptyMap()));;
MinAggregationBuilder nonDeterministicAggregationBuilder = new MinAggregationBuilder("min")
.field("number")
.script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, RANDOM_SCRIPT, Collections.emptyMap()));;
try (Directory directory = newDirectory()) {
RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory);
indexWriter.addDocument(singleton(new NumericDocValuesField("number", 7)));
@ -668,11 +678,19 @@ public class MinAggregatorTests extends AggregatorTestCase {
try (IndexReader indexReader = DirectoryReader.open(directory)) {
IndexSearcher indexSearcher = newSearcher(indexReader, true, true);
InternalMin min = searchAndReduce(indexSearcher, new MatchAllDocsQuery(), aggregationBuilder, fieldType);
assertEquals(-7.0, min.getValue(), 0);
InternalMin min = searchAndReduce(indexSearcher, new MatchAllDocsQuery(), nonDeterministicAggregationBuilder, fieldType);
assertTrue(min.getValue() >= 0.0 && min.getValue() <= 1.0);
assertTrue(AggregationInspectionHelper.hasValue(min));
assertFalse(queryShardContext.isCacheable());
indexSearcher = newSearcher(indexReader, true, true);
min = searchAndReduce(indexSearcher, new MatchAllDocsQuery(), aggregationBuilder, fieldType);
assertEquals(-7.0, min.getValue(), 0);
assertTrue(AggregationInspectionHelper.hasValue(min));
assertTrue(queryShardContext.isCacheable());
}
}
}

View File

@ -247,6 +247,23 @@ public class ScriptedMetricIT extends ESIntegTestCase {
return scripts;
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();
scripts.put("state.data = Math.random()", vars ->
aggScript(vars, state -> state.put("data", ScriptedMetricIT.randomDouble())));
scripts.put("state['count'] = Math.random() >= 0.5 ? 1 : 0", vars ->
aggScript(vars, state -> state.put("count", ScriptedMetricIT.randomDouble() >= 0.5 ? 1 : 0)));
scripts.put("return Math.random()", vars -> ScriptedMetricIT.randomDouble());
return scripts;
}
@SuppressWarnings("unchecked")
static Map<String, Object> aggScript(Map<String, Object> vars, Consumer<Map<String, Object>> fn) {
Map<String, Object> aggState = (Map<String, Object>) vars.get("state");
@ -1015,17 +1032,27 @@ public class ScriptedMetricIT extends ESIntegTestCase {
assertThat(aggregationResult.get(0), equalTo(0));
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script gets cached and nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
// TODO(stu): add non-determinism in init, agg, combine and reduce, ensure not cached
Script mapScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "state['count'] = 1", Collections.emptyMap());
Script combineScript =
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op aggregation", Collections.emptyMap());
Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME,
"no-op list aggregation", Collections.emptyMap());
Script ndInitScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "state.data = Math.random()",
Collections.emptyMap());
Script ndMapScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "state['count'] = Math.random() >= 0.5 ? 1 : 0",
Collections.emptyMap());
Script ndRandom = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "return Math.random()",
Collections.emptyMap());
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -1038,15 +1065,58 @@ public class ScriptedMetricIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a non-deterministic init script causes the result to not be cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript)).get();
.addAggregation(scriptedMetric("foo").initScript(ndInitScript).mapScript(mapScript).combineScript(combineScript)
.reduceScript(reduceScript)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a non-deterministic map script causes the result to not be cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(scriptedMetric("foo").mapScript(ndMapScript).combineScript(combineScript).reduceScript(reduceScript))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a non-deterministic combine script causes the result to not be cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(ndRandom).reduceScript(reduceScript)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// NOTE: random reduce scripts don't hit the query shard context (they are done on the coordinator) and so can be cached.
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(ndRandom)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Test that all deterministic scripts cause the request to be cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(2L));
}
public void testConflictingAggAndScriptParams() {

View File

@ -488,10 +488,10 @@ public class StatsIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -504,10 +504,10 @@ public class StatsIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
stats("foo").field("d").script(
new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -515,14 +515,24 @@ public class StatsIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(stats("foo").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(
stats("foo").field("d").script(
new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(stats("foo").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
}

View File

@ -47,6 +47,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.histogra
import static org.elasticsearch.search.aggregations.AggregationBuilders.sum;
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.METRIC_SCRIPT_ENGINE;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.RANDOM_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.SUM_VALUES_FIELD_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.VALUE_FIELD_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.VALUE_SCRIPT;
@ -374,10 +375,10 @@ public class SumIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -390,10 +391,10 @@ public class SumIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(sum("foo").field("d").script(
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_SCRIPT, Collections.emptyMap()))).get();
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -401,15 +402,25 @@ public class SumIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(sum("foo").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(sum("foo").field("d").script(
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_SCRIPT, Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(sum("foo").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
public void testFieldAlias() {

View File

@ -484,10 +484,10 @@ public class TDigestPercentileRanksIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -500,8 +500,20 @@ public class TDigestPercentileRanksIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentileRanks("foo", new double[]{50.0})
.field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(percentileRanks("foo", new double[]{50.0})
.field("d")
.script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap()))).get();
@ -510,17 +522,16 @@ public class TDigestPercentileRanksIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(1L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentileRanks("foo", new double[]{50.0}).field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(2L));
}
}

View File

@ -467,10 +467,10 @@ public class TDigestPercentilesIT extends AbstractNumericTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -483,9 +483,9 @@ public class TDigestPercentilesIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d")
.percentiles(50.0).script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())))
.percentiles(50.0).script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())))
.get();
assertSearchResponse(r);
@ -494,14 +494,24 @@ public class TDigestPercentilesIT extends AbstractNumericTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d").percentiles(50.0)).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d")
.percentiles(50.0).script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d").percentiles(50.0)).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
}

View File

@ -107,6 +107,11 @@ public class TopHitsIT extends ESIntegTestCase {
protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {
return Collections.singletonMap("5", script -> "5");
}
@Override
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() {
return Collections.singletonMap("Math.random()", script -> TopHitsIT.randomDouble());
}
}
public static String randomExecutionHint() {
@ -1087,10 +1092,10 @@ public class TopHitsIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
try {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(
@ -1108,10 +1113,10 @@ public class TopHitsIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script field does not get cached
// Test that a request using a nondeterministic script field does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(topHits("foo").scriptField("bar",
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "5", Collections.emptyMap()))).get();
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
@ -1119,7 +1124,32 @@ public class TopHitsIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script sort does not get cached
// Test that a request using a nondeterministic script sort does not get cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(topHits("foo").sort(
SortBuilders.scriptSort(
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap()),
ScriptSortType.STRING)))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a deterministic script field does not get cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(topHits("foo").scriptField("bar",
new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "5", Collections.emptyMap()))).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Test that a request using a deterministic script sort does not get cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(topHits("foo").sort(
SortBuilders.scriptSort(
@ -1130,17 +1160,16 @@ public class TopHitsIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
.getMissCount(), equalTo(2L));
// To make sure that the cache is working test that a request not using
// a script is cached
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(topHits("foo")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
.getMissCount(), equalTo(3L));
} finally {
assertAcked(client().admin().indices().prepareDelete("cache_test_idx")); // delete this - if we use tests.iters it would fail
}

View File

@ -43,6 +43,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.filter;
import static org.elasticsearch.search.aggregations.AggregationBuilders.global;
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.METRIC_SCRIPT_ENGINE;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.RANDOM_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.SUM_FIELD_PARAMS_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.SUM_VALUES_FIELD_SCRIPT;
import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.VALUE_FIELD_SCRIPT;
@ -211,10 +212,10 @@ public class ValueCountIT extends ESIntegTestCase {
}
/**
* Make sure that a request using a script does not get cached and a request
* not using a script does get cached.
* Make sure that a request using a deterministic script or not using a script get cached.
* Ensure requests using nondeterministic scripts do not get cached.
*/
public void testDontCacheScripts() throws Exception {
public void testScriptCaching() throws Exception {
assertAcked(prepareCreate("cache_test_idx").addMapping("type", "d", "type=long")
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get());
@ -227,10 +228,10 @@ public class ValueCountIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// Test that a request using a script does not get cached
// Test that a request using a nondeterministic script does not get cached
SearchResponse r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(count("foo").field("d").script(
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_FIELD_SCRIPT, Collections.emptyMap())))
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap())))
.get();
assertSearchResponse(r);
@ -239,15 +240,26 @@ public class ValueCountIT extends ESIntegTestCase {
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(0L));
// To make sure that the cache is working test that a request not using
// a script is cached
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(count("foo").field("d")).get();
// Test that a request using a deterministic script gets cached
r = client().prepareSearch("cache_test_idx").setSize(0)
.addAggregation(count("foo").field("d").script(
new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_FIELD_SCRIPT, Collections.emptyMap())))
.get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(1L));
// Ensure that non-scripted requests are cached as normal
r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(count("foo").field("d")).get();
assertSearchResponse(r);
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getHitCount(), equalTo(0L));
assertThat(client().admin().indices().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache()
.getMissCount(), equalTo(2L));
}
public void testOrderByEmptyAggregation() throws Exception {

View File

@ -0,0 +1,45 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.script;
import java.util.Map;
import java.util.function.Function;
/**
* A mocked script used for testing purposes. {@code deterministic} implies cacheability in query shard cache.
*/
public abstract class MockDeterministicScript implements Function<Map<String, Object>, Object>, ScriptFactory {
public abstract Object apply(Map<String, Object> vars);
public abstract boolean isResultDeterministic();
public static MockDeterministicScript asDeterministic(Function<Map<String, Object>, Object> script) {
return new MockDeterministicScript() {
@Override public boolean isResultDeterministic() { return true; }
@Override public Object apply(Map<String, Object> vars) { return script.apply(vars); }
};
}
public static MockDeterministicScript asNonDeterministic(Function<Map<String, Object>, Object> script) {
return new MockDeterministicScript() {
@Override public boolean isResultDeterministic() { return false; }
@Override public Object apply(Map<String, Object> vars) { return script.apply(vars); }
};
}
}

View File

@ -64,11 +64,22 @@ public class MockScriptEngine implements ScriptEngine {
public static final String NAME = "mockscript";
private final String type;
private final Map<String, Function<Map<String, Object>, Object>> scripts;
private final Map<String, MockDeterministicScript> scripts;
private final Map<ScriptContext<?>, ContextCompiler> contexts;
public MockScriptEngine(String type, Map<String, Function<Map<String, Object>, Object>> scripts,
Map<ScriptContext<?>, ContextCompiler> contexts) {
this(type, scripts, Collections.emptyMap(), contexts);
}
public MockScriptEngine(String type, Map<String, Function<Map<String, Object>, Object>> deterministicScripts,
Map<String, Function<Map<String, Object>, Object>> nonDeterministicScripts,
Map<ScriptContext<?>, ContextCompiler> contexts) {
Map<String, MockDeterministicScript> scripts = new HashMap<>(deterministicScripts.size() + nonDeterministicScripts.size());
deterministicScripts.forEach((key, value) -> scripts.put(key, MockDeterministicScript.asDeterministic(value)));
nonDeterministicScripts.forEach((key, value) -> scripts.put(key, MockDeterministicScript.asNonDeterministic(value)));
this.type = type;
this.scripts = Collections.unmodifiableMap(scripts);
this.contexts = Collections.unmodifiableMap(contexts);
@ -87,34 +98,14 @@ public class MockScriptEngine implements ScriptEngine {
public <T extends ScriptFactory> T compile(String name, String source, ScriptContext<T> context, Map<String, String> params) {
// Scripts are always resolved using the script's source. For inline scripts, it's easy because they don't have names and the
// source is always provided. For stored and file scripts, the source of the script must match the key of a predefined script.
Function<Map<String, Object>, Object> script = scripts.get(source);
MockDeterministicScript script = scripts.get(source);
if (script == null) {
throw new IllegalArgumentException("No pre defined script matching [" + source + "] for script with name [" + name + "], " +
"did you declare the mocked script?");
}
MockCompiledScript mockCompiled = new MockCompiledScript(name, params, source, script);
if (context.instanceClazz.equals(FieldScript.class)) {
FieldScript.Factory factory = (parameters, lookup) ->
ctx -> new FieldScript(parameters, lookup, ctx) {
@Override
public Object execute() {
Map<String, Object> vars = createVars(parameters);
vars.putAll(getLeafLookup().asMap());
return script.apply(vars);
}
};
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(FieldScript.class)) {
FieldScript.Factory factory = (parameters, lookup) ->
ctx -> new FieldScript(parameters, lookup, ctx) {
@Override
public Object execute() {
Map<String, Object> vars = createVars(parameters);
vars.putAll(getLeafLookup().asMap());
return script.apply(vars);
}
};
return context.factoryClazz.cast(factory);
return context.factoryClazz.cast(new MockFieldScriptFactory(script));
} else if(context.instanceClazz.equals(TermsSetQueryScript.class)) {
TermsSetQueryScript.Factory factory = (parameters, lookup) -> (TermsSetQueryScript.LeafFactory) ctx
-> new TermsSetQueryScript(parameters, lookup, ctx) {
@ -149,17 +140,7 @@ public class MockScriptEngine implements ScriptEngine {
};
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(StringSortScript.class)) {
StringSortScript.Factory factory = (parameters, lookup) -> (StringSortScript.LeafFactory) ctx
-> new StringSortScript(parameters, lookup, ctx) {
@Override
public String execute() {
Map<String, Object> vars = new HashMap<>(parameters);
vars.put("params", parameters);
vars.put("doc", getDoc());
return String.valueOf(script.apply(vars));
}
};
return context.factoryClazz.cast(factory);
return context.factoryClazz.cast(new MockStringSortScriptFactory(script));
} else if (context.instanceClazz.equals(IngestScript.class)) {
IngestScript.Factory factory = vars -> new IngestScript(vars) {
@Override
@ -169,37 +150,7 @@ public class MockScriptEngine implements ScriptEngine {
};
return context.factoryClazz.cast(factory);
} else if(context.instanceClazz.equals(AggregationScript.class)) {
AggregationScript.Factory factory = (parameters, lookup) -> new AggregationScript.LeafFactory() {
@Override
public AggregationScript newInstance(final LeafReaderContext ctx) {
return new AggregationScript(parameters, lookup, ctx) {
@Override
public Object execute() {
Map<String, Object> vars = new HashMap<>(parameters);
vars.put("params", parameters);
vars.put("doc", getDoc());
vars.put("_score", get_score());
vars.put("_value", get_value());
return script.apply(vars);
}
};
}
@Override
public boolean needs_score() {
return true;
}
};
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(IngestScript.class)) {
IngestScript.Factory factory = vars ->
new IngestScript(vars) {
@Override
public void execute(Map<String, Object> ctx) {
script.apply(ctx);
}
};
return context.factoryClazz.cast(factory);
return context.factoryClazz.cast(new MockAggregationScript(script));
} else if (context.instanceClazz.equals(IngestConditionalScript.class)) {
IngestConditionalScript.Factory factory = parameters -> new IngestConditionalScript(parameters) {
@Override
@ -242,13 +193,7 @@ public class MockScriptEngine implements ScriptEngine {
};
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(SignificantTermsHeuristicScoreScript.class)) {
SignificantTermsHeuristicScoreScript.Factory factory = () -> new SignificantTermsHeuristicScoreScript() {
@Override
public double execute(Map<String, Object> vars) {
return ((Number) script.apply(vars)).doubleValue();
}
};
return context.factoryClazz.cast(factory);
return context.factoryClazz.cast(new MockSignificantTermsHeuristicScoreScript(script));
} else if (context.instanceClazz.equals(TemplateScript.class)) {
TemplateScript.Factory factory = vars -> {
Map<String, Object> varsWithParams = new HashMap<>();
@ -282,19 +227,19 @@ public class MockScriptEngine implements ScriptEngine {
};
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(ScoreScript.class)) {
ScoreScript.Factory factory = new MockScoreScript(script);
ScoreScript.Factory factory = new MockScoreScript(script::apply);
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(ScriptedMetricAggContexts.InitScript.class)) {
ScriptedMetricAggContexts.InitScript.Factory factory = mockCompiled::createMetricAggInitScript;
ScriptedMetricAggContexts.InitScript.Factory factory = new MockMetricAggInitScriptFactory(script);
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(ScriptedMetricAggContexts.MapScript.class)) {
ScriptedMetricAggContexts.MapScript.Factory factory = mockCompiled::createMetricAggMapScript;
ScriptedMetricAggContexts.MapScript.Factory factory = new MockMetricAggMapScriptFactory(script);
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(ScriptedMetricAggContexts.CombineScript.class)) {
ScriptedMetricAggContexts.CombineScript.Factory factory = mockCompiled::createMetricAggCombineScript;
ScriptedMetricAggContexts.CombineScript.Factory factory = new MockMetricAggCombineScriptFactory(script);
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(ScriptedMetricAggContexts.ReduceScript.class)) {
ScriptedMetricAggContexts.ReduceScript.Factory factory = mockCompiled::createMetricAggReduceScript;
ScriptedMetricAggContexts.ReduceScript.Factory factory = new MockMetricAggReduceScriptFactory(script);
return context.factoryClazz.cast(factory);
} else if (context.instanceClazz.equals(IntervalFilterScript.class)) {
IntervalFilterScript.Factory factory = mockCompiled::createIntervalFilterScript;
@ -302,7 +247,7 @@ public class MockScriptEngine implements ScriptEngine {
}
ContextCompiler compiler = contexts.get(context);
if (compiler != null) {
return context.factoryClazz.cast(compiler.compile(script, params));
return context.factoryClazz.cast(compiler.compile(script::apply, params));
}
throw new IllegalArgumentException("mock script engine does not know how to handle context [" + context.name + "]");
}
@ -372,25 +317,6 @@ public class MockScriptEngine implements ScriptEngine {
return new MockSimilarityWeightScript(script != null ? script : ctx -> 42d);
}
public ScriptedMetricAggContexts.InitScript createMetricAggInitScript(Map<String, Object> params, Map<String, Object> state) {
return new MockMetricAggInitScript(params, state, script != null ? script : ctx -> 42d);
}
public ScriptedMetricAggContexts.MapScript.LeafFactory createMetricAggMapScript(Map<String, Object> params,
Map<String, Object> state,
SearchLookup lookup) {
return new MockMetricAggMapScript(params, state, lookup, script != null ? script : ctx -> 42d);
}
public ScriptedMetricAggContexts.CombineScript createMetricAggCombineScript(Map<String, Object> params,
Map<String, Object> state) {
return new MockMetricAggCombineScript(params, state, script != null ? script : ctx -> 42d);
}
public ScriptedMetricAggContexts.ReduceScript createMetricAggReduceScript(Map<String, Object> params, List<Object> states) {
return new MockMetricAggReduceScript(params, states, script != null ? script : ctx -> 42d);
}
public IntervalFilterScript createIntervalFilterScript() {
return new IntervalFilterScript() {
@Override
@ -471,6 +397,17 @@ public class MockScriptEngine implements ScriptEngine {
}
}
public static class MockMetricAggInitScriptFactory implements ScriptedMetricAggContexts.InitScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockMetricAggInitScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public ScriptedMetricAggContexts.InitScript newInstance(Map<String, Object> params, Map<String, Object> state) {
return new MockMetricAggInitScript(params, state, script);
}
}
public static class MockMetricAggInitScript extends ScriptedMetricAggContexts.InitScript {
private final Function<Map<String, Object>, Object> script;
@ -493,6 +430,18 @@ public class MockScriptEngine implements ScriptEngine {
}
}
public static class MockMetricAggMapScriptFactory implements ScriptedMetricAggContexts.MapScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockMetricAggMapScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public ScriptedMetricAggContexts.MapScript.LeafFactory newFactory(Map<String, Object> params, Map<String, Object> state,
SearchLookup lookup) {
return new MockMetricAggMapScript(params, state, lookup, script);
}
}
public static class MockMetricAggMapScript implements ScriptedMetricAggContexts.MapScript.LeafFactory {
private final Map<String, Object> params;
private final Map<String, Object> state;
@ -529,11 +478,21 @@ public class MockScriptEngine implements ScriptEngine {
}
}
public static class MockMetricAggCombineScriptFactory implements ScriptedMetricAggContexts.CombineScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockMetricAggCombineScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public ScriptedMetricAggContexts.CombineScript newInstance(Map<String, Object> params, Map<String, Object> state) {
return new MockMetricAggCombineScript(params, state, script);
}
}
public static class MockMetricAggCombineScript extends ScriptedMetricAggContexts.CombineScript {
private final Function<Map<String, Object>, Object> script;
MockMetricAggCombineScript(Map<String, Object> params, Map<String, Object> state,
Function<Map<String, Object>, Object> script) {
MockMetricAggCombineScript(Map<String, Object> params, Map<String, Object> state, Function<Map<String, Object>, Object> script) {
super(params, state);
this.script = script;
}
@ -551,11 +510,21 @@ public class MockScriptEngine implements ScriptEngine {
}
}
public static class MockMetricAggReduceScriptFactory implements ScriptedMetricAggContexts.ReduceScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockMetricAggReduceScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public ScriptedMetricAggContexts.ReduceScript newInstance(Map<String, Object> params, List<Object> states) {
return new MockMetricAggReduceScript(params, states, script);
}
}
public static class MockMetricAggReduceScript extends ScriptedMetricAggContexts.ReduceScript {
private final Function<Map<String, Object>, Object> script;
MockMetricAggReduceScript(Map<String, Object> params, List<Object> states,
Function<Map<String, Object>, Object> script) {
MockMetricAggReduceScript(Map<String, Object> params, List<Object> states, Function<Map<String, Object>, Object> script) {
super(params, states);
this.script = script;
}
@ -617,4 +586,88 @@ public class MockScriptEngine implements ScriptEngine {
}
}
class MockAggregationScript implements AggregationScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockAggregationScript(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public AggregationScript.LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup) {
return new AggregationScript.LeafFactory() {
@Override
public AggregationScript newInstance(final LeafReaderContext ctx) {
return new AggregationScript(params, lookup, ctx) {
@Override
public Object execute() {
Map<String, Object> vars = new HashMap<>(params);
vars.put("params", params);
vars.put("doc", getDoc());
vars.put("_score", get_score());
vars.put("_value", get_value());
return script.apply(vars);
}
};
}
@Override
public boolean needs_score() {
return true;
}
};
}
}
class MockSignificantTermsHeuristicScoreScript implements SignificantTermsHeuristicScoreScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockSignificantTermsHeuristicScoreScript(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public SignificantTermsHeuristicScoreScript newInstance() {
return new SignificantTermsHeuristicScoreScript() {
@Override
public double execute(Map<String, Object> vars) {
return ((Number) script.apply(vars)).doubleValue();
}
};
}
}
class MockFieldScriptFactory implements FieldScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockFieldScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public FieldScript.LeafFactory newFactory(Map<String, Object> parameters, SearchLookup lookup) {
return ctx -> new FieldScript(parameters, lookup, ctx) {
@Override
public Object execute() {
Map<String, Object> vars = createVars(parameters);
vars.putAll(getLeafLookup().asMap());
return script.apply(vars);
}
};
}
}
class MockStringSortScriptFactory implements StringSortScript.Factory, ScriptFactory {
private final MockDeterministicScript script;
MockStringSortScriptFactory(MockDeterministicScript script) { this.script = script; }
@Override public boolean isResultDeterministic() { return script.isResultDeterministic(); }
@Override
public StringSortScript.LeafFactory newFactory(Map<String, Object> parameters, SearchLookup lookup) {
return ctx -> new StringSortScript(parameters, lookup, ctx) {
@Override
public String execute() {
Map<String, Object> vars = new HashMap<>(parameters);
vars.put("params", parameters);
vars.put("doc", getDoc());
return String.valueOf(script.apply(vars));
}
};
}
}
}

View File

@ -37,11 +37,13 @@ public abstract class MockScriptPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
return new MockScriptEngine(pluginScriptLang(), pluginScripts(), pluginContextCompilers());
return new MockScriptEngine(pluginScriptLang(), pluginScripts(), nonDeterministicPluginScripts(), pluginContextCompilers());
}
protected abstract Map<String, Function<Map<String, Object>, Object>> pluginScripts();
protected Map<String, Function<Map<String, Object>, Object>> nonDeterministicPluginScripts() { return Collections.emptyMap(); }
protected Map<ScriptContext<?>, MockScriptEngine.ContextCompiler> pluginContextCompilers() {
return Collections.emptyMap();
}