Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index name expression resolver bwc layer for date parsing #58503

Merged
merged 15 commits into from
Jul 2, 2020
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,66 @@ teardown:
date: "2016-04-22T16:32:14.968Z"
}
- match: { _index: "events-2016-04-22"}

---
"Test date index name processor with joda pattern":
- do:
ingest.put_pipeline:
id: "1"
body: >
{
"processors": [
{
"date_index_name" : {
"field": "date",
"date_rounding": "d",
"index_name_prefix": "prefix-",
"index_name_format": "xxxx-w"
}
}
]
}
- match: { acknowledged: true }

- do:
index:
index: test
type: _doc
id: 1
pipeline: "1"
body: {
date: "2020-08-10T01:01:01.000Z"
}
- match: { _index: "prefix-2020-33"}


---
"Test date index name processor with java pattern":
- do:
ingest.put_pipeline:
id: "1"
body: >
{
"processors": [
{
"date_index_name" : {
"field": "date",
"date_rounding": "d",
"index_name_prefix": "prefix-",
"index_name_format": "8YYYY-w"
}
}
]
}
- match: { acknowledged: true }

- do:
index:
index: test
type: _doc
id: 1
pipeline: "1"
body: {
date: "2020-08-10T01:01:01.000Z"
}
- match: { _index: "prefix-2020-33"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
"Index java 8 date without timezone":

- skip:
version: " - 6.7.99"
reason: fixed in 6.8.11

- do:
indices.create:
index: test_index
body:
settings:
number_of_shards: 1
mappings:
doc:
properties:
date_field:
type: date
format: "8YYYY-ww"
- do:
bulk:
refresh: true
body:
- '{"index": {"_index": "test_index", "_type": "doc", "_id": "1"}}'
- '{"date_field": "2020-32"}'
- '{"index": {"_index": "test_index", "_type": "doc", "_id": "2"}}'
- '{"date_field": "2020-33"}'

- match: { errors: false }

- do:
get:
index: test_index
type: doc
id: 2

- match: { _index: test_index }
- match: { _type: doc }
- match: { _id: "2"}
- match: { _version: 1}
- match: { _source: { "date_field": "2020-33" }}

- do:
search:
index: test_index
body: { "query": { "range": { "date_field": { "gte": "2020-33" } } } }

- match: {hits.total: 1 }
- match: {hits.hits.0._index: test_index }
- match: {hits.hits.0._type: doc }
- match: {hits.hits.0._source.date_field: "2020-33" }

Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,26 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.joda.JodaDateFormatter;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.time.DateUtils;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.indices.InvalidIndexNameException;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
Expand All @@ -64,12 +61,11 @@ public class IndexNameExpressionResolver {
private final DateMathExpressionResolver dateMathExpressionResolver;

public IndexNameExpressionResolver(Settings settings) {
dateMathExpressionResolver = new DateMathExpressionResolver(settings);
expressionResolvers = Arrays.asList(
dateMathExpressionResolver,
new WildcardExpressionResolver());
dateMathExpressionResolver = new DateMathExpressionResolver(settings),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why is this moved here? i think setting a member variable shoudl be done on it's own line, like it was before, not hidden inside a list initialization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ agree. It was moved because I just cherrypicked the changes from https://github.com/elastic/elasticsearch/pull/34507/files and there was no conflict on this one
will fix

new WildcardExpressionResolver()
);
}


/**
* Same as {@link #concreteIndexNames(ClusterState, IndicesOptions, String...)}, but the index expressions and options
Expand Down Expand Up @@ -848,22 +844,23 @@ private static List<String> resolveEmptyOrTrivialWildcard(IndicesOptions options

static final class DateMathExpressionResolver implements ExpressionResolver {

private static final DateFormatter DEFAULT_DATE_FORMATTER = DateFormatters.forPattern("uuuu.MM.dd");
private static final String EXPRESSION_LEFT_BOUND = "<";
private static final String EXPRESSION_RIGHT_BOUND = ">";
private static final char LEFT_BOUND = '{';
private static final char RIGHT_BOUND = '}';
private static final char ESCAPE_CHAR = '\\';
private static final char TIME_ZONE_BOUND = '|';

private final DateTimeZone defaultTimeZone;
private final ZoneId defaultTimeZone;
private final String defaultDateFormatterPattern;
private final DateTimeFormatter defaultDateFormatter;
private final DateFormatter defaultDateFormatter;

DateMathExpressionResolver(Settings settings) {
String defaultTimeZoneId = settings.get("date_math_expression_resolver.default_time_zone", "UTC");
this.defaultTimeZone = DateTimeZone.forID(defaultTimeZoneId);
defaultDateFormatterPattern = settings.get("date_math_expression_resolver.default_date_format", "YYYY.MM.dd");
this.defaultDateFormatter = DateTimeFormat.forPattern(defaultDateFormatterPattern);
this.defaultTimeZone = ZoneId.of(defaultTimeZoneId);
defaultDateFormatterPattern = settings.get("date_math_expression_resolver.default_date_format", "uuuu.MM.dd");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is in 6.8, shouldn't this default format be prefixed with '8' since we still use joda by default here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, should be with 8. will fix

this.defaultDateFormatter = DateFormatters.forPattern("uuuu.MM.dd");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this use the pattern in defaultDateFormatterPattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, this should use the defaultDateFormatterPattern

}

@Override
Expand Down Expand Up @@ -930,11 +927,10 @@ String resolveExpression(String expression, final Context context) {
int dateTimeFormatLeftBoundIndex = inPlaceHolderString.indexOf(LEFT_BOUND);
String mathExpression;
String dateFormatterPattern;
DateTimeFormatter dateFormatter;
final DateTimeZone timeZone;
DateFormatter dateFormatter;
final ZoneId timeZone;
if (dateTimeFormatLeftBoundIndex < 0) {
mathExpression = inPlaceHolderString;
dateFormatterPattern = defaultDateFormatterPattern;
dateFormatter = defaultDateFormatter;
timeZone = defaultTimeZone;
} else {
Expand All @@ -947,23 +943,25 @@ String resolveExpression(String expression, final Context context) {
inPlaceHolderString);
}
mathExpression = inPlaceHolderString.substring(0, dateTimeFormatLeftBoundIndex);
String patternAndTZid =
String dateFormatterPatternAndTimeZoneId =
inPlaceHolderString.substring(dateTimeFormatLeftBoundIndex + 1, inPlaceHolderString.length() - 1);
int formatPatternTimeZoneSeparatorIndex = patternAndTZid.indexOf(TIME_ZONE_BOUND);
int formatPatternTimeZoneSeparatorIndex = dateFormatterPatternAndTimeZoneId.indexOf(TIME_ZONE_BOUND);
if (formatPatternTimeZoneSeparatorIndex != -1) {
dateFormatterPattern = patternAndTZid.substring(0, formatPatternTimeZoneSeparatorIndex);
timeZone = DateTimeZone.forID(patternAndTZid.substring(formatPatternTimeZoneSeparatorIndex + 1));
dateFormatterPattern
= dateFormatterPatternAndTimeZoneId.substring(0, formatPatternTimeZoneSeparatorIndex);
timeZone = ZoneId.of(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We previously added the '8' prefix in date formats so that both joda and java formats could be distinguished. I have a vague memory that the zone identifiers do not completely match up between joda and java. Is this correct? If so, what happens if the format specified by the user is one not supported by java?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we test against this discrepancies in https://github.com/elastic/elasticsearch/blob/6.8/server/src/test/java/org/elasticsearch/common/time/DateUtilsTests.java#L38
as long as timezone dbs are the same in both joda and java (and we make sure about this with this test) then there should always be a match

dateFormatterPatternAndTimeZoneId.substring(formatPatternTimeZoneSeparatorIndex + 1));
} else {
dateFormatterPattern = patternAndTZid;
dateFormatterPattern = dateFormatterPatternAndTimeZoneId;
timeZone = defaultTimeZone;
}
dateFormatter = DateTimeFormat.forPattern(dateFormatterPattern);
dateFormatter = DateFormatter.forPattern(dateFormatterPattern);
}
DateTimeFormatter parser = dateFormatter.withLocale(Locale.ROOT).withZone(timeZone);
JodaDateFormatter formatter = new JodaDateFormatter(dateFormatterPattern, parser, parser);
DateFormatter formatter = dateFormatter.withZone(timeZone);
DateMathParser dateMathParser = formatter.toDateMathParser();
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false,
DateUtils.dateTimeZoneToZoneId(timeZone));
long millis = dateMathParser.parse(mathExpression, context::getStartTime, false, timeZone);


String time = formatter.formatMillis(millis);
beforePlaceHolderSb.append(time);
inPlaceHolderSb = new StringBuilder();
Expand Down Expand Up @@ -1007,18 +1005,4 @@ String resolveExpression(String expression, final Context context) {
return beforePlaceHolderSb.toString();
}
}

/**
* Returns <code>true</code> iff the given expression resolves to the given index name otherwise <code>false</code>
*/
public final boolean matchesIndex(String indexName, String expression, ClusterState state) {
final String[] concreteIndices = concreteIndexNames(state, IndicesOptions.lenientExpandOpen(), expression);
for (String index : concreteIndices) {
if (Regex.simpleMatch(index, indexName)) {
return true;
}
}
return indexName.equals(expression);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;

import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
Expand All @@ -37,7 +36,6 @@
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalQueries;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
Expand Down Expand Up @@ -1676,24 +1674,28 @@ public static ZonedDateTime from(TemporalAccessor accessor) {
} else if (accessor.isSupported(MONTH_OF_YEAR)) {
// missing year, falling back to the epoch and then filling
return getLocaldate(accessor).atStartOfDay(zoneId);
} else if (accessor.isSupported(WeekFields.SUNDAY_START.weekBasedYear()) || accessor.isSupported(WeekFields.ISO.weekBasedYear())) {
return localDateFromWeekBasedDate(WeekFields.SUNDAY_START, accessor).atStartOfDay(zoneId);
} else if (accessor.isSupported(WeekFields.ISO.weekBasedYear())) {
if (accessor.isSupported(WeekFields.ISO.weekOfWeekBasedYear())) {
return Year.of(accessor.get(WeekFields.ISO.weekBasedYear()))
.atDay(1)
.with(WeekFields.ISO.weekOfWeekBasedYear(), accessor.getLong(WeekFields.ISO.weekOfWeekBasedYear()))
.atStartOfDay(zoneId);
} else {
return Year.of(accessor.get(WeekFields.ISO.weekBasedYear()))
.atDay(1)
.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY))
.atStartOfDay(zoneId);
}
return localDateFromWeekBasedDate(WeekFields.ISO, accessor).atStartOfDay(zoneId);
}

// we should not reach this piece of code, everything being parsed we should be able to
// convert to a zoned date time! If not, we have to extend the above methods
throw new IllegalArgumentException("temporal accessor [" + accessor + "] cannot be converted to zoned date time");
}
private static LocalDate localDateFromWeekBasedDate(WeekFields WEEK_FIELDS, TemporalAccessor accessor) {
if (accessor.isSupported(WEEK_FIELDS.weekOfWeekBasedYear())) {
return LocalDate.ofEpochDay(0)
.with(WEEK_FIELDS.weekBasedYear(), accessor.get(WEEK_FIELDS.weekBasedYear()))
.with(WEEK_FIELDS.weekOfWeekBasedYear(), accessor.get(WEEK_FIELDS.weekOfWeekBasedYear()))
.with(ChronoField.DAY_OF_WEEK, WeekFields.ISO.getFirstDayOfWeek().getValue());
} else {
return LocalDate.ofEpochDay(0)
.with(WEEK_FIELDS.weekBasedYear(), accessor.get(WEEK_FIELDS.weekBasedYear()))
.with(ChronoField.DAY_OF_WEEK, WeekFields.ISO.getFirstDayOfWeek().getValue());
}
}

private static LocalDate getLocaldate(TemporalAccessor accessor) {
if (accessor.isSupported(MONTH_OF_YEAR)) {
Expand Down