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:
Martijn van Groningen 2016-07-13 22:28:18 +02:00
parent 798ee177ed
commit e0ebf5da1c
29 changed files with 336 additions and 604 deletions

View File

@ -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,

View File

@ -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.
*/ */

View File

@ -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);
}
}

View File

@ -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);
}
}
} }

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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");
} }

View File

@ -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",

View File

@ -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));

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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 {

View File

@ -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";
} }
} }

View File

@ -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

View File

@ -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";
} }
} }

View File

@ -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,

View File

@ -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));
} }
} }

View File

@ -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;

View File

@ -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);
} }

View File

@ -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()));
} }

View File

@ -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) {

View File

@ -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);
}
} }

View File

@ -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) {