/* * 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.index.mapper.date; import org.elasticsearch.Version; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.test.ESSingleNodeTestCase; import org.junit.Before; import java.util.Arrays; import java.util.List; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.test.VersionUtils.randomVersionBetween; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoSearchHits; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; /** * Test class to check for all the conditions defined in * https://github.com/elastic/elasticsearch/issues/10971 */ public class DateBackwardsCompatibilityTests extends ESSingleNodeTestCase { private String index = "testindex"; private String type = "testtype"; private Version randomVersionBelow2x; @Before public void setup() throws Exception { randomVersionBelow2x = randomVersionBetween(getRandom(), Version.V_0_90_0, Version.V_1_6_1); } public void testThatPre2xIndicesNumbersAreTreatedAsEpochs() throws Exception { index = createPre2xIndexAndMapping(); long dateInMillis = 1435073872l * 1000; // Tue Jun 23 17:37:52 CEST 2015 XContentBuilder document = jsonBuilder().startObject().field("date_field", dateInMillis).endObject(); index(document); // search for date in time range QueryBuilder query = QueryBuilders.rangeQuery("date_field").from("2015-06-23").to("2015-06-24"); SearchResponse response = client().prepareSearch(index).setQuery(query).get(); assertHitCount(response, 1); } public void testThatPre2xFailedStringParsingLeadsToEpochParsing() throws Exception { index = createPre2xIndexAndMapping(); long dateInMillis = 1435073872l * 1000; // Tue Jun 23 17:37:52 CEST 2015 String date = String.valueOf(dateInMillis); XContentBuilder document = jsonBuilder().startObject().field("date_field", date).endObject(); index(document); // search for date in time range QueryBuilder query = QueryBuilders.rangeQuery("date_field").from("2015-06-23").to("2015-06-24"); SearchResponse response = client().prepareSearch(index).setQuery(query).get(); assertHitCount(response, 1); } public void testThatPre2xSupportsUnixTimestampsInAnyDateFormat() throws Exception { long dateInMillis = 1435073872l * 1000; // Tue Jun 23 17:37:52 CEST 2015 List<String> dateFormats = Arrays.asList("dateOptionalTime", "weekDate", "tTime", "ordinalDate", "hourMinuteSecond", "hourMinute"); for (String format : dateFormats) { XContentBuilder mapping = jsonBuilder().startObject().startObject("properties") .startObject("date_field").field("type", "date").field("format", format).endObject() .endObject().endObject(); index = createIndex(randomVersionBelow2x, mapping); XContentBuilder document = XContentFactory.jsonBuilder() .startObject() .field("date_field", String.valueOf(dateInMillis)) .endObject(); index(document); // indexing as regular timestamp should work as well document = XContentFactory.jsonBuilder() .startObject() .field("date_field", dateInMillis) .endObject(); index(document); client().admin().indices().prepareDelete(index).get(); } } public void testThatPre2xIndicesNumbersAreTreatedAsTimestamps() throws Exception { // looks like a unix time stamp but is meant as 2016-06-23T01:00:00.000 - see the specified date format long date = 2015062301000l; XContentBuilder mapping = jsonBuilder().startObject().startObject("properties") .startObject("date_field").field("type", "date").field("format","yyyyMMddHHSSS").endObject() .endObject().endObject(); index = createIndex(randomVersionBelow2x, mapping); XContentBuilder document = XContentFactory.jsonBuilder() .startObject() .field("date_field", randomBoolean() ? String.valueOf(date) : date) .endObject(); index(document); // no results in expected time range QueryBuilder query = QueryBuilders.rangeQuery("date_field").from("2015-06-23").to("2015-06-24").format("dateOptionalTime"); SearchResponse response = client().prepareSearch(index).setQuery(query).get(); assertNoSearchHits(response); // result in unix timestamp range QueryBuilder timestampQuery = QueryBuilders.rangeQuery("date_field").from(2015062300000L).to(2015062302000L); assertHitCount(client().prepareSearch(index).setQuery(timestampQuery).get(), 1); // result should also work with regular specified dates QueryBuilder regularTimeQuery = QueryBuilders.rangeQuery("date_field").from("2033-11-08").to("2033-11-09").format("dateOptionalTime"); assertHitCount(client().prepareSearch(index).setQuery(regularTimeQuery).get(), 1); } /** curl -XPUT "$NODE:9200/testindex" -d'{ "mappings":{"testtype":{"properties":{ "date_field":{"type":"date","format":"yyyyMMddHHSSS"}}}}}' curl -XPOST "$NODE:9200/testindex/testtype" -d'{ "date_field":2015062301000 }' curl -XPOST "$NODE:9200/testindex/testtype" -d'{ "date_field":"2015062301000" }' curl -XGET "$NODE:9200/testindex/_search?pretty" -d'{ "query":{"range":{"date_field":{"gte":"2015-06-23","lt":"2015-06-24","format":"dateOptionalTime"}}}}' */ public void testThatPost2xIndicesNumbersAreTreatedAsStrings() throws Exception { // looks like a unix time stamp but is meant as 2016-06-23T01:00:00.000 - see the specified date format long date = 2015062301000l; XContentBuilder mapping = jsonBuilder().startObject().startObject("properties") .startObject("date_field").field("type", "date").field("format","yyyyMMddHHSSS").endObject() .endObject().endObject(); index = createIndex(Version.CURRENT, mapping); XContentBuilder document = XContentFactory.jsonBuilder() .startObject() .field("date_field", String.valueOf(date)) .endObject(); index(document); document = XContentFactory.jsonBuilder() .startObject() .field("date_field", date) .endObject(); index(document); // search for date in time range QueryBuilder query = QueryBuilders.rangeQuery("date_field").from("2015-06-23").to("2015-06-24").format("dateOptionalTime"); SearchResponse response = client().prepareSearch(index).setQuery(query).get(); assertHitCount(response, 2); } public void testDynamicDateDetectionIn2xDoesNotSupportEpochs() throws Exception { try { XContentBuilder mapping = jsonBuilder().startObject() .startArray("dynamic_date_formats").value("dateOptionalTime").value("epoch_seconds").endArray() .endObject(); createIndex(Version.CURRENT, mapping); fail("Expected a MapperParsingException, but did not happen"); } catch (MapperParsingException e) { assertThat(e.getMessage(), containsString("Failed to parse mapping [" + type + "]")); assertThat(e.getMessage(), containsString("Epoch [epoch_seconds] is not supported as dynamic date format")); } } private String createPre2xIndexAndMapping() throws Exception { return createIndexAndMapping(randomVersionBelow2x); } private String createIndexAndMapping(Version version) throws Exception { XContentBuilder mapping = jsonBuilder().startObject().startObject("properties") .startObject("date_field").field("type", "date").field("format", "dateOptionalTime").endObject() .endObject().endObject(); return createIndex(version, mapping); } private String createIndex(Version version, XContentBuilder mapping) { Settings settings = settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); createIndex(index, settings, type, mapping); ensureGreen(index); return index; } private void index(XContentBuilder document) { IndexResponse indexResponse = client().prepareIndex(index, type).setSource(document).setRefresh(true).get(); assertThat(indexResponse.isCreated(), is(true)); } }