/*
* 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.hadoop.serialization;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.elasticsearch.hadoop.EsHadoopIllegalArgumentException;
import org.elasticsearch.hadoop.cfg.ConfigurationOptions;
import org.elasticsearch.hadoop.cfg.Settings;
import org.elasticsearch.hadoop.rest.InitializationUtils;
import org.elasticsearch.hadoop.serialization.builder.JdkValueWriter;
import org.elasticsearch.hadoop.serialization.bulk.BulkCommand;
import org.elasticsearch.hadoop.serialization.bulk.BulkCommands;
import org.elasticsearch.hadoop.util.BytesArray;
import org.elasticsearch.hadoop.util.EsMajorVersion;
import org.elasticsearch.hadoop.util.StringUtils;
import org.elasticsearch.hadoop.util.TestSettings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public class CommandTest {
private final BytesArray ba = new BytesArray(1024);
private Object data;
private final String operation;
private boolean noId = false;
private boolean jsonInput = false;
private final EsMajorVersion version;
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ ConfigurationOptions.ES_OPERATION_INDEX, false, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, false, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, false, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_INDEX, true, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, true, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, true, EsMajorVersion.V_1_X },
{ ConfigurationOptions.ES_OPERATION_INDEX, false, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, false, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, false, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_INDEX, true, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, true, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, true, EsMajorVersion.V_2_X },
{ ConfigurationOptions.ES_OPERATION_INDEX, false, EsMajorVersion.V_5_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, false, EsMajorVersion.V_5_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, false, EsMajorVersion.V_5_X },
{ ConfigurationOptions.ES_OPERATION_INDEX, true, EsMajorVersion.V_5_X },
{ ConfigurationOptions.ES_OPERATION_CREATE, true, EsMajorVersion.V_5_X },
{ ConfigurationOptions.ES_OPERATION_UPDATE, true, EsMajorVersion.V_5_X },
});
}
public CommandTest(String operation, boolean jsonInput, EsMajorVersion version) {
this.operation = operation;
this.jsonInput = jsonInput;
this.version = version;
}
@Before
public void prepare() {
ba.reset();
if (!jsonInput) {
Map map = new LinkedHashMap();
map.put("n", 1);
map.put("s", "v");
data = map;
}
else {
data = "{\"n\":1,\"s\":\"v\"}";
}
}
@Test
public void testNoHeader() throws Exception {
assumeFalse(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
create(settings()).write(data).copyTo(ba);
String result = prefix() + "}}" + map();
assertEquals(result, ba.toString());
}
@Test
// check user friendliness and escape the string if needed
public void testConstantId() throws Exception {
Settings settings = settings();
noId = true;
settings.setProperty(ConfigurationOptions.ES_MAPPING_ID, "<foobar>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_id\":\"foobar\"}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testParent() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_PARENT, "<5>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_parent\":5}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testVersion() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_VERSION, "<3>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_version\":3,\"_version_type\":\"external\"}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testTtl() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_TTL, "<2>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_ttl\":2}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testTimestamp() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_TIMESTAMP, "<3>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_timestamp\":3}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testRouting() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_ROUTING, "<4>");
create(settings).write(data).copyTo(ba);
String result = prefix() + "\"_routing\":4}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testAll() throws Exception {
Settings settings = settings();
settings.setProperty(ConfigurationOptions.ES_MAPPING_ID, "n");
settings.setProperty(ConfigurationOptions.ES_MAPPING_TTL, "<2>");
settings.setProperty(ConfigurationOptions.ES_MAPPING_ROUTING, "s");
create(settings).write(data).copyTo(ba);
String result = "{\"" + operation + "\":{\"_id\":1,\"_routing\":\"v\",\"_ttl\":2}}" + map();
assertEquals(result, ba.toString());
}
@Test
public void testIdPattern() throws Exception {
Settings settings = settings();
settings.setResourceWrite("foo/{n}");
create(settings).write(data).copyTo(ba);
String result = "{\"" + operation + "\":{\"_index\":\"foo\",\"_type\":\"1\"" + (isUpdateOp() ? ",\"_id\":2" : "") + "}}" + map();
assertEquals(result, ba.toString());
}
@Test(expected = EsHadoopIllegalArgumentException.class)
public void testIdMandatory() throws Exception {
assumeTrue(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
Settings set = settings();
set.setProperty(ConfigurationOptions.ES_MAPPING_ID, "");
create(set).write(data).copyTo(ba);
}
@Test
public void testUpdateOnlyScript5X() throws Exception {
assumeTrue(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
assumeTrue(version.after(EsMajorVersion.V_1_X));
Settings set = settings();
set.setProperty(ConfigurationOptions.ES_INDEX_AUTO_CREATE, "yes");
set.setProperty(ConfigurationOptions.ES_UPDATE_RETRY_ON_CONFLICT, "3");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT, "counter = 3");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_LANG, "groovy");
create(set).write(data).copyTo(ba);
String result =
"{\"" + operation + "\":{\"_id\":2,\"_retry_on_conflict\":3}}\n" +
"{\"script\":{\"inline\":\"counter = 3\",\"lang\":\"groovy\"}}\n";
assertEquals(result, ba.toString());
}
@Test
public void testUpdateOnlyScript1X() throws Exception {
assumeTrue(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
assumeTrue(version.onOrBefore(EsMajorVersion.V_1_X));
Settings set = settings();
set.setProperty(ConfigurationOptions.ES_INDEX_AUTO_CREATE, "yes");
set.setProperty(ConfigurationOptions.ES_UPDATE_RETRY_ON_CONFLICT, "3");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT, "counter = 3");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_LANG, "groovy");
create(set).write(data).copyTo(ba);
String result =
"{\"" + operation + "\":{\"_id\":2,\"_retry_on_conflict\":3}}\n" +
"{\"lang\":\"groovy\",\"script\":\"counter = 3\"}\n";
assertEquals(result, ba.toString());
}
@Test
public void testUpdateOnlyParamScript5X() throws Exception {
assumeTrue(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
assumeTrue(version.after(EsMajorVersion.V_1_X));
Settings set = settings();
set.setProperty(ConfigurationOptions.ES_MAPPING_ID, "n");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT, "counter = param1; anothercounter = param2");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_LANG, "groovy");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_PARAMS, " param1:<1>, param2:n ");
create(set).write(data).copyTo(ba);
String result =
"{\"" + operation + "\":{\"_id\":1}}\n" +
"{\"script\":{\"inline\":\"counter = param1; anothercounter = param2\",\"lang\":\"groovy\",\"params\":{\"param1\":1,\"param2\":1}}}\n";
assertEquals(result, ba.toString());
}
@Test
public void testUpdateOnlyParamScript1X() throws Exception {
assumeTrue(ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation));
assumeTrue(version.onOrBefore(EsMajorVersion.V_1_X));
Settings set = settings();
set.setProperty(ConfigurationOptions.ES_MAPPING_ID, "n");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT, "counter = param1; anothercounter = param2");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_LANG, "groovy");
set.setProperty(ConfigurationOptions.ES_UPDATE_SCRIPT_PARAMS, " param1:<1>, param2:n ");
create(set).write(data).copyTo(ba);
String result =
"{\"" + operation + "\":{\"_id\":1}}\n" +
"{\"params\":{\"param1\":1,\"param2\":1},\"lang\":\"groovy\",\"script\":\"counter = param1; anothercounter = param2\"}\n";
assertEquals(result, ba.toString());
}
private BulkCommand create(Settings settings) {
if (!StringUtils.hasText(settings.getResourceWrite())) {
settings.setProperty(ConfigurationOptions.ES_WRITE_OPERATION, operation);
}
return BulkCommands.create(settings, null, version);
}
private Settings settings() {
Settings set = new TestSettings();
set.setProperty(ConfigurationOptions.ES_INPUT_JSON, Boolean.toString(jsonInput));
InitializationUtils.setValueWriterIfNotSet(set, JdkValueWriter.class, null);
InitializationUtils.setFieldExtractorIfNotSet(set, MapFieldExtractor.class, null);
InitializationUtils.setBytesConverterIfNeeded(set, JdkBytesConverter.class, null);
set.setProperty(ConfigurationOptions.ES_WRITE_OPERATION, operation);
set.setResourceWrite("foo/bar");
if (isUpdateOp()) {
set.setProperty(ConfigurationOptions.ES_MAPPING_ID, "<2>");
}
return set;
}
private String prefix() {
StringBuilder sb = new StringBuilder("{\"" + operation + "\":{");
if (isUpdateOp() && !noId) {
sb.append("\"_id\":2,");
}
return sb.toString();
}
private String map() {
StringBuilder sb = new StringBuilder("\n{");
if (isUpdateOp()) {
sb.append("\"doc\":{");
}
sb.append("\"n\":1,\"s\":\"v\"}");
if (isUpdateOp()) {
sb.append("}");
}
sb.append("\n");
return sb.toString();
}
private boolean isUpdateOp() {
return ConfigurationOptions.ES_OPERATION_UPDATE.equals(operation);
}
}