package io.crate.metadata.doc; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import io.crate.Constants; import io.crate.Version; import io.crate.action.sql.SessionContext; import io.crate.analyze.*; import io.crate.metadata.*; import io.crate.metadata.table.ColumnPolicy; import io.crate.operation.udf.UserDefinedFunctionService; import io.crate.sql.parser.SqlParser; import io.crate.sql.tree.CreateTable; import io.crate.sql.tree.Statement; import io.crate.test.integration.CrateDummyClusterServiceUnitTest; import io.crate.types.ArrayType; import io.crate.types.DataType; import io.crate.types.DataTypes; import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateAction; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.inject.Provider; import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.env.Environment; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import javax.annotation.Nullable; import java.io.IOException; import java.util.*; import static io.crate.testing.SymbolMatchers.*; import static io.crate.testing.TestingHelpers.getFunctions; import static org.hamcrest.Matchers.*; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.mockito.Mockito.mock; // @formatter:off public class DocIndexMetaDataTest extends CrateDummyClusterServiceUnitTest { private Functions functions; private UserDefinedFunctionService udfService; private IndexMetaData getIndexMetaData(String indexName, XContentBuilder builder) throws IOException { return getIndexMetaData(indexName, builder, Settings.EMPTY, null); } private IndexMetaData getIndexMetaData(String indexName, XContentBuilder builder, Settings settings, @Nullable AliasMetaData aliasMetaData) throws IOException { Map<String, Object> mappingSource = XContentHelper.convertToMap(builder.bytes(), true).v2(); mappingSource = sortProperties(mappingSource); Settings.Builder settingsBuilder = Settings.builder() .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .put("index.version.created", org.elasticsearch.Version.CURRENT) .put(settings); IndexMetaData.Builder mdBuilder = IndexMetaData.builder(indexName) .settings(settingsBuilder) .putMapping(new MappingMetaData(Constants.DEFAULT_MAPPING_TYPE, mappingSource)); if (aliasMetaData != null) { mdBuilder.putAlias(aliasMetaData); } return mdBuilder.build(); } private DocIndexMetaData newMeta(IndexMetaData metaData, String name) throws IOException { return new DocIndexMetaData(functions, metaData, new TableIdent(null, name)).build(); } @Before public void before() throws Exception { functions = getFunctions(); udfService = new UserDefinedFunctionService(clusterService); } @Test public void testNestedColumnIdent() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("person") .startObject("properties") .startObject("addresses") .startObject("properties") .startObject("city") .field("type", "string") .endObject() .startObject("country") .field("type", "string") .endObject() .endObject() .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); Reference reference = md.references().get(new ColumnIdent("person", Arrays.asList("addresses", "city"))); assertNotNull(reference); } @Test public void testExtractObjectColumnDefinitions() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("implicit_dynamic") .startObject("properties") .startObject("name") .field("type", "string") .endObject() .endObject() .endObject() .startObject("explicit_dynamic") .field("dynamic", "true") .startObject("properties") .startObject("name") .field("type", "string") .endObject() .startObject("age") .field("type", "integer") .endObject() .endObject() .endObject() .startObject("ignored") .field("dynamic", "false") .startObject("properties") .startObject("name") .field("type", "string") .endObject() .startObject("age") .field("type", "integer") .endObject() .endObject() .endObject() .startObject("strict") .field("dynamic", "strict") .startObject("properties") .startObject("age") .field("type", "integer") .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.columns().size(), is(4)); assertThat(md.references().size(), is(17)); assertThat(md.references().get(new ColumnIdent("implicit_dynamic")).columnPolicy(), is(ColumnPolicy.DYNAMIC)); assertThat(md.references().get(new ColumnIdent("explicit_dynamic")).columnPolicy(), is(ColumnPolicy.DYNAMIC)); assertThat(md.references().get(new ColumnIdent("ignored")).columnPolicy(), is(ColumnPolicy.IGNORED)); assertThat(md.references().get(new ColumnIdent("strict")).columnPolicy(), is(ColumnPolicy.STRICT)); } @Test public void testExtractColumnDefinitions() throws Exception { // @formatter:off XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .field("primary_keys", "integerIndexed") .endObject() .startObject("properties") .startObject("integerIndexed") .field("type", "integer") .endObject() .startObject("integerIndexedBWC") .field("type", "integer") .field("index", "not_analyzed") .endObject() .startObject("integerNotIndexed") .field("type", "integer") .field("index", "false") .endObject() .startObject("integerNotIndexedBWC") .field("type", "integer") .field("index", "no") .endObject() .startObject("stringNotIndexed") .field("type", "string") .field("index", "false") .endObject() .startObject("stringNotIndexedBWC") .field("type", "string") .field("index", "no") .endObject() .startObject("stringNotAnalyzed") .field("type", "keyword") .endObject() .startObject("stringNotAnalyzedBWC") .field("type", "string") .field("index", "not_analyzed") .endObject() .startObject("stringAnalyzed") .field("type", "text") .field("analyzer", "standard") .endObject() .startObject("stringAnalyzedBWC") .field("type", "string") .field("index", "analyzed") .field("analyzer", "standard") .endObject() .startObject("person") .startObject("properties") .startObject("first_name") .field("type", "string") .endObject() .startObject("birthday") .field("type", "date") .endObject() .endObject() .endObject() .endObject() .endObject(); // @formatter:on IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.columns().size(), is(11)); assertThat(md.references().size(), is(20)); Reference birthday = md.references().get(new ColumnIdent("person", "birthday")); assertThat(birthday.valueType(), is(DataTypes.TIMESTAMP)); assertThat(birthday.indexType(), is(Reference.IndexType.NOT_ANALYZED)); Reference integerIndexed = md.references().get(new ColumnIdent("integerIndexed")); assertThat(integerIndexed.indexType(), is(Reference.IndexType.NOT_ANALYZED)); Reference integerIndexedBWC = md.references().get(new ColumnIdent("integerIndexedBWC")); assertThat(integerIndexedBWC.indexType(), is(Reference.IndexType.NOT_ANALYZED)); Reference integerNotIndexed = md.references().get(new ColumnIdent("integerNotIndexed")); assertThat(integerNotIndexed.indexType(), is(Reference.IndexType.NO)); Reference integerNotIndexedBWC = md.references().get(new ColumnIdent("integerNotIndexedBWC")); assertThat(integerNotIndexedBWC.indexType(), is(Reference.IndexType.NO)); Reference stringNotIndexed = md.references().get(new ColumnIdent("stringNotIndexed")); assertThat(stringNotIndexed.indexType(), is(Reference.IndexType.NO)); Reference stringNotIndexedBWC = md.references().get(new ColumnIdent("stringNotIndexedBWC")); assertThat(stringNotIndexedBWC.indexType(), is(Reference.IndexType.NO)); Reference stringNotAnalyzed = md.references().get(new ColumnIdent("stringNotAnalyzed")); assertThat(stringNotAnalyzed.indexType(), is(Reference.IndexType.NOT_ANALYZED)); Reference stringNotAnalyzedBWC = md.references().get(new ColumnIdent("stringNotAnalyzedBWC")); assertThat(stringNotAnalyzedBWC.indexType(), is(Reference.IndexType.NOT_ANALYZED)); Reference stringAnalyzed = md.references().get(new ColumnIdent("stringAnalyzed")); assertThat(stringAnalyzed.indexType(), is(Reference.IndexType.ANALYZED)); Reference stringAnalyzedBWC = md.references().get(new ColumnIdent("stringAnalyzedBWC")); assertThat(stringAnalyzedBWC.indexType(), is(Reference.IndexType.ANALYZED)); ImmutableList<Reference> references = ImmutableList.copyOf(md.references().values()); List<String> fqns = Lists.transform(references, new Function<Reference, String>() { @Nullable @Override public String apply(@Nullable Reference input) { return input.ident().columnIdent().fqn(); } }); assertThat(fqns, Matchers.is( ImmutableList.of("_doc", "_fetchid", "_id", "_raw", "_score", "_uid", "_version", "integerIndexed", "integerIndexedBWC", "integerNotIndexed", "integerNotIndexedBWC", "person", "person.birthday", "person.first_name", "stringAnalyzed", "stringAnalyzedBWC", "stringNotAnalyzed", "stringNotAnalyzedBWC", "stringNotIndexed", "stringNotIndexedBWC"))); } @Test public void testExtractPartitionedByColumns() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .field("primary_keys", "id") .startArray("partitioned_by") .startArray() .value("datum").value("date") .endArray() .endArray() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .startObject("content") .field("type", "string") .field("index", "true") .field("analyzer", "standard") .endObject() .startObject("person") .startObject("properties") .startObject("first_name") .field("type", "string") .endObject() .startObject("birthday") .field("type", "date") .endObject() .endObject() .endObject() .startObject("nested") .field("type", "nested") .startObject("properties") .startObject("inner_nested") .field("type", "date") .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); assertEquals(6, md.columns().size()); assertEquals(16, md.references().size()); assertEquals(1, md.partitionedByColumns().size()); assertEquals(DataTypes.TIMESTAMP, md.partitionedByColumns().get(0).valueType()); assertThat(md.partitionedByColumns().get(0).ident().columnIdent().fqn(), is("datum")); assertThat(md.partitionedBy().size(), is(1)); assertThat(md.partitionedBy().get(0), is(ColumnIdent.fromPath("datum"))); } @Test public void testExtractPartitionedByWithPartitionedByInColumns() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .startArray("partitioned_by") .startArray() .value("datum").value("date") .endArray() .endArray() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("datum") .field("type", "date") .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); // partitioned by column is not added twice assertEquals(2, md.columns().size()); assertEquals(9, md.references().size()); assertEquals(1, md.partitionedByColumns().size()); } @Test public void testExtractPartitionedByWithNestedPartitionedByInColumns() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .startArray("partitioned_by") .startArray() .value("nested.datum").value("date") .endArray() .endArray() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("nested") .field("type", "nested") .startObject("properties") .startObject("datum") .field("type", "date") .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder); DocIndexMetaData md = newMeta(metaData, "test1"); // partitioned by column is not added twice assertEquals(2, md.columns().size()); assertEquals(10, md.references().size()); assertEquals(1, md.partitionedByColumns().size()); } private Map<String, Object> sortProperties(Map<String, Object> mappingSource) { return sortProperties(mappingSource, false); } /** * in the DocumentMapper that ES uses at some place the properties of the mapping are sorted. * this logic doesn't seem to be triggered if the IndexMetaData is created using the * IndexMetaData.Builder. * <p/> * in order to have the same behaviour as if a Node was started and a index with mapping was created * using the ES tools pre-sort the mapping here. */ @SuppressWarnings("unchecked") private Map<String, Object> sortProperties(Map<String, Object> mappingSource, boolean doSort) { Map<String, Object> map; if (doSort) { map = new TreeMap<>(); } else { map = new HashMap<>(); } boolean sortNext; Object value; for (Map.Entry<String, Object> entry : mappingSource.entrySet()) { value = entry.getValue(); sortNext = entry.getKey().equals("properties"); if (value instanceof Map) { map.put(entry.getKey(), sortProperties((Map) entry.getValue(), sortNext)); } else { map.put(entry.getKey(), entry.getValue()); } } return map; } @Test public void testExtractColumnDefinitionsFromEmptyIndex() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test2", builder); DocIndexMetaData md = newMeta(metaData, "test2"); assertThat(md.columns(), hasSize(0)); } @Test public void testDocSysColumnReferences() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("properties") .startObject("content") .field("type", "string") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData metaData = newMeta(getIndexMetaData("test", builder), "test"); Reference id = metaData.references().get(new ColumnIdent("_id")); assertNotNull(id); Reference version = metaData.references().get(new ColumnIdent("_version")); assertNotNull(version); Reference score = metaData.references().get(new ColumnIdent("_score")); assertNotNull(score); } @Test public void testExtractPrimaryKey() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .field("primary_keys", "id") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .startObject("datum") .field("type", "date") .endObject() .startObject("content") .field("type", "string") .field("index", "true") .field("analyzer", "standard") .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test3", builder); DocIndexMetaData md = newMeta(metaData, "test3"); assertThat(md.primaryKey().size(), is(1)); assertThat(md.primaryKey(), contains(new ColumnIdent("id"))); builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("properties") .startObject("content") .field("type", "string") .endObject() .endObject() .endObject() .endObject(); md = newMeta(getIndexMetaData("test4", builder), "test4"); assertThat(md.primaryKey().size(), is(1)); // _id is always the fallback primary key builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .endObject() .endObject(); md = newMeta(getIndexMetaData("test5", builder), "test5"); assertThat(md.primaryKey().size(), is(1)); } @Test public void testExtractMultiplePrimaryKeys() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .array("primary_keys", "id", "title") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test_multi_pk", builder); DocIndexMetaData md = newMeta(metaData, "test_multi_pk"); assertThat(md.primaryKey().size(), is(2)); assertThat(md.primaryKey(), hasItems(ColumnIdent.fromPath("id"), ColumnIdent.fromPath("title"))); } @Test public void testExtractNoPrimaryKey() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test_no_pk", builder); DocIndexMetaData md = newMeta(metaData, "test_no_pk"); assertThat(md.primaryKey().size(), is(1)); assertThat(md.primaryKey(), hasItems(ColumnIdent.fromPath("_id"))); builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .array("primary_keys") // results in empty list .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); metaData = getIndexMetaData("test_no_pk2", builder); md = newMeta(metaData, "test_no_pk2"); assertThat(md.primaryKey().size(), is(1)); assertThat(md.primaryKey(), hasItems(ColumnIdent.fromPath("_id"))); } @Test public void testSchemaWithNotNullColumns() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .startObject("constraints") .array("not_null", "id", "title") .endObject() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test_notnull_columns", builder); DocIndexMetaData md = newMeta(metaData, "test_notnull_columns"); assertThat(md.columns().get(0).isNullable(), is(false)); assertThat(md.columns().get(1).isNullable(), is(false)); } @Test public void testSchemaWithNotNullGeneratedColumn() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .startObject("generated_columns") .field("week", "date_trunc('week', ts)") .endObject() .startObject("constraints") .array("not_null", "week") .endObject() .endObject() .startObject("properties") .startObject("ts").field("type", "date").endObject() .startObject("week").field("type", "long").endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder, Settings.EMPTY, null); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.columns().size(), is(2)); Reference week = md.references().get(new ColumnIdent("week")); assertThat(week, Matchers.notNullValue()); assertThat(week.isNullable(), is(false)); assertThat(week, instanceOf(GeneratedReference.class)); assertThat(((GeneratedReference) week).formattedGeneratedExpression(), is("date_trunc('week', ts)")); assertThat(((GeneratedReference) week).generatedExpression(), isFunction("date_trunc", isLiteral("week"), isReference("ts"))); assertThat(((GeneratedReference) week).referencedReferences(), contains(isReference("ts"))); } @Test public void extractRoutingColumn() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .field("primary_keys", "id") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "multi_field") .field("path", "just_name") .startObject("fields") .startObject("title") .field("type", "string") .endObject() .startObject("ft") .field("type", "string") .field("index", "true") .field("analyzer", "english") .endObject() .endObject() .endObject() .startObject("datum") .field("type", "date") .endObject() .startObject("content") .field("type", "multi_field") .field("path", "just_name") .startObject("fields") .startObject("content") .field("type", "string") .field("index", "false") .endObject() .startObject("ft") .field("type", "string") .field("index", "true") .field("analyzer", "english") .endObject() .endObject() .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData md = newMeta(getIndexMetaData("test8", builder), "test8"); assertThat(md.routingCol(), is(new ColumnIdent("id"))); builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("properties") .startObject("content") .field("type", "string") .endObject() .endObject() .endObject() .endObject(); md = newMeta(getIndexMetaData("test9", builder), "test8"); assertThat(md.routingCol(), is(new ColumnIdent("_id"))); builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .array("primary_keys", "id", "num") .field("routing", "num") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("num") .field("type", "long") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); md = newMeta(getIndexMetaData("test10", builder), "test10"); assertThat(md.routingCol(), is(new ColumnIdent("num"))); } @Test public void extractRoutingColumnFromEmptyIndex() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .endObject() .endObject(); DocIndexMetaData md = newMeta(getIndexMetaData("test11", builder), "test11"); assertThat(md.routingCol(), is(new ColumnIdent("_id"))); } @Test public void testAutogeneratedPrimaryKey() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .endObject() .endObject(); DocIndexMetaData md = newMeta(getIndexMetaData("test11", builder), "test11"); assertThat(md.primaryKey().size(), is(1)); assertThat(md.primaryKey().get(0), is(new ColumnIdent("_id"))); assertThat(md.hasAutoGeneratedPrimaryKey(), is(true)); } @Test public void testNoAutogeneratedPrimaryKey() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("_meta") .field("primary_keys", "id") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData md = newMeta(getIndexMetaData("test11", builder), "test11"); assertThat(md.primaryKey().size(), is(1)); assertThat(md.primaryKey().get(0), is(new ColumnIdent("id"))); assertThat(md.hasAutoGeneratedPrimaryKey(), is(false)); } @Test public void testAnalyzedColumnWithAnalyzer() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("properties") .startObject("content_de") .field("type", "text") .field("index", "true") .field("analyzer", "german") .endObject() .startObject("content_en") .field("type", "text") .field("analyzer", "english") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData md = newMeta(getIndexMetaData("test_analyzer", builder), "test_analyzer"); assertThat(md.columns().size(), is(2)); assertThat(md.columns().get(0).indexType(), is(Reference.IndexType.ANALYZED)); assertThat(md.columns().get(0).ident().columnIdent().fqn(), is("content_de")); assertThat(md.columns().get(1).indexType(), is(Reference.IndexType.ANALYZED)); assertThat(md.columns().get(1).ident().columnIdent().fqn(), is("content_en")); } @Test public void testGeoPointType() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table foo (p geo_point)"); assertThat(md.columns().size(), is(1)); Reference reference = md.columns().get(0); assertThat(reference.valueType(), equalTo(DataTypes.GEO_POINT)); } @Test public void testCreateTableMappingGenerationAndParsingCompat() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table foo (" + "id int primary key," + "tags array(string)," + "o object as (" + " age int," + " name string" + ")," + "date timestamp primary key" + ") partitioned by (date)"); assertThat(md.columns().size(), is(4)); assertThat(md.primaryKey(), Matchers.contains(new ColumnIdent("id"), new ColumnIdent("date"))); assertThat(md.references().get(new ColumnIdent("tags")).valueType(), is(new ArrayType(DataTypes.STRING))); } @Test public void testCreateTableMappingGenerationAndParsingArrayInsideObject() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement( "create table t1 (" + "id int primary key," + "details object as (names array(string))" + ") with (number_of_replicas=0)"); DataType type = md.references().get(new ColumnIdent("details", "names")).valueType(); assertThat(type, Matchers.equalTo(new ArrayType(DataTypes.STRING))); } @Test public void testCreateTableMappingGenerationAndParsingCompatNoMeta() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table foo (id int, name string)"); assertThat(md.columns().size(), is(2)); assertThat(md.hasAutoGeneratedPrimaryKey(), is(true)); } private DocIndexMetaData getDocIndexMetaDataFromStatement(String stmt) throws IOException { Statement statement = SqlParser.createStatement(stmt); final TransportPutIndexTemplateAction transportPutIndexTemplateAction = mock(TransportPutIndexTemplateAction.class); Provider<TransportPutIndexTemplateAction> indexTemplateActionProvider = () -> transportPutIndexTemplateAction; DocTableInfoFactory docTableInfoFactory = new InternalDocTableInfoFactory( functions, new IndexNameExpressionResolver(Settings.EMPTY), indexTemplateActionProvider ); DocSchemaInfo docSchemaInfo = new DocSchemaInfo(Schemas.DEFAULT_SCHEMA_NAME, clusterService, functions, udfService, docTableInfoFactory); CreateTableStatementAnalyzer analyzer = new CreateTableStatementAnalyzer( new Schemas( Settings.EMPTY, ImmutableMap.of("doc", docSchemaInfo), clusterService, new DocSchemaInfoFactory(docTableInfoFactory, functions, udfService)), new FulltextAnalyzerResolver(clusterService, new AnalysisRegistry( new Environment(Settings.builder() .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) .build()), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap() ) ), functions, new NumberOfShards(clusterService) ); Analysis analysis = new Analysis(SessionContext.SYSTEM_SESSION, ParameterContext.EMPTY, ParamTypeHints.EMPTY); CreateTableAnalyzedStatement analyzedStatement = analyzer.analyze( (CreateTable) statement, analysis.parameterContext(), analysis.sessionContext()); Settings.Builder settingsBuilder = Settings.builder() .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .put("index.version.created", org.elasticsearch.Version.CURRENT) .put(analyzedStatement.tableParameter().settings()); IndexMetaData indexMetaData = IndexMetaData.builder(analyzedStatement.tableIdent().name()) .settings(settingsBuilder) .putMapping(new MappingMetaData(Constants.DEFAULT_MAPPING_TYPE, analyzedStatement.mapping())) .build(); return newMeta(indexMetaData, analyzedStatement.tableIdent().name()); } @Test public void testCompoundIndexColumn() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table t (" + " id integer primary key," + " name string," + " fun string index off," + " INDEX fun_name_ft using fulltext(name, fun)" + ")"); assertThat(md.indices().size(), is(1)); assertThat(md.columns().size(), is(3)); assertThat(md.indices().get(ColumnIdent.fromPath("fun_name_ft")), instanceOf(IndexReference.class)); IndexReference indexInfo = md.indices().get(ColumnIdent.fromPath("fun_name_ft")); assertThat(indexInfo.indexType(), is(Reference.IndexType.ANALYZED)); assertThat(indexInfo.ident().columnIdent().fqn(), is("fun_name_ft")); } @Test public void testCompoundIndexColumnNested() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table t (" + " id integer primary key," + " name string," + " o object as (" + " fun string" + " )," + " INDEX fun_name_ft using fulltext(name, o['fun'])" + ")"); assertThat(md.indices().size(), is(1)); assertThat(md.columns().size(), is(3)); assertThat(md.indices().get(ColumnIdent.fromPath("fun_name_ft")), instanceOf(IndexReference.class)); IndexReference indexInfo = md.indices().get(ColumnIdent.fromPath("fun_name_ft")); assertThat(indexInfo.indexType(), is(Reference.IndexType.ANALYZED)); assertThat(indexInfo.ident().columnIdent().fqn(), is("fun_name_ft")); } @Test public void testExtractColumnPolicy() throws Exception { XContentBuilder ignoredBuilder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .field("dynamic", false) .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData mdIgnored = newMeta(getIndexMetaData("test_ignored", ignoredBuilder), "test_ignored"); assertThat(mdIgnored.columnPolicy(), is(ColumnPolicy.IGNORED)); XContentBuilder strictBuilder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .field("dynamic", "strict") .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData mdStrict = newMeta(getIndexMetaData("test_strict", strictBuilder), "test_strict"); assertThat(mdStrict.columnPolicy(), is(ColumnPolicy.STRICT)); XContentBuilder dynamicBuilder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .field("dynamic", true) .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData mdDynamic = newMeta(getIndexMetaData("test_dynamic", dynamicBuilder), "test_dynamic"); assertThat(mdDynamic.columnPolicy(), is(ColumnPolicy.DYNAMIC)); XContentBuilder missingBuilder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData mdMissing = newMeta(getIndexMetaData("test_missing", missingBuilder), "test_missing"); assertThat(mdMissing.columnPolicy(), is(ColumnPolicy.DYNAMIC)); XContentBuilder wrongBuilder = XContentFactory.jsonBuilder() .startObject() .startObject(Constants.DEFAULT_MAPPING_TYPE) .field("dynamic", "wrong") .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("content") .field("type", "string") .field("index", "false") .endObject() .endObject() .endObject() .endObject(); DocIndexMetaData mdWrong = newMeta(getIndexMetaData("test_wrong", wrongBuilder), "test_wrong"); assertThat(mdWrong.columnPolicy(), is(ColumnPolicy.DYNAMIC)); } @Test public void testCreateArrayMapping() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table t (" + " id integer primary key," + " tags array(string)," + " scores array(short)" + ")"); assertThat(md.references().get(ColumnIdent.fromPath("tags")).valueType(), is(new ArrayType(DataTypes.STRING))); assertThat(md.references().get(ColumnIdent.fromPath("scores")).valueType(), is(new ArrayType(DataTypes.SHORT))); } @Test public void testCreateObjectArrayMapping() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table t (" + " id integer primary key," + " tags array(object(strict) as (" + " size double index off," + " numbers array(integer)," + " quote string index using fulltext" + " ))" + ")"); assertThat(md.references().get(ColumnIdent.fromPath("tags")).valueType(), is(new ArrayType(DataTypes.OBJECT))); assertThat(md.references().get(ColumnIdent.fromPath("tags")).columnPolicy(), is(ColumnPolicy.STRICT)); assertThat(md.references().get(ColumnIdent.fromPath("tags.size")).valueType(), is(DataTypes.DOUBLE)); assertThat(md.references().get(ColumnIdent.fromPath("tags.size")).indexType(), is(Reference.IndexType.NO)); assertThat(md.references().get(ColumnIdent.fromPath("tags.numbers")).valueType(), is(new ArrayType(DataTypes.INTEGER))); assertThat(md.references().get(ColumnIdent.fromPath("tags.quote")).valueType(), is(DataTypes.STRING)); assertThat(md.references().get(ColumnIdent.fromPath("tags.quote")).indexType(), is(Reference.IndexType.ANALYZED)); } @Test public void testNoBackwardCompatibleArrayMapping() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .field("primary_keys", "id") .startObject("columns") .startObject("array_col") .field("collection_type", "array") .endObject() .startObject("nested") .startObject("properties") .startObject("inner_nested") .field("collection_type", "array") .endObject() .endObject() .endObject() .endObject() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .startObject("array_col") .field("type", "ip") .endObject() .startObject("nested") .field("type", "nested") .startObject("properties") .startObject("inner_nested") .field("type", "date") .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData indexMetaData = getIndexMetaData("test1", builder); DocIndexMetaData docIndexMetaData = newMeta(indexMetaData, "test1"); // ARRAY TYPES NOT DETECTED assertThat(docIndexMetaData.references().get(ColumnIdent.fromPath("array_col")).valueType(), is(DataTypes.IP)); assertThat(docIndexMetaData.references().get(ColumnIdent.fromPath("nested.inner_nested")).valueType(), is(DataTypes.TIMESTAMP)); } @Test public void testNewArrayMapping() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .field("primary_keys", "id") .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .startObject("title") .field("type", "string") .field("index", "false") .endObject() .startObject("array_col") .field("type", "array") .startObject("inner") .field("type", "ip") .endObject() .endObject() .startObject("nested") .field("type", "object") .startObject("properties") .startObject("inner_nested") .field("type", "array") .startObject("inner") .field("type", "date") .endObject() .endObject() .endObject() .endObject() .endObject() .endObject(); IndexMetaData indexMetaData = getIndexMetaData("test1", builder); DocIndexMetaData docIndexMetaData = newMeta(indexMetaData, "test1"); assertThat(docIndexMetaData.references().get(ColumnIdent.fromPath("array_col")).valueType(), is(new ArrayType(DataTypes.IP))); assertThat(docIndexMetaData.references().get(ColumnIdent.fromPath("nested.inner_nested")).valueType(), is(new ArrayType(DataTypes.TIMESTAMP))); } @Test public void testMergePartitionWithDifferentShardsAndReplicas() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .field("primary_keys", "id") .startArray("partitioned_by") .startArray().value("datum").value("date").endArray() .endArray() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .endObject() .endObject(); Settings templateSettings = Settings.builder() .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 2) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 6) .build(); IndexMetaData metaData = getIndexMetaData("test1", builder, templateSettings, null); DocIndexMetaData md = newMeta(metaData, "test1"); PartitionName partitionName = new PartitionName("test1", Arrays.asList(new BytesRef("0"))); Settings partitionSettings = Settings.builder() .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 10) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 50) .build(); AliasMetaData aliasMetaData = AliasMetaData.newAliasMetaDataBuilder("test1") .build(); IndexMetaData partitionMetaData = getIndexMetaData(partitionName.asIndexName(), builder, partitionSettings, aliasMetaData); DocIndexMetaData partitionMD = newMeta(partitionMetaData, partitionName.asIndexName()); assertThat(partitionMD.aliases().size(), is(1)); assertThat(partitionMD.aliases(), hasItems("test1")); assertThat(partitionMD.isAlias(), is(false)); assertThat(partitionMD.concreteIndexName(), is(partitionName.asIndexName())); DocIndexMetaData merged = md.merge(partitionMD, mock(TransportPutIndexTemplateAction.class), true); assertThat(merged.numberOfReplicas(), is(BytesRefs.toBytesRef(2))); assertThat(merged.numberOfShards(), is(6)); } @Test public void testStringArrayWithFulltextIndex() throws Exception { DocIndexMetaData metaData = getDocIndexMetaDataFromStatement( "create table t (tags array(string) index using fulltext)"); Reference reference = metaData.columns().get(0); assertThat(reference.valueType(), equalTo(new ArrayType(DataTypes.STRING))); } @Test public void testCreateTableWithNestedPrimaryKey() throws Exception { DocIndexMetaData metaData = getDocIndexMetaDataFromStatement("create table t (o object as (x int primary key))"); assertThat(metaData.primaryKey(), contains(new ColumnIdent("o", "x"))); metaData = getDocIndexMetaDataFromStatement("create table t (x object as (y object as (z int primary key)))"); assertThat(metaData.primaryKey(), contains(new ColumnIdent("x", Arrays.asList("y", "z")))); } @Test public void testSchemaEquals() throws Exception { DocIndexMetaData md = getDocIndexMetaDataFromStatement("create table schema_equals1 (id byte, tags array(string))"); DocIndexMetaData mdSame = getDocIndexMetaDataFromStatement("create table schema_equals1 (id byte, tags array(string))"); DocIndexMetaData mdOther = getDocIndexMetaDataFromStatement("create table schema_equals2 (id byte, tags array(string))"); DocIndexMetaData mdWithPk = getDocIndexMetaDataFromStatement("create table schema_equals3 (id byte primary key, tags array(string))"); DocIndexMetaData mdWithStringCol = getDocIndexMetaDataFromStatement("create table schema_equals4 (id byte, tags array(string), col string)"); DocIndexMetaData mdWithStringColNotAnalyzed = getDocIndexMetaDataFromStatement("create table schema_equals5 (id byte, tags array(string), col string index off)"); DocIndexMetaData mdWithStringColNotAnalyzedAndIndex = getDocIndexMetaDataFromStatement("create table schema_equals6 (id byte, tags array(string), col string index off, index ft_index using fulltext(col))"); assertThat(md.schemaEquals(md), is(true)); assertThat(md == mdSame, is(false)); assertThat(md.schemaEquals(mdSame), is(true)); // same table name assertThat(md.schemaEquals(mdOther), is(false)); // different table name assertThat(md.schemaEquals(mdWithPk), is(false)); assertThat(md.schemaEquals(mdWithStringCol), is(false)); assertThat(mdWithPk.schemaEquals(mdWithStringCol), is(false)); assertThat(mdWithStringCol.schemaEquals(mdWithStringColNotAnalyzed), is(false)); assertThat(mdWithStringColNotAnalyzed.schemaEquals(mdWithStringColNotAnalyzedAndIndex), is(false)); } @Test public void testSchemaWithGeneratedColumn() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .startObject("generated_columns") .field("week", "date_trunc('week', ts)") .endObject() .endObject() .startObject("properties") .startObject("ts").field("type", "date").endObject() .startObject("week").field("type", "long").endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder, Settings.EMPTY, null); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.columns().size(), is(2)); Reference week = md.references().get(new ColumnIdent("week")); assertThat(week, Matchers.notNullValue()); assertThat(week, instanceOf(GeneratedReference.class)); assertThat(((GeneratedReference) week).formattedGeneratedExpression(), is("date_trunc('week', ts)")); assertThat(((GeneratedReference) week).generatedExpression(), isFunction("date_trunc", isLiteral("week"), isReference("ts"))); assertThat(((GeneratedReference) week).referencedReferences(), contains(isReference("ts"))); } @Test public void testCopyToWithoutMetaIndices() throws Exception { // regression test... this mapping used to cause an NPE XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("description") .field("type", "string") .array("copy_to", "description_ft") .endObject() .startObject("description_ft") .field("type", "string") .field("analyzer", "english") .endObject() .endObject() .endObject(); IndexMetaData metaData = getIndexMetaData("test1", builder, Settings.EMPTY, null); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.indices().size(), is(1)); assertThat(md.indices().keySet().iterator().next(), is(new ColumnIdent("description_ft"))); } @Test public void testVersionsRead() throws Exception { // @formatter:off XContentBuilder builder = XContentFactory.jsonBuilder() .startObject() .startObject("_meta") .startObject("version") .startObject(Version.Property.CREATED.toString()) .field(Version.CRATEDB_VERSION_KEY, 560499) .field(Version.ES_VERSION_KEY, 2040299) .endObject() .startObject(Version.Property.UPGRADED.toString()) .field(Version.CRATEDB_VERSION_KEY, Version.CURRENT.id) .field(Version.ES_VERSION_KEY, Version.CURRENT.esVersion.id) .endObject() .endObject() .endObject() .startObject("properties") .startObject("id") .field("type", "integer") .endObject() .endObject() .endObject(); // @formatter: on IndexMetaData metaData = getIndexMetaData("test1", builder, Settings.EMPTY, null); DocIndexMetaData md = newMeta(metaData, "test1"); assertThat(md.versionCreated().id, is(560499)); assertThat(md.versionCreated().esVersion.id, is(2040299)); assertThat(md.versionUpgraded(), is(Version.CURRENT)); } }