/*
* 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.expression;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.GeneralScriptException;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.metrics.stats.Stats;
import org.elasticsearch.search.aggregations.pipeline.SimpleValue;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.hamcrest.ElasticsearchAssertions;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram;
import static org.elasticsearch.search.aggregations.AggregationBuilders.sum;
import static org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilders.bucketScript;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.notNullValue;
// TODO: please convert to unit tests!
public class MoreExpressionTests extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(ExpressionPlugin.class);
}
private SearchRequestBuilder buildRequest(String script, Object... params) {
ensureGreen("test");
Map<String, Object> paramsMap = new HashMap<>();
assert (params.length % 2 == 0);
for (int i = 0; i < params.length; i += 2) {
paramsMap.put(params[i].toString(), params[i + 1]);
}
SearchRequestBuilder req = client().prepareSearch().setIndices("test");
req.setQuery(QueryBuilders.matchAllQuery())
.addSort(SortBuilders.fieldSort("_uid")
.order(SortOrder.ASC))
.addScriptField("foo", new Script(ScriptType.INLINE, "expression", script, paramsMap));
return req;
}
public void testBasic() throws Exception {
createIndex("test");
ensureGreen("test");
client().prepareIndex("test", "doc", "1").setSource("foo", 4).setRefreshPolicy(IMMEDIATE).get();
SearchResponse rsp = buildRequest("doc['foo'] + 1").get();
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D);
}
public void testFunction() throws Exception {
createIndex("test");
ensureGreen("test");
client().prepareIndex("test", "doc", "1").setSource("foo", 4).setRefreshPolicy(IMMEDIATE).get();
SearchResponse rsp = buildRequest("doc['foo'] + abs(1)").get();
assertSearchResponse(rsp);
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D);
}
public void testBasicUsingDotValue() throws Exception {
createIndex("test");
ensureGreen("test");
client().prepareIndex("test", "doc", "1").setSource("foo", 4).setRefreshPolicy(IMMEDIATE).get();
SearchResponse rsp = buildRequest("doc['foo'].value + 1").get();
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D);
}
public void testScore() throws Exception {
createIndex("test");
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("text", "hello goodbye"),
client().prepareIndex("test", "doc", "2").setSource("text", "hello hello hello goodbye"),
client().prepareIndex("test", "doc", "3").setSource("text", "hello hello goodebye"));
ScoreFunctionBuilder<?> score = ScoreFunctionBuilders.scriptFunction(new Script(ScriptType.INLINE, "expression", "1 / _score", Collections.emptyMap()));
SearchRequestBuilder req = client().prepareSearch().setIndices("test");
req.setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("text", "hello"), score).boostMode(CombineFunction.REPLACE));
req.setSearchType(SearchType.DFS_QUERY_THEN_FETCH); // make sure DF is consistent
SearchResponse rsp = req.get();
assertSearchResponse(rsp);
SearchHits hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals("1", hits.getAt(0).getId());
assertEquals("3", hits.getAt(1).getId());
assertEquals("2", hits.getAt(2).getId());
}
public void testDateMethods() throws Exception {
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "date0", "type=date", "date1", "type=date"));
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("date0", "2015-04-28T04:02:07Z", "date1", "1985-09-01T23:11:01Z"),
client().prepareIndex("test", "doc", "2").setSource("date0", "2013-12-25T11:56:45Z", "date1", "1983-10-13T23:15:00Z"));
SearchResponse rsp = buildRequest("doc['date0'].getSeconds() - doc['date0'].getMinutes()").get();
assertEquals(2, rsp.getHits().getTotalHits());
SearchHits hits = rsp.getHits();
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(-11.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date0'].getHourOfDay() + doc['date1'].getDayOfMonth()").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(24.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date1'].getMonth() + 1").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(9.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(10.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date1'].getYear()").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(1985.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(1983.0, hits.getAt(1).field("foo").getValue(), 0.0D);
}
public void testDateObjectMethods() throws Exception {
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "date0", "type=date", "date1", "type=date"));
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("date0", "2015-04-28T04:02:07Z", "date1", "1985-09-01T23:11:01Z"),
client().prepareIndex("test", "doc", "2").setSource("date0", "2013-12-25T11:56:45Z", "date1", "1983-10-13T23:15:00Z"));
SearchResponse rsp = buildRequest("doc['date0'].date.secondOfMinute - doc['date0'].date.minuteOfHour").get();
assertEquals(2, rsp.getHits().getTotalHits());
SearchHits hits = rsp.getHits();
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(-11.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date0'].date.getHourOfDay() + doc['date1'].date.dayOfMonth").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(24.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date1'].date.monthOfYear + 1").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(10.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(11.0, hits.getAt(1).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['date1'].date.year").get();
assertEquals(2, rsp.getHits().getTotalHits());
hits = rsp.getHits();
assertEquals(1985.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(1983.0, hits.getAt(1).field("foo").getValue(), 0.0D);
}
public void testMultiValueMethods() throws Exception {
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "double0", "type=double", "double1", "type=double", "double2", "type=double"));
ensureGreen("test");
Map<String, Object> doc1 = new HashMap<>();
doc1.put("double0", new Double[]{5.0d, 1.0d, 1.5d});
doc1.put("double1", new Double[]{1.2d, 2.4d});
doc1.put("double2", 3.0d);
Map<String, Object> doc2 = new HashMap<>();
doc2.put("double0", 5.0d);
doc2.put("double1", 3.0d);
Map<String, Object> doc3 = new HashMap<>();
doc3.put("double0", new Double[]{5.0d, 1.0d, 1.5d, -1.5d});
doc3.put("double1", 4.0d);
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource(doc1),
client().prepareIndex("test", "doc", "2").setSource(doc2),
client().prepareIndex("test", "doc", "3").setSource(doc3));
SearchResponse rsp = buildRequest("doc['double0'].count() + doc['double1'].count()").get();
assertSearchResponse(rsp);
SearchHits hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(2.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].sum()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(7.5, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(6.0, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].avg() + doc['double1'].avg()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(4.3, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(8.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(5.5, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].median()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(1.5, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(1.25, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].min()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(1.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(-1.5, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].max()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(2).field("foo").getValue(), 0.0D);
rsp = buildRequest("doc['double0'].sum()/doc['double0'].count()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(2.5, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(1.5, hits.getAt(2).field("foo").getValue(), 0.0D);
// make sure count() works for missing
rsp = buildRequest("doc['double2'].count()").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(1.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(0.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(0.0, hits.getAt(2).field("foo").getValue(), 0.0D);
// make sure .empty works in the same way
rsp = buildRequest("doc['double2'].empty ? 5.0 : 2.0").get();
assertSearchResponse(rsp);
hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(2.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(5.0, hits.getAt(2).field("foo").getValue(), 0.0D);
}
public void testInvalidDateMethodCall() throws Exception {
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "double", "type=double"));
ensureGreen("test");
indexRandom(true, client().prepareIndex("test", "doc", "1").setSource("double", "178000000.0"));
try {
buildRequest("doc['double'].getYear()").get();
fail();
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained IllegalArgumentException",
e.toString().contains("IllegalArgumentException"), equalTo(true));
assertThat(e.toString() + "should have contained does not exist for numeric field",
e.toString().contains("does not exist for numeric field"), equalTo(true));
}
}
public void testSparseField() throws Exception {
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "x", "type=long", "y", "type=long"));
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("x", 4),
client().prepareIndex("test", "doc", "2").setSource("y", 2));
SearchResponse rsp = buildRequest("doc['x'] + 1").get();
ElasticsearchAssertions.assertSearchResponse(rsp);
SearchHits hits = rsp.getHits();
assertEquals(2, rsp.getHits().getTotalHits());
assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(1.0, hits.getAt(1).field("foo").getValue(), 0.0D);
}
public void testMissingField() throws Exception {
createIndex("test");
ensureGreen("test");
client().prepareIndex("test", "doc", "1").setSource("x", 4).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("doc['bogus']").get();
fail("Expected missing field to cause failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained missing field error",
e.toString().contains("does not exist in mappings"), equalTo(true));
}
}
public void testParams() throws Exception {
createIndex("test");
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("x", 10),
client().prepareIndex("test", "doc", "2").setSource("x", 3),
client().prepareIndex("test", "doc", "3").setSource("x", 5));
// a = int, b = double, c = long
String script = "doc['x'] * a + b + ((c + doc['x']) > 5000000009 ? 1 : 0)";
SearchResponse rsp = buildRequest(script, "a", 2, "b", 3.5, "c", 5000000000L).get();
SearchHits hits = rsp.getHits();
assertEquals(3, hits.getTotalHits());
assertEquals(24.5, hits.getAt(0).field("foo").getValue(), 0.0D);
assertEquals(9.5, hits.getAt(1).field("foo").getValue(), 0.0D);
assertEquals(13.5, hits.getAt(2).field("foo").getValue(), 0.0D);
}
public void testCompileFailure() {
client().prepareIndex("test", "doc", "1").setSource("x", 1).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("garbage%@#%@").get();
fail("Expected expression compilation failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained compilation failure",
e.toString().contains("compile error"), equalTo(true));
}
}
public void testNonNumericParam() {
client().prepareIndex("test", "doc", "1").setSource("x", 1).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("a", "a", "astring").get();
fail("Expected string parameter to cause failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained non-numeric parameter error",
e.toString().contains("must be a numeric type"), equalTo(true));
}
}
public void testNonNumericField() {
client().prepareIndex("test", "doc", "1").setSource("text", "this is not a number").setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("doc['text.keyword']").get();
fail("Expected text field to cause execution failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained non-numeric field error",
e.toString().contains("must be numeric"), equalTo(true));
}
}
public void testInvalidGlobalVariable() {
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("bogus").get();
fail("Expected bogus variable to cause execution failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained unknown variable error",
e.toString().contains("Unknown variable"), equalTo(true));
}
}
public void testDocWithoutField() {
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("doc").get();
fail("Expected doc variable without field to cause execution failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained a missing specific field error",
e.toString().contains("must be used with a specific field"), equalTo(true));
}
}
public void testInvalidFieldMember() {
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefreshPolicy(IMMEDIATE).get();
try {
buildRequest("doc['foo'].bogus").get();
fail("Expected bogus field member to cause execution failure");
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString() + "should have contained ScriptException",
e.toString().contains("ScriptException"), equalTo(true));
assertThat(e.toString() + "should have contained member variable [bogus] does not exist",
e.toString().contains("Member variable [bogus] does not exist"), equalTo(true));
}
}
public void testSpecialValueVariable() throws Exception {
// i.e. _value for aggregations
createIndex("test");
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("x", 5, "y", 1.2),
client().prepareIndex("test", "doc", "2").setSource("x", 10, "y", 1.4),
client().prepareIndex("test", "doc", "3").setSource("x", 13, "y", 1.8));
SearchRequestBuilder req = client().prepareSearch().setIndices("test");
req.setQuery(QueryBuilders.matchAllQuery())
.addAggregation(
AggregationBuilders.stats("int_agg").field("x")
.script(new Script(ScriptType.INLINE,
ExpressionScriptEngine.NAME, "_value * 3", Collections.emptyMap())))
.addAggregation(
AggregationBuilders.stats("double_agg").field("y")
.script(new Script(ScriptType.INLINE,
ExpressionScriptEngine.NAME, "_value - 1.1", Collections.emptyMap())))
.addAggregation(
AggregationBuilders.stats("const_agg").field("x") // specifically to test a script w/o _value
.script(new Script(ScriptType.INLINE,
ExpressionScriptEngine.NAME, "3.0", Collections.emptyMap()))
);
SearchResponse rsp = req.get();
assertEquals(3, rsp.getHits().getTotalHits());
Stats stats = rsp.getAggregations().get("int_agg");
assertEquals(39.0, stats.getMax(), 0.0001);
assertEquals(15.0, stats.getMin(), 0.0001);
stats = rsp.getAggregations().get("double_agg");
assertEquals(0.7, stats.getMax(), 0.0001);
assertEquals(0.1, stats.getMin(), 0.0001);
stats = rsp.getAggregations().get("const_agg");
assertThat(stats.getMax(), equalTo(3.0));
assertThat(stats.getMin(), equalTo(3.0));
assertThat(stats.getAvg(), equalTo(3.0));
}
public void testStringSpecialValueVariable() throws Exception {
// i.e. expression script for term aggregations, which is not allowed
assertAcked(client().admin().indices().prepareCreate("test")
.addMapping("doc", "text", "type=keyword").get());
ensureGreen("test");
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("text", "hello"),
client().prepareIndex("test", "doc", "2").setSource("text", "goodbye"),
client().prepareIndex("test", "doc", "3").setSource("text", "hello"));
SearchRequestBuilder req = client().prepareSearch().setIndices("test");
req.setQuery(QueryBuilders.matchAllQuery())
.addAggregation(
AggregationBuilders.terms("term_agg").field("text")
.script(
new Script(ScriptType.INLINE, ExpressionScriptEngine.NAME, "_value", Collections.emptyMap())));
String message;
try {
// shards that don't have docs with the "text" field will not fail,
// so we may or may not get a total failure
SearchResponse rsp = req.get();
assertThat(rsp.getShardFailures().length, greaterThan(0)); // at least the shards containing the docs should have failed
message = rsp.getShardFailures()[0].reason();
} catch (SearchPhaseExecutionException e) {
message = e.toString();
}
assertThat(message + "should have contained ScriptException",
message.contains("ScriptException"), equalTo(true));
assertThat(message + "should have contained text variable error",
message.contains("text variable"), equalTo(true));
}
// series of unit test for using expressions as executable scripts
public void testExecutableScripts() throws Exception {
assumeTrue("test creates classes directly, cannot run with security manager", System.getSecurityManager() == null);
Map<String, Object> vars = new HashMap<>();
vars.put("a", 2.5);
vars.put("b", 3);
vars.put("xyz", -1);
Expression expr = JavascriptCompiler.compile("a+b+xyz");
CompiledScript compiledScript = new CompiledScript(ScriptType.INLINE, "", "expression", expr);
ExpressionExecutableScript ees = new ExpressionExecutableScript(compiledScript, vars);
assertEquals((Double) ees.run(), 4.5, 0.001);
ees.setNextVar("b", -2.5);
assertEquals((Double) ees.run(), -1, 0.001);
ees.setNextVar("a", -2.5);
ees.setNextVar("b", -2.5);
ees.setNextVar("xyz", -2.5);
assertEquals((Double) ees.run(), -7.5, 0.001);
String message;
try {
vars = new HashMap<>();
vars.put("a", 1);
ees = new ExpressionExecutableScript(compiledScript, vars);
ees.run();
fail("An incorrect number of variables were allowed to be used in an expression.");
} catch (GeneralScriptException se) {
message = se.getMessage();
assertThat(message + " should have contained number of variables", message.contains("number of variables"), equalTo(true));
}
try {
vars = new HashMap<>();
vars.put("a", 1);
vars.put("b", 3);
vars.put("c", -1);
ees = new ExpressionExecutableScript(compiledScript, vars);
ees.run();
fail("A variable was allowed to be set that does not exist in the expression.");
} catch (GeneralScriptException se) {
message = se.getMessage();
assertThat(message + " should have contained does not exist in", message.contains("does not exist in"), equalTo(true));
}
try {
vars = new HashMap<>();
vars.put("a", 1);
vars.put("b", 3);
vars.put("xyz", "hello");
ees = new ExpressionExecutableScript(compiledScript, vars);
ees.run();
fail("A non-number was allowed to be use in the expression.");
} catch (GeneralScriptException se) {
message = se.getMessage();
assertThat(message + " should have contained process numbers", message.contains("process numbers"), equalTo(true));
}
}
// test to make sure expressions are not allowed to be used as update scripts
public void testInvalidUpdateScript() throws Exception {
try {
createIndex("test_index");
ensureGreen("test_index");
indexRandom(true, client().prepareIndex("test_index", "doc", "1").setSource("text_field", "text"));
UpdateRequestBuilder urb = client().prepareUpdate().setIndex("test_index");
urb.setType("doc");
urb.setId("1");
urb.setScript(new Script(ScriptType.INLINE, ExpressionScriptEngine.NAME, "0", Collections.emptyMap()));
urb.get();
fail("Expression scripts should not be allowed to run as update scripts.");
} catch (Exception e) {
String message = e.getMessage();
assertThat(message + " should have contained failed to execute", message.contains("failed to execute"), equalTo(true));
message = e.getCause().getMessage();
assertThat(message + " should have contained not supported", message.contains("not supported"), equalTo(true));
}
}
// test to make sure expressions are allowed to be used for reduce in pipeline aggregations
public void testPipelineAggregationScript() throws Exception {
createIndex("agg_index");
ensureGreen("agg_index");
indexRandom(true,
client().prepareIndex("agg_index", "doc", "1").setSource("one", 1.0, "two", 2.0, "three", 3.0, "four", 4.0),
client().prepareIndex("agg_index", "doc", "2").setSource("one", 2.0, "two", 2.0, "three", 3.0, "four", 4.0),
client().prepareIndex("agg_index", "doc", "3").setSource("one", 3.0, "two", 2.0, "three", 3.0, "four", 4.0),
client().prepareIndex("agg_index", "doc", "4").setSource("one", 4.0, "two", 2.0, "three", 3.0, "four", 4.0),
client().prepareIndex("agg_index", "doc", "5").setSource("one", 5.0, "two", 2.0, "three", 3.0, "four", 4.0));
SearchResponse response = client()
.prepareSearch("agg_index")
.addAggregation(
histogram("histogram")
.field("one")
.interval(2)
.subAggregation(sum("twoSum").field("two"))
.subAggregation(sum("threeSum").field("three"))
.subAggregation(sum("fourSum").field("four"))
.subAggregation(bucketScript("totalSum",
new Script(ScriptType.INLINE,
ExpressionScriptEngine.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()),
"twoSum", "threeSum", "fourSum")))
.execute().actionGet();
Histogram histogram = response.getAggregations().get("histogram");
assertThat(histogram, notNullValue());
assertThat(histogram.getName(), equalTo("histogram"));
List<? extends Histogram.Bucket> buckets = histogram.getBuckets();
for (int bucketCount = 0; bucketCount < buckets.size(); ++bucketCount) {
Histogram.Bucket bucket = buckets.get(bucketCount);
if (bucket.getDocCount() == 1) {
SimpleValue seriesArithmetic = bucket.getAggregations().get("totalSum");
assertThat(seriesArithmetic, notNullValue());
double seriesArithmeticValue = seriesArithmetic.value();
assertEquals(9.0, seriesArithmeticValue, 0.001);
} else if (bucket.getDocCount() == 2) {
SimpleValue seriesArithmetic = bucket.getAggregations().get("totalSum");
assertThat(seriesArithmetic, notNullValue());
double seriesArithmeticValue = seriesArithmetic.value();
assertEquals(18.0, seriesArithmeticValue, 0.001);
} else {
fail("Incorrect number of documents in a bucket in the histogram.");
}
}
}
public void testGeo() throws Exception {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location").field("type", "geo_point");
xContentBuilder.endObject().endObject().endObject().endObject();
assertAcked(prepareCreate("test").addMapping("type1", xContentBuilder));
ensureGreen();
client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
.field("name", "test")
.startObject("location").field("lat", 61.5240).field("lon", 105.3188).endObject()
.endObject()).execute().actionGet();
refresh();
// access .lat
SearchResponse rsp = buildRequest("doc['location'].lat").get();
assertSearchResponse(rsp);
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(61.5240, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
// access .lon
rsp = buildRequest("doc['location'].lon").get();
assertSearchResponse(rsp);
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(105.3188, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
// access .empty
rsp = buildRequest("doc['location'].empty ? 1 : 0").get();
assertSearchResponse(rsp);
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(0, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
// call haversin
rsp = buildRequest("haversin(38.9072, 77.0369, doc['location'].lat, doc['location'].lon)").get();
assertSearchResponse(rsp);
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(3170D, rsp.getHits().getAt(0).field("foo").getValue(), 50D);
}
public void testBoolean() throws Exception {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("doc")
.startObject("properties").startObject("vip").field("type", "boolean");
xContentBuilder.endObject().endObject().endObject().endObject();
assertAcked(prepareCreate("test").addMapping("doc", xContentBuilder));
ensureGreen();
indexRandom(true,
client().prepareIndex("test", "doc", "1").setSource("price", 1.0, "vip", true),
client().prepareIndex("test", "doc", "2").setSource("price", 2.0, "vip", false),
client().prepareIndex("test", "doc", "3").setSource("price", 2.0, "vip", false));
// access .value
SearchResponse rsp = buildRequest("doc['vip'].value").get();
assertSearchResponse(rsp);
assertEquals(3, rsp.getHits().getTotalHits());
assertEquals(1.0D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
assertEquals(0.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D);
assertEquals(0.0D, rsp.getHits().getAt(2).field("foo").getValue(), 1.0D);
// access .empty
rsp = buildRequest("doc['vip'].empty ? 1 : 0").get();
assertSearchResponse(rsp);
assertEquals(3, rsp.getHits().getTotalHits());
assertEquals(0.0D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
assertEquals(0.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D);
assertEquals(1.0D, rsp.getHits().getAt(2).field("foo").getValue(), 1.0D);
// ternary operator
// vip's have a 50% discount
rsp = buildRequest("doc['vip'] ? doc['price']/2 : doc['price']").get();
assertSearchResponse(rsp);
assertEquals(3, rsp.getHits().getTotalHits());
assertEquals(0.5D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D);
assertEquals(2.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D);
assertEquals(2.0D, rsp.getHits().getAt(2).field("foo").getValue(), 1.0D);
}
}