Template cleanup:
* Removed `Template` class and unified script & template parsing logic. Templates are scripts, so they should be defined as a script. Unless there will be separate template infrastructure, templates should share as much code as possible with scripts. * Removed ScriptParseException in favour for ElasticsearchParseException * Moved TemplateQueryBuilder to lang-mustache module because this query is hard coded to work with mustache only
This commit is contained in:
parent
798ee177ed
commit
e0ebf5da1c
|
@ -658,8 +658,7 @@ public class ElasticsearchException extends RuntimeException implements ToXConte
|
||||||
org.elasticsearch.search.aggregations.InvalidAggregationPathException::new, 121),
|
org.elasticsearch.search.aggregations.InvalidAggregationPathException::new, 121),
|
||||||
INDEX_ALREADY_EXISTS_EXCEPTION(org.elasticsearch.indices.IndexAlreadyExistsException.class,
|
INDEX_ALREADY_EXISTS_EXCEPTION(org.elasticsearch.indices.IndexAlreadyExistsException.class,
|
||||||
org.elasticsearch.indices.IndexAlreadyExistsException::new, 123),
|
org.elasticsearch.indices.IndexAlreadyExistsException::new, 123),
|
||||||
SCRIPT_PARSE_EXCEPTION(org.elasticsearch.script.Script.ScriptParseException.class,
|
// 124 used to be Script.ScriptParseException
|
||||||
org.elasticsearch.script.Script.ScriptParseException::new, 124),
|
|
||||||
HTTP_ON_TRANSPORT_EXCEPTION(TcpTransport.HttpOnTransportException.class,
|
HTTP_ON_TRANSPORT_EXCEPTION(TcpTransport.HttpOnTransportException.class,
|
||||||
TcpTransport.HttpOnTransportException::new, 125),
|
TcpTransport.HttpOnTransportException::new, 125),
|
||||||
MAPPER_PARSING_EXCEPTION(org.elasticsearch.index.mapper.MapperParsingException.class,
|
MAPPER_PARSING_EXCEPTION(org.elasticsearch.index.mapper.MapperParsingException.class,
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||||
import org.elasticsearch.indices.TermsLookup;
|
import org.elasticsearch.indices.TermsLookup;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -621,27 +620,6 @@ public abstract class QueryBuilders {
|
||||||
return new WrapperQueryBuilder(source);
|
return new WrapperQueryBuilder(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Facilitates creating template query requests using an inline script
|
|
||||||
*/
|
|
||||||
public static TemplateQueryBuilder templateQuery(Template template) {
|
|
||||||
return new TemplateQueryBuilder(template);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Facilitates creating template query requests using an inline script
|
|
||||||
*/
|
|
||||||
public static TemplateQueryBuilder templateQuery(String template, Map<String, Object> vars) {
|
|
||||||
return new TemplateQueryBuilder(new Template(template, ScriptService.ScriptType.INLINE, null, null, vars));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Facilitates creating template query requests
|
|
||||||
*/
|
|
||||||
public static TemplateQueryBuilder templateQuery(String template, ScriptService.ScriptType templateType, Map<String, Object> vars) {
|
|
||||||
return new TemplateQueryBuilder(new Template(template, templateType, null, null, vars));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filter based on doc/mapping type.
|
* A filter based on doc/mapping type.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,184 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
|
||||||
import org.elasticsearch.script.Script.ScriptField;
|
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
|
||||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
public abstract class AbstractScriptParser<S extends Script> {
|
|
||||||
|
|
||||||
public abstract String parseInlineScript(XContentParser parser) throws IOException;
|
|
||||||
|
|
||||||
protected abstract S createScript(String script, ScriptType type, String lang, Map<String, Object> params);
|
|
||||||
|
|
||||||
protected abstract S createSimpleScript(XContentParser parser) throws IOException;
|
|
||||||
|
|
||||||
public S parse(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException {
|
|
||||||
|
|
||||||
XContentParser.Token token = parser.currentToken();
|
|
||||||
// If the parser hasn't yet been pushed to the first token, do it now
|
|
||||||
if (token == null) {
|
|
||||||
token = parser.nextToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token == XContentParser.Token.VALUE_STRING) {
|
|
||||||
return createSimpleScript(parser);
|
|
||||||
}
|
|
||||||
if (token != XContentParser.Token.START_OBJECT) {
|
|
||||||
throw new ScriptParseException("expected a string value or an object, but found [{}] instead", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
String script = null;
|
|
||||||
ScriptType type = null;
|
|
||||||
String lang = getDefaultScriptLang();
|
|
||||||
Map<String, Object> params = null;
|
|
||||||
|
|
||||||
String currentFieldName = null;
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
|
||||||
currentFieldName = parser.currentName();
|
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, ScriptType.INLINE.getParseField())) {
|
|
||||||
type = ScriptType.INLINE;
|
|
||||||
script = parseInlineScript(parser);
|
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, ScriptType.FILE.getParseField())) {
|
|
||||||
type = ScriptType.FILE;
|
|
||||||
if (token == XContentParser.Token.VALUE_STRING) {
|
|
||||||
script = parser.text();
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("expected a string value for field [{}], but found [{}]", currentFieldName, token);
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, ScriptType.STORED.getParseField())) {
|
|
||||||
type = ScriptType.STORED;
|
|
||||||
if (token == XContentParser.Token.VALUE_STRING) {
|
|
||||||
script = parser.text();
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("expected a string value for field [{}], but found [{}]", currentFieldName, token);
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, ScriptField.LANG)) {
|
|
||||||
if (token == XContentParser.Token.VALUE_STRING) {
|
|
||||||
lang = parser.text();
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("expected a string value for field [{}], but found [{}]", currentFieldName, token);
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, ScriptField.PARAMS)) {
|
|
||||||
if (token == XContentParser.Token.START_OBJECT) {
|
|
||||||
params = parser.map();
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("expected an object for field [{}], but found [{}]", currentFieldName, token);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("unexpected field [{}]", currentFieldName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (script == null) {
|
|
||||||
throw new ScriptParseException("expected one of [{}], [{}] or [{}] fields, but found none", ScriptType.INLINE.getParseField()
|
|
||||||
.getPreferredName(), ScriptType.FILE.getParseField().getPreferredName(), ScriptType.STORED.getParseField()
|
|
||||||
.getPreferredName());
|
|
||||||
}
|
|
||||||
assert type != null : "if script is not null, type should definitely not be null";
|
|
||||||
return createScript(script, type, lang, params);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the default script language for this parser or <code>null</code>
|
|
||||||
* to use the default set in the ScriptService
|
|
||||||
*/
|
|
||||||
protected String getDefaultScriptLang() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public S parse(Map<String, Object> config, boolean removeMatchedEntries, ParseFieldMatcher parseFieldMatcher) {
|
|
||||||
String script = null;
|
|
||||||
ScriptType type = null;
|
|
||||||
String lang = null;
|
|
||||||
Map<String, Object> params = null;
|
|
||||||
for (Iterator<Entry<String, Object>> itr = config.entrySet().iterator(); itr.hasNext();) {
|
|
||||||
Entry<String, Object> entry = itr.next();
|
|
||||||
String parameterName = entry.getKey();
|
|
||||||
Object parameterValue = entry.getValue();
|
|
||||||
if (parseFieldMatcher.match(parameterName, ScriptField.LANG)) {
|
|
||||||
if (parameterValue instanceof String || parameterValue == null) {
|
|
||||||
lang = (String) parameterValue;
|
|
||||||
if (removeMatchedEntries) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("Value must be of type String: [" + parameterName + "]");
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(parameterName, ScriptField.PARAMS)) {
|
|
||||||
if (parameterValue instanceof Map || parameterValue == null) {
|
|
||||||
params = (Map<String, Object>) parameterValue;
|
|
||||||
if (removeMatchedEntries) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("Value must be of type String: [" + parameterName + "]");
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(parameterName, ScriptType.INLINE.getParseField())) {
|
|
||||||
if (parameterValue instanceof String || parameterValue == null) {
|
|
||||||
script = (String) parameterValue;
|
|
||||||
type = ScriptType.INLINE;
|
|
||||||
if (removeMatchedEntries) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("Value must be of type String: [" + parameterName + "]");
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(parameterName, ScriptType.FILE.getParseField())) {
|
|
||||||
if (parameterValue instanceof String || parameterValue == null) {
|
|
||||||
script = (String) parameterValue;
|
|
||||||
type = ScriptType.FILE;
|
|
||||||
if (removeMatchedEntries) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("Value must be of type String: [" + parameterName + "]");
|
|
||||||
}
|
|
||||||
} else if (parseFieldMatcher.match(parameterName, ScriptType.STORED.getParseField())) {
|
|
||||||
if (parameterValue instanceof String || parameterValue == null) {
|
|
||||||
script = (String) parameterValue;
|
|
||||||
type = ScriptType.STORED;
|
|
||||||
if (removeMatchedEntries) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("Value must be of type String: [" + parameterName + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (script == null) {
|
|
||||||
throw new ScriptParseException("expected one of [{}], [{}] or [{}] fields, but found none", ScriptType.INLINE.getParseField()
|
|
||||||
.getPreferredName(), ScriptType.FILE.getParseField().getPreferredName(), ScriptType.STORED.getParseField()
|
|
||||||
.getPreferredName());
|
|
||||||
}
|
|
||||||
assert type != null : "if script is not null, type should definitely not be null";
|
|
||||||
return createScript(script, type, lang, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -19,79 +19,75 @@
|
||||||
|
|
||||||
package org.elasticsearch.script;
|
package org.elasticsearch.script;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
import org.elasticsearch.common.ParseFieldMatcher;
|
||||||
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Script holds all the parameters necessary to compile or find in cache and then execute a script.
|
* Script holds all the parameters necessary to compile or find in cache and then execute a script.
|
||||||
*/
|
*/
|
||||||
public class Script implements ToXContent, Writeable {
|
public final class Script implements ToXContent, Writeable {
|
||||||
|
|
||||||
public static final ScriptType DEFAULT_TYPE = ScriptType.INLINE;
|
public static final ScriptType DEFAULT_TYPE = ScriptType.INLINE;
|
||||||
private static final ScriptParser PARSER = new ScriptParser();
|
|
||||||
|
|
||||||
private String script;
|
private String script;
|
||||||
@Nullable private ScriptType type;
|
private ScriptType type;
|
||||||
@Nullable private String lang;
|
@Nullable private String lang;
|
||||||
@Nullable private Map<String, Object> params;
|
@Nullable private Map<String, Object> params;
|
||||||
|
@Nullable private XContentType contentType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for simple inline script. The script will have no lang or
|
* Constructor for simple inline script. The script will have no lang or params set.
|
||||||
* params set.
|
|
||||||
*
|
*
|
||||||
* @param script
|
* @param script The inline script to execute.
|
||||||
* The inline script to execute.
|
|
||||||
*/
|
*/
|
||||||
public Script(String script) {
|
public Script(String script) {
|
||||||
this(script, null);
|
this(script, ScriptType.INLINE, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Script(String script, ScriptType type, @Nullable String lang, @Nullable Map<String, ?> params) {
|
||||||
* For sub-classes to use to override the default language
|
this(script, type, lang, params, null);
|
||||||
*/
|
|
||||||
protected Script(String script, String lang) {
|
|
||||||
this(script, ScriptType.INLINE, lang, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Script.
|
* Constructor for Script.
|
||||||
*
|
*
|
||||||
* @param script
|
* @param script The cache key of the script to be compiled/executed. For inline scripts this is the actual
|
||||||
* The cache key of the script to be compiled/executed. For
|
* script source code. For indexed scripts this is the id used in the request. For on file
|
||||||
* inline scripts this is the actual script source code. For
|
* scripts this is the file name.
|
||||||
* indexed scripts this is the id used in the request. For on
|
* @param type The type of script -- dynamic, stored, or file.
|
||||||
* file scripts this is the file name.
|
* @param lang The language of the script to be compiled/executed.
|
||||||
* @param type
|
* @param params The map of parameters the script will be executed with.
|
||||||
* The type of script -- dynamic, indexed, or file.
|
* @param contentType The {@link XContentType} of the script. Only relevant for inline scripts that have not been
|
||||||
* @param lang
|
* defined as a plain string, but as json or yaml content. This class needs this information
|
||||||
* The language of the script to be compiled/executed.
|
* when serializing the script back to xcontent.
|
||||||
* @param params
|
|
||||||
* The map of parameters the script will be executed with.
|
|
||||||
*/
|
*/
|
||||||
public Script(String script, ScriptType type, @Nullable String lang, @Nullable Map<String, ? extends Object> params) {
|
@SuppressWarnings("unchecked")
|
||||||
if (script == null) {
|
public Script(String script, ScriptType type, @Nullable String lang, @Nullable Map<String, ?> params,
|
||||||
throw new IllegalArgumentException("The parameter script (String) must not be null in Script.");
|
@Nullable XContentType contentType) {
|
||||||
|
if (contentType != null && type != ScriptType.INLINE) {
|
||||||
|
throw new IllegalArgumentException("The parameter contentType only makes sense for inline scripts");
|
||||||
}
|
}
|
||||||
if (type == null) {
|
this.script = Objects.requireNonNull(script);
|
||||||
throw new IllegalArgumentException("The parameter type (ScriptType) must not be null in Script.");
|
this.type = Objects.requireNonNull(type);
|
||||||
}
|
|
||||||
this.script = script;
|
|
||||||
this.type = type;
|
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
this.params = (Map<String, Object>)params;
|
this.params = (Map<String, Object>) params;
|
||||||
|
this.contentType = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Script(StreamInput in) throws IOException {
|
public Script(StreamInput in) throws IOException {
|
||||||
|
@ -100,13 +96,14 @@ public class Script implements ToXContent, Writeable {
|
||||||
type = ScriptType.readFrom(in);
|
type = ScriptType.readFrom(in);
|
||||||
}
|
}
|
||||||
lang = in.readOptionalString();
|
lang = in.readOptionalString();
|
||||||
|
params = in.readMap();
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
params = in.readMap();
|
contentType = XContentType.readFrom(in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(script);
|
out.writeString(script);
|
||||||
boolean hasType = type != null;
|
boolean hasType = type != null;
|
||||||
out.writeBoolean(hasType);
|
out.writeBoolean(hasType);
|
||||||
|
@ -114,16 +111,14 @@ public class Script implements ToXContent, Writeable {
|
||||||
ScriptType.writeTo(type, out);
|
ScriptType.writeTo(type, out);
|
||||||
}
|
}
|
||||||
out.writeOptionalString(lang);
|
out.writeOptionalString(lang);
|
||||||
boolean hasParams = params != null;
|
out.writeMap(params);
|
||||||
out.writeBoolean(hasParams);
|
boolean hasContentType = contentType != null;
|
||||||
if (hasParams) {
|
out.writeBoolean(hasContentType);
|
||||||
out.writeMap(params);
|
if (hasContentType) {
|
||||||
|
XContentType.writeTo(contentType, out);
|
||||||
}
|
}
|
||||||
doWriteTo(out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doWriteTo(StreamOutput out) throws IOException {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for getting the script.
|
* Method for getting the script.
|
||||||
* @return The cache key of the script to be compiled/executed. For dynamic scripts this is the actual
|
* @return The cache key of the script to be compiled/executed. For dynamic scripts this is the actual
|
||||||
|
@ -137,7 +132,7 @@ public class Script implements ToXContent, Writeable {
|
||||||
/**
|
/**
|
||||||
* Method for getting the type.
|
* Method for getting the type.
|
||||||
*
|
*
|
||||||
* @return The type of script -- inline, indexed, or file.
|
* @return The type of script -- inline, stored, or file.
|
||||||
*/
|
*/
|
||||||
public ScriptType getType() {
|
public ScriptType getType() {
|
||||||
return type == null ? DEFAULT_TYPE : type;
|
return type == null ? DEFAULT_TYPE : type;
|
||||||
|
@ -161,14 +156,25 @@ public class Script implements ToXContent, Writeable {
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The content type of the script if it is an inline script and the script has been defined as json
|
||||||
|
* or yaml content instead of a plain string.
|
||||||
|
*/
|
||||||
|
public XContentType getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final XContentBuilder toXContent(XContentBuilder builder, Params builderParams) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params builderParams) throws IOException {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
return builder.value(script);
|
return builder.value(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
scriptFieldToXContent(script, type, builder, builderParams);
|
if (type == ScriptType.INLINE && contentType != null && builder.contentType() == contentType) {
|
||||||
|
builder.rawField(type.getParseField().getPreferredName(), new BytesArray(script));
|
||||||
|
} else {
|
||||||
|
builder.field(type.getParseField().getPreferredName(), script);
|
||||||
|
}
|
||||||
if (lang != null) {
|
if (lang != null) {
|
||||||
builder.field(ScriptField.LANG.getPreferredName(), lang);
|
builder.field(ScriptField.LANG.getPreferredName(), lang);
|
||||||
}
|
}
|
||||||
|
@ -179,29 +185,80 @@ public class Script implements ToXContent, Writeable {
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected XContentBuilder scriptFieldToXContent(String script, ScriptType type, XContentBuilder builder, Params builderParams)
|
|
||||||
throws IOException {
|
|
||||||
builder.field(type.getParseField().getPreferredName(), script);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Script parse(Map<String, Object> config, boolean removeMatchedEntries, ParseFieldMatcher parseFieldMatcher) {
|
|
||||||
return PARSER.parse(config, removeMatchedEntries, parseFieldMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Script parse(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException {
|
public static Script parse(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException {
|
||||||
return PARSER.parse(parser, parseFieldMatcher);
|
return parse(parser, parseFieldMatcher, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Script parse(XContentParser parser, ParseFieldMatcher parseFieldMatcher, @Nullable String lang) throws IOException {
|
||||||
|
XContentParser.Token token = parser.currentToken();
|
||||||
|
// If the parser hasn't yet been pushed to the first token, do it now
|
||||||
|
if (token == null) {
|
||||||
|
token = parser.nextToken();
|
||||||
|
}
|
||||||
|
if (token == XContentParser.Token.VALUE_STRING) {
|
||||||
|
return new Script(parser.text());
|
||||||
|
}
|
||||||
|
if (token != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new ElasticsearchParseException("expected a string value or an object, but found [{}] instead", token);
|
||||||
|
}
|
||||||
|
String script = null;
|
||||||
|
ScriptType type = null;
|
||||||
|
Map<String, Object> params = null;
|
||||||
|
XContentType contentType = null;
|
||||||
|
String cfn = null;
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
cfn = parser.currentName();
|
||||||
|
} else if (parseFieldMatcher.match(cfn, ScriptType.INLINE.getParseField())) {
|
||||||
|
type = ScriptType.INLINE;
|
||||||
|
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||||
|
contentType = parser.contentType();
|
||||||
|
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
|
||||||
|
script = builder.copyCurrentStructure(parser).bytes().utf8ToString();
|
||||||
|
} else {
|
||||||
|
script = parser.text();
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(cfn, ScriptType.FILE.getParseField())) {
|
||||||
|
type = ScriptType.FILE;
|
||||||
|
if (token == XContentParser.Token.VALUE_STRING) {
|
||||||
|
script = parser.text();
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("expected a string value for field [{}], but found [{}]", cfn, token);
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(cfn, ScriptType.STORED.getParseField())) {
|
||||||
|
type = ScriptType.STORED;
|
||||||
|
if (token == XContentParser.Token.VALUE_STRING) {
|
||||||
|
script = parser.text();
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("expected a string value for field [{}], but found [{}]", cfn, token);
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(cfn, ScriptField.LANG)) {
|
||||||
|
if (token == XContentParser.Token.VALUE_STRING) {
|
||||||
|
lang = parser.text();
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("expected a string value for field [{}], but found [{}]", cfn, token);
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(cfn, ScriptField.PARAMS)) {
|
||||||
|
if (token == XContentParser.Token.START_OBJECT) {
|
||||||
|
params = parser.map();
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("expected an object for field [{}], but found [{}]", cfn, token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("unexpected field [{}]", cfn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (script == null) {
|
||||||
|
throw new ElasticsearchParseException("expected one of [{}], [{}] or [{}] fields, but found none",
|
||||||
|
ScriptType.INLINE.getParseField() .getPreferredName(), ScriptType.FILE.getParseField().getPreferredName(),
|
||||||
|
ScriptType.STORED.getParseField() .getPreferredName());
|
||||||
|
}
|
||||||
|
return new Script(script, type, lang, params, contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
return Objects.hash(lang, params, script, type, contentType);
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((lang == null) ? 0 : lang.hashCode());
|
|
||||||
result = prime * result + ((params == null) ? 0 : params.hashCode());
|
|
||||||
result = prime * result + ((script == null) ? 0 : script.hashCode());
|
|
||||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,52 +267,18 @@ public class Script implements ToXContent, Writeable {
|
||||||
if (obj == null) return false;
|
if (obj == null) return false;
|
||||||
if (getClass() != obj.getClass()) return false;
|
if (getClass() != obj.getClass()) return false;
|
||||||
Script other = (Script) obj;
|
Script other = (Script) obj;
|
||||||
if (lang == null) {
|
|
||||||
if (other.lang != null) return false;
|
return Objects.equals(lang, other.lang) &&
|
||||||
} else {
|
Objects.equals(params, other.params) &&
|
||||||
if (!lang.equals(other.lang)) return false;
|
Objects.equals(script, other.script) &&
|
||||||
}
|
Objects.equals(type, other.type) &&
|
||||||
if (params == null) {
|
Objects.equals(contentType, other.contentType);
|
||||||
if (other.params != null) return false;
|
|
||||||
} else {
|
|
||||||
if (!params.equals(other.params)) return false;
|
|
||||||
}
|
|
||||||
if (script == null) {
|
|
||||||
if (other.script != null) return false;
|
|
||||||
} else {
|
|
||||||
if (!script.equals(other.script)) return false;
|
|
||||||
}
|
|
||||||
if (type != other.type) return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[script: " + script + ", type: " + type.getParseField().getPreferredName() + ", lang: " + lang + ", params: " + params
|
return "[script: " + script + ", type: " + type.getParseField().getPreferredName() + ", lang: "
|
||||||
+ "]";
|
+ lang + ", params: " + params + "]";
|
||||||
}
|
|
||||||
|
|
||||||
private static class ScriptParser extends AbstractScriptParser<Script> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Script createSimpleScript(XContentParser parser) throws IOException {
|
|
||||||
if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
|
|
||||||
return new Script(parser.text());
|
|
||||||
} else {
|
|
||||||
throw new ScriptParseException("expected a string value for field [{}], but found [{}]", parser.currentName(),
|
|
||||||
parser.currentToken());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Script createScript(String script, ScriptType type, String lang, Map<String, Object> params) {
|
|
||||||
return new Script(script, type, lang, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String parseInlineScript(XContentParser parser) throws IOException {
|
|
||||||
return parser.text();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ScriptField {
|
public interface ScriptField {
|
||||||
|
@ -264,14 +287,4 @@ public class Script implements ToXContent, Writeable {
|
||||||
ParseField PARAMS = new ParseField("params");
|
ParseField PARAMS = new ParseField("params");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ScriptParseException extends ElasticsearchException {
|
|
||||||
|
|
||||||
public ScriptParseException(String msg, Object... args) {
|
|
||||||
super(LoggerMessageFormat.format(msg, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScriptParseException(StreamInput in) throws IOException{
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 org.elasticsearch.common.Nullable;
|
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
|
||||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Template extends Script {
|
|
||||||
|
|
||||||
/** Default templating language */
|
|
||||||
public static final String DEFAULT_LANG = "mustache";
|
|
||||||
|
|
||||||
private XContentType contentType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for simple inline template. The template will have no lang,
|
|
||||||
* content type or params set.
|
|
||||||
*
|
|
||||||
* @param template
|
|
||||||
* The inline template.
|
|
||||||
*/
|
|
||||||
public Template(String template) {
|
|
||||||
super(template, DEFAULT_LANG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for Template.
|
|
||||||
*
|
|
||||||
* @param template
|
|
||||||
* The cache key of the template to be compiled/executed. For
|
|
||||||
* inline templates this is the actual templates source code. For
|
|
||||||
* indexed templates this is the id used in the request. For on
|
|
||||||
* file templates this is the file name.
|
|
||||||
* @param type
|
|
||||||
* The type of template -- dynamic, indexed, or file.
|
|
||||||
* @param lang
|
|
||||||
* The language of the template to be compiled/executed.
|
|
||||||
* @param xContentType
|
|
||||||
* The {@link XContentType} of the template.
|
|
||||||
* @param params
|
|
||||||
* The map of parameters the template will be executed with.
|
|
||||||
*/
|
|
||||||
public Template(String template, ScriptType type, @Nullable String lang, @Nullable XContentType xContentType,
|
|
||||||
@Nullable Map<String, Object> params) {
|
|
||||||
super(template, type, lang == null ? DEFAULT_LANG : lang, params);
|
|
||||||
this.contentType = xContentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Template(StreamInput in) throws IOException {
|
|
||||||
super(in);
|
|
||||||
if (in.readBoolean()) {
|
|
||||||
this.contentType = XContentType.readFrom(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
|
||||||
boolean hasContentType = contentType != null;
|
|
||||||
out.writeBoolean(hasContentType);
|
|
||||||
if (hasContentType) {
|
|
||||||
XContentType.writeTo(contentType, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for getting the {@link XContentType} of the template.
|
|
||||||
*
|
|
||||||
* @return The {@link XContentType} of the template.
|
|
||||||
*/
|
|
||||||
public XContentType getContentType() {
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected XContentBuilder scriptFieldToXContent(String template, ScriptType type, XContentBuilder builder, Params builderParams)
|
|
||||||
throws IOException {
|
|
||||||
if (type == ScriptType.INLINE && contentType != null && builder.contentType() == contentType) {
|
|
||||||
builder.rawField(type.getParseField().getPreferredName(), new BytesArray(template));
|
|
||||||
} else {
|
|
||||||
builder.field(type.getParseField().getPreferredName(), template);
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Script parse(Map<String, Object> config, boolean removeMatchedEntries, ParseFieldMatcher parseFieldMatcher) {
|
|
||||||
return new TemplateParser(DEFAULT_LANG).parse(config, removeMatchedEntries, parseFieldMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Template parse(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException {
|
|
||||||
return new TemplateParser(DEFAULT_LANG).parse(parser, parseFieldMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = super.hashCode();
|
|
||||||
result = prime * result + ((contentType == null) ? 0 : contentType.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) return true;
|
|
||||||
if (!super.equals(obj)) return false;
|
|
||||||
if (getClass() != obj.getClass()) return false;
|
|
||||||
Template other = (Template) obj;
|
|
||||||
if (contentType != other.contentType) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TemplateParser extends AbstractScriptParser<Template> {
|
|
||||||
|
|
||||||
private XContentType contentType = null;
|
|
||||||
private String defaultLang;
|
|
||||||
|
|
||||||
public TemplateParser(String defaultLang) {
|
|
||||||
this.defaultLang = defaultLang;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Template createSimpleScript(XContentParser parser) throws IOException {
|
|
||||||
return new Template(String.valueOf(parser.objectText()), ScriptType.INLINE, DEFAULT_LANG, contentType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Template createScript(String script, ScriptType type, String lang, Map<String, Object> params) {
|
|
||||||
return new Template(script, type, lang, contentType, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String parseInlineScript(XContentParser parser) throws IOException {
|
|
||||||
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
|
||||||
contentType = parser.contentType();
|
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
|
|
||||||
return builder.copyCurrentStructure(parser).bytes().utf8ToString();
|
|
||||||
} else {
|
|
||||||
return parser.text();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getDefaultScriptLang() {
|
|
||||||
return defaultLang;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -75,7 +75,6 @@ import org.elasticsearch.index.query.SpanNotQueryBuilder;
|
||||||
import org.elasticsearch.index.query.SpanOrQueryBuilder;
|
import org.elasticsearch.index.query.SpanOrQueryBuilder;
|
||||||
import org.elasticsearch.index.query.SpanTermQueryBuilder;
|
import org.elasticsearch.index.query.SpanTermQueryBuilder;
|
||||||
import org.elasticsearch.index.query.SpanWithinQueryBuilder;
|
import org.elasticsearch.index.query.SpanWithinQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TemplateQueryBuilder;
|
|
||||||
import org.elasticsearch.index.query.TermQueryBuilder;
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TermsQueryBuilder;
|
import org.elasticsearch.index.query.TermsQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TypeQueryBuilder;
|
import org.elasticsearch.index.query.TypeQueryBuilder;
|
||||||
|
@ -795,7 +794,6 @@ public class SearchModule extends AbstractModule {
|
||||||
registerQuery(FunctionScoreQueryBuilder::new, c -> FunctionScoreQueryBuilder.fromXContent(scoreFunctionParserRegistry, c),
|
registerQuery(FunctionScoreQueryBuilder::new, c -> FunctionScoreQueryBuilder.fromXContent(scoreFunctionParserRegistry, c),
|
||||||
FunctionScoreQueryBuilder.QUERY_NAME_FIELD);
|
FunctionScoreQueryBuilder.QUERY_NAME_FIELD);
|
||||||
registerQuery(SimpleQueryStringBuilder::new, SimpleQueryStringBuilder::fromXContent, SimpleQueryStringBuilder.QUERY_NAME_FIELD);
|
registerQuery(SimpleQueryStringBuilder::new, SimpleQueryStringBuilder::fromXContent, SimpleQueryStringBuilder.QUERY_NAME_FIELD);
|
||||||
registerQuery(TemplateQueryBuilder::new, TemplateQueryBuilder::fromXContent, TemplateQueryBuilder.QUERY_NAME_FIELD);
|
|
||||||
registerQuery(TypeQueryBuilder::new, TypeQueryBuilder::fromXContent, TypeQueryBuilder.QUERY_NAME_FIELD);
|
registerQuery(TypeQueryBuilder::new, TypeQueryBuilder::fromXContent, TypeQueryBuilder.QUERY_NAME_FIELD);
|
||||||
registerQuery(ScriptQueryBuilder::new, ScriptQueryBuilder::fromXContent, ScriptQueryBuilder.QUERY_NAME_FIELD);
|
registerQuery(ScriptQueryBuilder::new, ScriptQueryBuilder::fromXContent, ScriptQueryBuilder.QUERY_NAME_FIELD);
|
||||||
registerQuery(GeoDistanceQueryBuilder::new, GeoDistanceQueryBuilder::fromXContent, GeoDistanceQueryBuilder.QUERY_NAME_FIELD);
|
registerQuery(GeoDistanceQueryBuilder::new, GeoDistanceQueryBuilder::fromXContent, GeoDistanceQueryBuilder.QUERY_NAME_FIELD);
|
||||||
|
|
|
@ -36,8 +36,9 @@ import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.script.CompiledScript;
|
import org.elasticsearch.script.CompiledScript;
|
||||||
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
import org.elasticsearch.script.ScriptContext;
|
||||||
import org.elasticsearch.script.Template;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.suggest.SuggestUtils;
|
import org.elasticsearch.search.suggest.SuggestUtils;
|
||||||
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
||||||
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
|
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
|
||||||
|
@ -87,7 +88,7 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
|
||||||
private int tokenLimit = NoisyChannelSpellChecker.DEFAULT_TOKEN_LIMIT;
|
private int tokenLimit = NoisyChannelSpellChecker.DEFAULT_TOKEN_LIMIT;
|
||||||
private String preTag;
|
private String preTag;
|
||||||
private String postTag;
|
private String postTag;
|
||||||
private Template collateQuery;
|
private Script collateQuery;
|
||||||
private Map<String, Object> collateParams;
|
private Map<String, Object> collateParams;
|
||||||
private boolean collatePrune = PhraseSuggestionContext.DEFAULT_COLLATE_PRUNE;
|
private boolean collatePrune = PhraseSuggestionContext.DEFAULT_COLLATE_PRUNE;
|
||||||
private SmoothingModel model;
|
private SmoothingModel model;
|
||||||
|
@ -135,7 +136,7 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
|
||||||
postTag = in.readOptionalString();
|
postTag = in.readOptionalString();
|
||||||
separator = in.readString();
|
separator = in.readString();
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
collateQuery = new Template(in);
|
collateQuery = new Script(in);
|
||||||
}
|
}
|
||||||
collateParams = in.readMap();
|
collateParams = in.readMap();
|
||||||
collatePrune = in.readOptionalBoolean();
|
collatePrune = in.readOptionalBoolean();
|
||||||
|
@ -389,14 +390,14 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
|
||||||
* Sets a query used for filtering out suggested phrases (collation).
|
* Sets a query used for filtering out suggested phrases (collation).
|
||||||
*/
|
*/
|
||||||
public PhraseSuggestionBuilder collateQuery(String collateQuery) {
|
public PhraseSuggestionBuilder collateQuery(String collateQuery) {
|
||||||
this.collateQuery = new Template(collateQuery);
|
this.collateQuery = new Script(collateQuery, ScriptService.ScriptType.INLINE, "mustache", Collections.emptyMap());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a query used for filtering out suggested phrases (collation).
|
* Sets a query used for filtering out suggested phrases (collation).
|
||||||
*/
|
*/
|
||||||
public PhraseSuggestionBuilder collateQuery(Template collateQueryTemplate) {
|
public PhraseSuggestionBuilder collateQuery(Script collateQueryTemplate) {
|
||||||
this.collateQuery = collateQueryTemplate;
|
this.collateQuery = collateQueryTemplate;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +405,7 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
|
||||||
/**
|
/**
|
||||||
* gets the query used for filtering out suggested phrases (collation).
|
* gets the query used for filtering out suggested phrases (collation).
|
||||||
*/
|
*/
|
||||||
public Template collateQuery() {
|
public Script collateQuery() {
|
||||||
return this.collateQuery;
|
return this.collateQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,7 +564,7 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
|
||||||
"suggester[phrase][collate] query already set, doesn't support additional ["
|
"suggester[phrase][collate] query already set, doesn't support additional ["
|
||||||
+ currentFieldName + "]");
|
+ currentFieldName + "]");
|
||||||
}
|
}
|
||||||
Template template = Template.parse(parser, parseFieldMatcher);
|
Script template = Script.parse(parser, parseFieldMatcher, "mustache");
|
||||||
tmpSuggestion.collateQuery(template);
|
tmpSuggestion.collateQuery(template);
|
||||||
} else if (parseFieldMatcher.match(currentFieldName, PhraseSuggestionBuilder.COLLATE_QUERY_PARAMS)) {
|
} else if (parseFieldMatcher.match(currentFieldName, PhraseSuggestionBuilder.COLLATE_QUERY_PARAMS)) {
|
||||||
tmpSuggestion.collateParams(parser.map());
|
tmpSuggestion.collateParams(parser.map());
|
||||||
|
|
|
@ -771,7 +771,7 @@ public class ExceptionSerializationTests extends ESTestCase {
|
||||||
ids.put(121, org.elasticsearch.search.aggregations.InvalidAggregationPathException.class);
|
ids.put(121, org.elasticsearch.search.aggregations.InvalidAggregationPathException.class);
|
||||||
ids.put(122, null);
|
ids.put(122, null);
|
||||||
ids.put(123, org.elasticsearch.indices.IndexAlreadyExistsException.class);
|
ids.put(123, org.elasticsearch.indices.IndexAlreadyExistsException.class);
|
||||||
ids.put(124, org.elasticsearch.script.Script.ScriptParseException.class);
|
ids.put(124, null);
|
||||||
ids.put(125, TcpTransport.HttpOnTransportException.class);
|
ids.put(125, TcpTransport.HttpOnTransportException.class);
|
||||||
ids.put(126, org.elasticsearch.index.mapper.MapperParsingException.class);
|
ids.put(126, org.elasticsearch.index.mapper.MapperParsingException.class);
|
||||||
ids.put(127, org.elasticsearch.search.SearchContextException.class);
|
ids.put(127, org.elasticsearch.search.SearchContextException.class);
|
||||||
|
|
|
@ -46,7 +46,6 @@ import org.elasticsearch.index.mapper.Uid;
|
||||||
import org.elasticsearch.index.mapper.internal.TypeFieldMapper;
|
import org.elasticsearch.index.mapper.internal.TypeFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
|
||||||
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||||
|
@ -310,7 +309,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
try {
|
try {
|
||||||
parseQuery(testQuery);
|
parseQuery(testQuery);
|
||||||
fail("some parsing exception expected for query: " + testQuery);
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
} catch (ParsingException | ScriptParseException | ElasticsearchParseException e) {
|
} catch (ParsingException | ElasticsearchParseException e) {
|
||||||
// different kinds of exception wordings depending on location
|
// different kinds of exception wordings depending on location
|
||||||
// of mutation, so no simple asserts possible here
|
// of mutation, so no simple asserts possible here
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|
|
@ -34,14 +34,11 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
|
||||||
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||||
import org.elasticsearch.search.sort.SortOrder;
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
import org.elasticsearch.test.AbstractQueryTestCase;
|
import org.elasticsearch.test.AbstractQueryTestCase;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -202,7 +199,7 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
||||||
try {
|
try {
|
||||||
parseQuery(testQuery);
|
parseQuery(testQuery);
|
||||||
fail("some parsing exception expected for query: " + testQuery);
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
} catch (ParsingException | ScriptParseException | ElasticsearchParseException e) {
|
} catch (ParsingException | ElasticsearchParseException e) {
|
||||||
// different kinds of exception wordings depending on location
|
// different kinds of exception wordings depending on location
|
||||||
// of mutation, so no simple asserts possible here
|
// of mutation, so no simple asserts possible here
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|
|
@ -196,7 +196,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
try {
|
try {
|
||||||
parseQuery(testQuery);
|
parseQuery(testQuery);
|
||||||
fail("some parsing exception expected for query: " + testQuery);
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
} catch (ParsingException | Script.ScriptParseException | ElasticsearchParseException e) {
|
} catch (ParsingException | ElasticsearchParseException e) {
|
||||||
// different kinds of exception wordings depending on location
|
// different kinds of exception wordings depending on location
|
||||||
// of mutation, so no simple asserts possible here
|
// of mutation, so no simple asserts possible here
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|
|
@ -76,7 +76,6 @@ import static org.elasticsearch.index.query.QueryBuilders.spanNotQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.spanOrQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.spanOrQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.spanTermQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.spanTermQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.spanWithinQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.spanWithinQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.templateQuery;
|
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.typeQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.typeQuery;
|
||||||
|
@ -359,13 +358,6 @@ public class QueryDSLDocumentationTests extends ESTestCase {
|
||||||
spanTermQuery("field1", "foo"));
|
spanTermQuery("field1", "foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testTemplate() {
|
|
||||||
templateQuery(
|
|
||||||
"gender_template",
|
|
||||||
ScriptType.STORED,
|
|
||||||
new HashMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testTerm() {
|
public void testTerm() {
|
||||||
termQuery("name", "kimchy");
|
termQuery("name", "kimchy");
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,7 +225,6 @@ public class SearchModuleTests extends ModuleTestCase {
|
||||||
"span_or",
|
"span_or",
|
||||||
"span_term",
|
"span_term",
|
||||||
"span_within",
|
"span_within",
|
||||||
"template",
|
|
||||||
"term",
|
"term",
|
||||||
"terms",
|
"terms",
|
||||||
"type",
|
"type",
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.search.suggest.phrase;
|
package org.elasticsearch.search.suggest.phrase;
|
||||||
|
|
||||||
import org.elasticsearch.script.Template;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
|
import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -109,7 +109,7 @@ public class PhraseSuggestionBuilderTests extends AbstractSuggestionBuilderTestC
|
||||||
builder.separator(randomValueOtherThan(builder.separator(), () -> randomAsciiOfLengthBetween(1, 10)));
|
builder.separator(randomValueOtherThan(builder.separator(), () -> randomAsciiOfLengthBetween(1, 10)));
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
Template collateQuery = builder.collateQuery();
|
Script collateQuery = builder.collateQuery();
|
||||||
if (collateQuery != null) {
|
if (collateQuery != null) {
|
||||||
builder.collateQuery(randomValueOtherThan(collateQuery.getScript(), () -> randomAsciiOfLengthBetween(3, 20)));
|
builder.collateQuery(randomValueOtherThan(collateQuery.getScript(), () -> randomAsciiOfLengthBetween(3, 20)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,7 +156,7 @@ public class PhraseSuggestionBuilderTests extends AbstractSuggestionBuilderTestC
|
||||||
assertEquals("suggestion field name is empty", e.getMessage());
|
assertEquals("suggestion field name is empty", e.getMessage());
|
||||||
|
|
||||||
PhraseSuggestionBuilder builder = new PhraseSuggestionBuilder(randomAsciiOfLengthBetween(2, 20));
|
PhraseSuggestionBuilder builder = new PhraseSuggestionBuilder(randomAsciiOfLengthBetween(2, 20));
|
||||||
|
|
||||||
e = expectThrows(IllegalArgumentException.class, () -> builder.gramSize(0));
|
e = expectThrows(IllegalArgumentException.class, () -> builder.gramSize(0));
|
||||||
assertEquals("gramSize must be >= 1", e.getMessage());
|
assertEquals("gramSize must be >= 1", e.getMessage());
|
||||||
e = expectThrows(IllegalArgumentException.class, () -> builder.gramSize(-1));
|
e = expectThrows(IllegalArgumentException.class, () -> builder.gramSize(-1));
|
||||||
|
|
|
@ -3,6 +3,21 @@
|
||||||
|
|
||||||
See {ref}/search-template.html[Search Template] documentation
|
See {ref}/search-template.html[Search Template] documentation
|
||||||
|
|
||||||
|
In order to use the `template` query from the Java API
|
||||||
|
the lang-mustache module dependency should be on the classpath and
|
||||||
|
the transport client should be loaded with the lang-mustache plugin:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
--------------------------------------------------
|
||||||
|
TransportClient transportClient = TransportClient.builder()
|
||||||
|
.settings(Settings.builder().put("node.name", "node"))
|
||||||
|
.addPlugin(MustachePlugin.class)
|
||||||
|
.build();
|
||||||
|
transportClient.addTransportAddress(
|
||||||
|
new InetSocketTransportAddress(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9300))
|
||||||
|
);
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
Define your template parameters as a `Map<String,Object>`:
|
Define your template parameters as a `Map<String,Object>`:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
|
@ -31,7 +46,7 @@ Define your template query:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
QueryBuilder qb = templateQuery(
|
QueryBuilder qb = new TemplateQueryBuilder(
|
||||||
"gender_template", <1>
|
"gender_template", <1>
|
||||||
ScriptService.ScriptType.FILE, <2>
|
ScriptService.ScriptType.FILE, <2>
|
||||||
template_params); <3>
|
template_params); <3>
|
||||||
|
@ -40,11 +55,14 @@ QueryBuilder qb = templateQuery(
|
||||||
<2> template stored on disk in `gender_template.mustache`
|
<2> template stored on disk in `gender_template.mustache`
|
||||||
<3> parameters
|
<3> parameters
|
||||||
|
|
||||||
You can also store your template in a special index named `.scripts`:
|
You can also store your template in the cluster state:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
client.preparePutIndexedScript("mustache", "template_gender",
|
client.admin().cluster().preparePutStoredScript()
|
||||||
|
.setScriptLang("mustache")
|
||||||
|
.setId("template_gender")
|
||||||
|
.setSource(new BytesArray(
|
||||||
"{\n" +
|
"{\n" +
|
||||||
" \"template\" : {\n" +
|
" \"template\" : {\n" +
|
||||||
" \"query\" : {\n" +
|
" \"query\" : {\n" +
|
||||||
|
@ -53,19 +71,19 @@ client.preparePutIndexedScript("mustache", "template_gender",
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
"}").get();
|
"}")).get();
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
To execute an indexed templates, use `ScriptService.ScriptType.INDEXED`:
|
To execute a stored templates, use `ScriptService.ScriptType.STORED`:
|
||||||
|
|
||||||
[source,java]
|
[source,java]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
QueryBuilder qb = templateQuery(
|
QueryBuilder qb = new TemplateQueryBuilder(
|
||||||
"gender_template", <1>
|
"gender_template", <1>
|
||||||
ScriptType.INDEXED, <2>
|
ScriptType.STORED, <2>
|
||||||
template_params); <3>
|
template_params); <3>
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
<1> template name
|
<1> template name
|
||||||
<2> template stored in an index
|
<2> template stored in the cluster state
|
||||||
<3> parameters
|
<3> parameters
|
||||||
|
|
||||||
|
|
|
@ -289,3 +289,24 @@ registers the "js" file extension for on-disk scripts.
|
||||||
==== Removed scripting query string parameters from update rest api
|
==== Removed scripting query string parameters from update rest api
|
||||||
|
|
||||||
The `script`, `script_id` and `scripting_upsert` query string parameters have been removed from the update api.
|
The `script`, `script_id` and `scripting_upsert` query string parameters have been removed from the update api.
|
||||||
|
|
||||||
|
==== Java transport client
|
||||||
|
|
||||||
|
The `TemplateQueryBuilder` has been moved to the `lang-mustache` module.
|
||||||
|
Therefor when using the `TemplateQueryBuilder` from the Java native client the
|
||||||
|
lang-mustache module should be on the classpath. Also the transport client
|
||||||
|
should load the lang-mustache module as plugin:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
--------------------------------------------------
|
||||||
|
TransportClient transportClient = TransportClient.builder()
|
||||||
|
.settings(Settings.builder().put("node.name", "node"))
|
||||||
|
.addPlugin(MustachePlugin.class)
|
||||||
|
.build();
|
||||||
|
transportClient.addTransportAddress(
|
||||||
|
new InetSocketTransportAddress(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9300))
|
||||||
|
);
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
Also the helper methods in `QueryBuilders` class that create a `TemplateQueryBuilder` instance have been removed,
|
||||||
|
instead the constructors on `TemplateQueryBuilder` should be used.
|
|
@ -42,7 +42,7 @@ public class ScriptProcessorTests extends ESTestCase {
|
||||||
int randomInt = randomInt();
|
int randomInt = randomInt();
|
||||||
ScriptService scriptService = mock(ScriptService.class);
|
ScriptService scriptService = mock(ScriptService.class);
|
||||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||||
Script script = mock(Script.class);
|
Script script = new Script("_script");
|
||||||
when(scriptService.compile(any(), any(), any())).thenReturn(compiledScript);
|
when(scriptService.compile(any(), any(), any())).thenReturn(compiledScript);
|
||||||
ExecutableScript executableScript = mock(ExecutableScript.class);
|
ExecutableScript executableScript = mock(ExecutableScript.class);
|
||||||
when(scriptService.executable(any(), any())).thenReturn(executableScript);
|
when(scriptService.executable(any(), any())).thenReturn(executableScript);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
esplugin {
|
esplugin {
|
||||||
description 'Mustache scripting integration for Elasticsearch'
|
description 'Mustache scripting integration for Elasticsearch'
|
||||||
classname 'org.elasticsearch.script.mustache.MustachePlugin'
|
classname 'org.elasticsearch.script.mustache.MustachePlugin'
|
||||||
|
hasClientJar = true // For the template query
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.rest.action.search.template;
|
package org.elasticsearch.rest.action.search.template;
|
||||||
|
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestDeleteStoredScriptAction;
|
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestDeleteStoredScriptAction;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
|
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
|
||||||
|
|
||||||
|
@ -38,6 +36,6 @@ public class RestDeleteSearchTemplateAction extends RestDeleteStoredScriptAction
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getScriptLang(RestRequest request) {
|
protected String getScriptLang(RestRequest request) {
|
||||||
return Template.DEFAULT_LANG;
|
return "mustache";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.rest.action.search.template;
|
package org.elasticsearch.rest.action.search.template;
|
||||||
|
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestGetStoredScriptAction;
|
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestGetStoredScriptAction;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
|
|
||||||
|
@ -40,7 +38,7 @@ public class RestGetSearchTemplateAction extends RestGetStoredScriptAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getScriptLang(RestRequest request) {
|
protected String getScriptLang(RestRequest request) {
|
||||||
return Template.DEFAULT_LANG;
|
return "mustache";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.rest.action.search.template;
|
package org.elasticsearch.rest.action.search.template;
|
||||||
|
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestPutStoredScriptAction;
|
import org.elasticsearch.rest.action.admin.cluster.storedscripts.RestPutStoredScriptAction;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.PUT;
|
import static org.elasticsearch.rest.RestRequest.Method.PUT;
|
||||||
|
@ -40,6 +38,6 @@ public class RestPutSearchTemplateAction extends RestPutStoredScriptAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getScriptLang(RestRequest request) {
|
protected String getScriptLang(RestRequest request) {
|
||||||
return Template.DEFAULT_LANG;
|
return "mustache";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.elasticsearch.rest.action.search.template.RestPutSearchTemplateAction
|
||||||
import org.elasticsearch.rest.action.search.template.RestRenderSearchTemplateAction;
|
import org.elasticsearch.rest.action.search.template.RestRenderSearchTemplateAction;
|
||||||
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
|
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
|
||||||
import org.elasticsearch.script.ScriptEngineService;
|
import org.elasticsearch.script.ScriptEngineService;
|
||||||
|
import org.elasticsearch.search.SearchModule;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -54,6 +55,10 @@ public class MustachePlugin extends Plugin implements ScriptPlugin, ActionPlugin
|
||||||
new ActionHandler<>(MultiSearchTemplateAction.INSTANCE, TransportMultiSearchTemplateAction.class));
|
new ActionHandler<>(MultiSearchTemplateAction.INSTANCE, TransportMultiSearchTemplateAction.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onModule(SearchModule module) {
|
||||||
|
module.registerQuery(TemplateQueryBuilder::new, TemplateQueryBuilder::fromXContent, TemplateQueryBuilder.QUERY_NAME_FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Class<? extends RestHandler>> getRestHandlers() {
|
public List<Class<? extends RestHandler>> getRestHandlers() {
|
||||||
return Arrays.asList(RestSearchTemplateAction.class, RestMultiSearchTemplateAction.class, RestGetSearchTemplateAction.class,
|
return Arrays.asList(RestSearchTemplateAction.class, RestMultiSearchTemplateAction.class, RestGetSearchTemplateAction.class,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.script.mustache;
|
||||||
|
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
|
@ -27,12 +27,21 @@ import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
|
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||||
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.script.ExecutableScript;
|
import org.elasticsearch.script.ExecutableScript;
|
||||||
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
import org.elasticsearch.script.ScriptContext;
|
||||||
import org.elasticsearch.script.Template;
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -45,20 +54,25 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
|
||||||
public static final ParseField QUERY_NAME_FIELD = new ParseField(NAME);
|
public static final ParseField QUERY_NAME_FIELD = new ParseField(NAME);
|
||||||
|
|
||||||
/** Template to fill. */
|
/** Template to fill. */
|
||||||
private final Template template;
|
private final Script template;
|
||||||
|
|
||||||
/**
|
public TemplateQueryBuilder(String template, ScriptService.ScriptType scriptType, Map<String, Object> params) {
|
||||||
* @param template
|
this.template = new Script(template, scriptType, "mustache", params);
|
||||||
* the template to use for that query.
|
}
|
||||||
* */
|
|
||||||
public TemplateQueryBuilder(Template template) {
|
public TemplateQueryBuilder(String template, ScriptService.ScriptType scriptType, Map<String, Object> params, XContentType ct) {
|
||||||
|
this.template = new Script(template, scriptType, "mustache", params, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for tests, so that mock script can be used:
|
||||||
|
TemplateQueryBuilder(Script template) {
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
throw new IllegalArgumentException("query template cannot be null");
|
throw new IllegalArgumentException("query template cannot be null");
|
||||||
}
|
}
|
||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Template template() {
|
public Script template() {
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +81,7 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
|
||||||
*/
|
*/
|
||||||
public TemplateQueryBuilder(StreamInput in) throws IOException {
|
public TemplateQueryBuilder(StreamInput in) throws IOException {
|
||||||
super(in);
|
super(in);
|
||||||
template = new Template(in);
|
template = new Script(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,7 +139,7 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
|
||||||
*/
|
*/
|
||||||
public static Optional<TemplateQueryBuilder> fromXContent(QueryParseContext parseContext) throws IOException {
|
public static Optional<TemplateQueryBuilder> fromXContent(QueryParseContext parseContext) throws IOException {
|
||||||
XContentParser parser = parseContext.parser();
|
XContentParser parser = parseContext.parser();
|
||||||
Template template = Template.parse(parser, parseContext.getParseFieldMatcher());
|
Script template = Script.parse(parser, parseContext.getParseFieldMatcher(), "mustache");
|
||||||
return Optional.of(new TemplateQueryBuilder(template));
|
return Optional.of(new TemplateQueryBuilder(template));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -46,7 +46,7 @@ import org.elasticsearch.index.fielddata.IndexFieldDataService;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.TemplateQueryBuilder;
|
import org.elasticsearch.script.mustache.TemplateQueryBuilder;
|
||||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
|
|
|
@ -30,13 +30,11 @@ import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.script.mustache.TemplateQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TemplateQueryBuilder;
|
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
|
import org.elasticsearch.rest.action.search.template.RestSearchTemplateAction;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||||
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
@ -95,8 +93,7 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
Map<String, Object> vars = new HashMap<>();
|
Map<String, Object> vars = new HashMap<>();
|
||||||
vars.put("template", "all");
|
vars.put("template", "all");
|
||||||
|
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(new Template("{\"match_{{template}}\": {}}\"", ScriptType.INLINE, null,
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("{\"match_{{template}}\": {}}\"", ScriptType.INLINE,vars);
|
||||||
null, vars));
|
|
||||||
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
assertHitCount(sr, 2);
|
assertHitCount(sr, 2);
|
||||||
|
@ -108,9 +105,8 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
SearchResponse sr = client().prepareSearch()
|
SearchResponse sr = client().prepareSearch()
|
||||||
.setSource(
|
.setSource(
|
||||||
new SearchSourceBuilder().size(0).query(
|
new SearchSourceBuilder().size(0).query(
|
||||||
QueryBuilders.templateQuery(new Template("{ \"match_{{template}}\": {} }",
|
new TemplateQueryBuilder("{ \"match_{{template}}\": {} }", ScriptType.INLINE, params)))
|
||||||
ScriptType.INLINE, null, null, params)))).execute()
|
.get();
|
||||||
.actionGet();
|
|
||||||
assertNoFailures(sr);
|
assertNoFailures(sr);
|
||||||
assertThat(sr.getHits().hits().length, equalTo(0));
|
assertThat(sr.getHits().hits().length, equalTo(0));
|
||||||
}
|
}
|
||||||
|
@ -118,8 +114,7 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
public void testTemplateWOReplacementInBody() throws IOException {
|
public void testTemplateWOReplacementInBody() throws IOException {
|
||||||
Map<String, Object> vars = new HashMap<>();
|
Map<String, Object> vars = new HashMap<>();
|
||||||
|
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(new Template(
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("{\"match_all\": {}}\"", ScriptType.INLINE, vars);
|
||||||
"{\"match_all\": {}}\"", ScriptType.INLINE, null, null, vars));
|
|
||||||
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
assertHitCount(sr, 2);
|
assertHitCount(sr, 2);
|
||||||
|
@ -129,8 +124,7 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
Map<String, Object> vars = new HashMap<>();
|
Map<String, Object> vars = new HashMap<>();
|
||||||
vars.put("template", "all");
|
vars.put("template", "all");
|
||||||
|
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(new Template(
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("storedTemplate", ScriptService.ScriptType.FILE, vars);
|
||||||
"storedTemplate", ScriptService.ScriptType.FILE, null, null, vars));
|
|
||||||
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
assertHitCount(sr, 2);
|
assertHitCount(sr, 2);
|
||||||
|
@ -139,7 +133,7 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
public void testRawFSTemplate() throws IOException {
|
public void testRawFSTemplate() throws IOException {
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("template", "all");
|
params.put("template", "all");
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(new Template("storedTemplate", ScriptType.FILE, null, null, params));
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("storedTemplate", ScriptType.FILE, params);
|
||||||
SearchResponse sr = client().prepareSearch().setQuery(builder).get();
|
SearchResponse sr = client().prepareSearch().setQuery(builder).get();
|
||||||
assertHitCount(sr, 2);
|
assertHitCount(sr, 2);
|
||||||
}
|
}
|
||||||
|
@ -411,8 +405,7 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
Map<String, Object> vars = new HashMap<>();
|
Map<String, Object> vars = new HashMap<>();
|
||||||
vars.put("fieldParam", "bar");
|
vars.put("fieldParam", "bar");
|
||||||
|
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(new Template(
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("3", ScriptService.ScriptType.STORED, vars);
|
||||||
"3", ScriptService.ScriptType.STORED, null, null, vars));
|
|
||||||
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
SearchResponse sr = client().prepareSearch().setQuery(builder)
|
||||||
.execute().actionGet();
|
.execute().actionGet();
|
||||||
assertHitCount(sr, 1);
|
assertHitCount(sr, 1);
|
||||||
|
@ -420,11 +413,11 @@ public class TemplateQueryTests extends ESIntegTestCase {
|
||||||
// "{\"template\": {\"id\": \"3\",\"params\" : {\"fieldParam\" : \"foo\"}}}";
|
// "{\"template\": {\"id\": \"3\",\"params\" : {\"fieldParam\" : \"foo\"}}}";
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("fieldParam", "foo");
|
params.put("fieldParam", "foo");
|
||||||
TemplateQueryBuilder templateQuery = new TemplateQueryBuilder(new Template("3", ScriptType.STORED, null, null, params));
|
TemplateQueryBuilder templateQuery = new TemplateQueryBuilder("3", ScriptType.STORED, params);
|
||||||
sr = client().prepareSearch().setQuery(templateQuery).get();
|
sr = client().prepareSearch().setQuery(templateQuery).get();
|
||||||
assertHitCount(sr, 4);
|
assertHitCount(sr, 4);
|
||||||
|
|
||||||
templateQuery = new TemplateQueryBuilder(new Template("/mustache/3", ScriptType.STORED, null, null, params));
|
templateQuery = new TemplateQueryBuilder("/mustache/3", ScriptType.STORED, params);
|
||||||
sr = client().prepareSearch().setQuery(templateQuery).get();
|
sr = client().prepareSearch().setQuery(templateQuery).get();
|
||||||
assertHitCount(sr, 4);
|
assertHitCount(sr, 4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,21 +17,29 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.script.mustache;
|
||||||
|
|
||||||
import org.apache.lucene.index.memory.MemoryIndex;
|
import org.apache.lucene.index.memory.MemoryIndex;
|
||||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
|
import org.elasticsearch.plugins.Plugin;
|
||||||
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||||
import org.elasticsearch.script.Template;
|
|
||||||
import org.elasticsearch.test.AbstractQueryTestCase;
|
import org.elasticsearch.test.AbstractQueryTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -43,9 +51,14 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
*/
|
*/
|
||||||
private QueryBuilder templateBase;
|
private QueryBuilder templateBase;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Class<? extends Plugin>> getPlugins() {
|
||||||
|
return Collections.singleton(MustachePlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
templateBase = RandomQueryBuilder.createQuery(random());
|
templateBase = new MatchQueryBuilder("field", "some values");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -55,7 +68,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TemplateQueryBuilder doCreateTestQueryBuilder() {
|
protected TemplateQueryBuilder doCreateTestQueryBuilder() {
|
||||||
return new TemplateQueryBuilder(new Template(templateBase.toString(), ScriptType.INLINE, "mockscript", null, null));
|
return new TemplateQueryBuilder(new Script(templateBase.toString(), ScriptType.INLINE, "mockscript", null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +77,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIllegalArgument() {
|
public void testIllegalArgument() {
|
||||||
expectThrows(IllegalArgumentException.class, () -> new TemplateQueryBuilder((Template) null));
|
expectThrows(IllegalArgumentException.class, () -> new TemplateQueryBuilder((Script) null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +92,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
try {
|
try {
|
||||||
parseQuery(queryAsString);
|
parseQuery(queryAsString);
|
||||||
fail("ScriptParseException expected.");
|
fail("ScriptParseException expected.");
|
||||||
} catch (ScriptParseException e) {
|
} catch (ElasticsearchParseException e) {
|
||||||
assertTrue(e.getMessage().contains("bogusField"));
|
assertTrue(e.getMessage().contains("bogusField"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,8 +100,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
public void testJSONGeneration() throws IOException {
|
public void testJSONGeneration() throws IOException {
|
||||||
Map<String, Object> vars = new HashMap<>();
|
Map<String, Object> vars = new HashMap<>();
|
||||||
vars.put("template", "filled");
|
vars.put("template", "filled");
|
||||||
TemplateQueryBuilder builder = new TemplateQueryBuilder(
|
TemplateQueryBuilder builder = new TemplateQueryBuilder("I am a $template string", ScriptType.INLINE, vars);
|
||||||
new Template("I am a $template string", ScriptType.INLINE, null, null, vars));
|
|
||||||
XContentBuilder content = XContentFactory.jsonBuilder();
|
XContentBuilder content = XContentFactory.jsonBuilder();
|
||||||
content.startObject();
|
content.startObject();
|
||||||
builder.doXContent(content, null);
|
builder.doXContent(content, null);
|
||||||
|
@ -103,8 +115,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
String query = "{\"template\": {\"inline\": \"{\\\"match_{{template}}\\\": {}}\\\"\",\"params\" : {\"template\" : \"all\"}}}";
|
String query = "{\"template\": {\"inline\": \"{\\\"match_{{template}}\\\": {}}\\\"\",\"params\" : {\"template\" : \"all\"}}}";
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("template", "all");
|
params.put("template", "all");
|
||||||
QueryBuilder expectedBuilder = new TemplateQueryBuilder(new Template(expectedTemplateString, ScriptType.INLINE, null, null,
|
QueryBuilder expectedBuilder = new TemplateQueryBuilder(expectedTemplateString, ScriptType.INLINE, params);
|
||||||
params));
|
|
||||||
assertParsedQuery(query, expectedBuilder);
|
assertParsedQuery(query, expectedBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,16 +124,15 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
String query = "{\"template\": {\"inline\": {\"match_{{template}}\": {}},\"params\" : {\"template\" : \"all\"}}}";
|
String query = "{\"template\": {\"inline\": {\"match_{{template}}\": {}},\"params\" : {\"template\" : \"all\"}}}";
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("template", "all");
|
params.put("template", "all");
|
||||||
QueryBuilder expectedBuilder = new TemplateQueryBuilder(new Template(expectedTemplateString, ScriptType.INLINE, null,
|
QueryBuilder expectedBuilder = new TemplateQueryBuilder(expectedTemplateString, ScriptType.INLINE, params, XContentType.JSON);
|
||||||
XContentType.JSON, params));
|
|
||||||
assertParsedQuery(query, expectedBuilder);
|
assertParsedQuery(query, expectedBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void testMustRewrite() throws IOException {
|
public void testMustRewrite() throws IOException {
|
||||||
String query = "{ \"match_all\" : {}}";
|
String query = "{ \"match_all\" : {}}";
|
||||||
QueryBuilder builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
|
QueryBuilder builder = new TemplateQueryBuilder(new Script(query, ScriptType.INLINE, "mockscript",
|
||||||
XContentType.JSON, Collections.emptyMap()));
|
Collections.emptyMap(), XContentType.JSON));
|
||||||
try {
|
try {
|
||||||
builder.toQuery(createShardContext());
|
builder.toQuery(createShardContext());
|
||||||
fail();
|
fail();
|
||||||
|
@ -134,24 +144,24 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
|
||||||
|
|
||||||
public void testRewriteWithInnerName() throws IOException {
|
public void testRewriteWithInnerName() throws IOException {
|
||||||
final String query = "{ \"match_all\" : {\"_name\" : \"foobar\"}}";
|
final String query = "{ \"match_all\" : {\"_name\" : \"foobar\"}}";
|
||||||
QueryBuilder builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
|
QueryBuilder builder = new TemplateQueryBuilder(new Script(query, ScriptType.INLINE, "mockscript",
|
||||||
XContentType.JSON, Collections.emptyMap()));
|
Collections.emptyMap(), XContentType.JSON));
|
||||||
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(createShardContext()));
|
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(createShardContext()));
|
||||||
|
|
||||||
builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
|
builder = new TemplateQueryBuilder(new Script(query, ScriptType.INLINE, "mockscript",
|
||||||
XContentType.JSON, Collections.emptyMap())).queryName("outer");
|
Collections.emptyMap(), XContentType.JSON)).queryName("outer");
|
||||||
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"),
|
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"),
|
||||||
builder.rewrite(createShardContext()));
|
builder.rewrite(createShardContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRewriteWithInnerBoost() throws IOException {
|
public void testRewriteWithInnerBoost() throws IOException {
|
||||||
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
|
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
|
||||||
QueryBuilder builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
|
QueryBuilder builder = new TemplateQueryBuilder(new Script(query.toString(), ScriptType.INLINE, "mockscript",
|
||||||
XContentType.JSON, Collections.emptyMap()));
|
Collections.emptyMap(), XContentType.JSON));
|
||||||
assertEquals(query, builder.rewrite(createShardContext()));
|
assertEquals(query, builder.rewrite(createShardContext()));
|
||||||
|
|
||||||
builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
|
builder = new TemplateQueryBuilder(new Script(query.toString(), ScriptType.INLINE, "mockscript",
|
||||||
XContentType.JSON, Collections.emptyMap())).boost(3);
|
Collections.emptyMap(), XContentType.JSON)).boost(3);
|
||||||
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(createShardContext()));
|
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(createShardContext()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ public class PercolateQueryBuilderTests extends AbstractQueryTestCase<PercolateQ
|
||||||
try {
|
try {
|
||||||
parseQuery(testQuery);
|
parseQuery(testQuery);
|
||||||
fail("some parsing exception expected for query: " + testQuery);
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
} catch (ParsingException | Script.ScriptParseException | ElasticsearchParseException e) {
|
} catch (ParsingException | ElasticsearchParseException e) {
|
||||||
// different kinds of exception wordings depending on location
|
// different kinds of exception wordings depending on location
|
||||||
// of mutation, so no simple asserts possible here
|
// of mutation, so no simple asserts possible here
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.reindex;
|
package org.elasticsearch.index.reindex;
|
||||||
|
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
import org.elasticsearch.client.node.NodeClient;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
|
import org.elasticsearch.common.ParseFieldMatcher;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||||
|
@ -29,15 +31,18 @@ import org.elasticsearch.rest.RestChannel;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
||||||
import org.elasticsearch.search.suggest.Suggesters;
|
import org.elasticsearch.search.suggest.Suggesters;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||||
|
import static org.elasticsearch.script.Script.ScriptField;
|
||||||
|
|
||||||
public class RestUpdateByQueryAction extends AbstractBulkByQueryRestHandler<UpdateByQueryRequest, UpdateByQueryAction> {
|
public class RestUpdateByQueryAction extends AbstractBulkByQueryRestHandler<UpdateByQueryRequest, UpdateByQueryAction> {
|
||||||
|
|
||||||
|
@ -67,11 +72,65 @@ public class RestUpdateByQueryAction extends AbstractBulkByQueryRestHandler<Upda
|
||||||
|
|
||||||
Map<String, Consumer<Object>> consumers = new HashMap<>();
|
Map<String, Consumer<Object>> consumers = new HashMap<>();
|
||||||
consumers.put("conflicts", o -> internal.setConflicts((String) o));
|
consumers.put("conflicts", o -> internal.setConflicts((String) o));
|
||||||
consumers.put("script", o -> internal.setScript(Script.parse((Map<String, Object>)o, false, parseFieldMatcher)));
|
consumers.put("script", o -> internal.setScript(parseScript((Map<String, Object>)o, parseFieldMatcher)));
|
||||||
|
|
||||||
parseInternalRequest(internal, request, consumers);
|
parseInternalRequest(internal, request, consumers);
|
||||||
|
|
||||||
internal.setPipeline(request.param("pipeline"));
|
internal.setPipeline(request.param("pipeline"));
|
||||||
return internal;
|
return internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static Script parseScript(Map<String, Object> config, ParseFieldMatcher parseFieldMatcher) {
|
||||||
|
String script = null;
|
||||||
|
ScriptService.ScriptType type = null;
|
||||||
|
String lang = null;
|
||||||
|
Map<String, Object> params = null;
|
||||||
|
for (Iterator<Map.Entry<String, Object>> itr = config.entrySet().iterator(); itr.hasNext();) {
|
||||||
|
Map.Entry<String, Object> entry = itr.next();
|
||||||
|
String parameterName = entry.getKey();
|
||||||
|
Object parameterValue = entry.getValue();
|
||||||
|
if (parseFieldMatcher.match(parameterName, ScriptField.LANG)) {
|
||||||
|
if (parameterValue instanceof String || parameterValue == null) {
|
||||||
|
lang = (String) parameterValue;
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("Value must be of type String: [" + parameterName + "]");
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(parameterName, ScriptField.PARAMS)) {
|
||||||
|
if (parameterValue instanceof Map || parameterValue == null) {
|
||||||
|
params = (Map<String, Object>) parameterValue;
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("Value must be of type String: [" + parameterName + "]");
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(parameterName, ScriptService.ScriptType.INLINE.getParseField())) {
|
||||||
|
if (parameterValue instanceof String || parameterValue == null) {
|
||||||
|
script = (String) parameterValue;
|
||||||
|
type = ScriptService.ScriptType.INLINE;
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("Value must be of type String: [" + parameterName + "]");
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(parameterName, ScriptService.ScriptType.FILE.getParseField())) {
|
||||||
|
if (parameterValue instanceof String || parameterValue == null) {
|
||||||
|
script = (String) parameterValue;
|
||||||
|
type = ScriptService.ScriptType.FILE;
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("Value must be of type String: [" + parameterName + "]");
|
||||||
|
}
|
||||||
|
} else if (parseFieldMatcher.match(parameterName, ScriptService.ScriptType.STORED.getParseField())) {
|
||||||
|
if (parameterValue instanceof String || parameterValue == null) {
|
||||||
|
script = (String) parameterValue;
|
||||||
|
type = ScriptService.ScriptType.STORED;
|
||||||
|
} else {
|
||||||
|
throw new ElasticsearchParseException("Value must be of type String: [" + parameterName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (script == null) {
|
||||||
|
throw new ElasticsearchParseException("expected one of [{}], [{}] or [{}] fields, but found none",
|
||||||
|
ScriptService.ScriptType.INLINE.getParseField().getPreferredName(), ScriptService.ScriptType.FILE.getParseField()
|
||||||
|
.getPreferredName(), ScriptService.ScriptType.STORED.getParseField().getPreferredName());
|
||||||
|
}
|
||||||
|
assert type != null : "if script is not null, type should definitely not be null";
|
||||||
|
return new Script(script, type, lang, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,6 @@ import org.elasticsearch.plugins.MapperPlugin;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.plugins.PluginsService;
|
import org.elasticsearch.plugins.PluginsService;
|
||||||
import org.elasticsearch.plugins.SearchPlugin;
|
import org.elasticsearch.plugins.SearchPlugin;
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
|
||||||
import org.elasticsearch.script.ScriptModule;
|
import org.elasticsearch.script.ScriptModule;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.SearchModule;
|
import org.elasticsearch.search.SearchModule;
|
||||||
|
@ -304,7 +303,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
|
||||||
try {
|
try {
|
||||||
parseQuery(testQuery);
|
parseQuery(testQuery);
|
||||||
fail("some parsing exception expected for query: " + testQuery);
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
} catch (ParsingException | ScriptParseException | ElasticsearchParseException e) {
|
} catch (ParsingException | ElasticsearchParseException e) {
|
||||||
// different kinds of exception wordings depending on location
|
// different kinds of exception wordings depending on location
|
||||||
// of mutation, so no simple asserts possible here
|
// of mutation, so no simple asserts possible here
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|
Loading…
Reference in New Issue