Skip to content

Commit

Permalink
[ML] Fixes job validation for nested time fields. (#24137) (#24255)
Browse files Browse the repository at this point in the history
This fixes an issue where job validation would return that a date field was invalid for nested date fields.
  • Loading branch information
walterra authored Oct 19, 2018
1 parent e073a17 commit 1f98dd7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"fields":{"metadata.timestamp":{"date":{"type":"date","searchable":true,"aggregatable":true}}}}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

import _ from 'lodash';
import expect from 'expect.js';
import { validateTimeRange } from '../validate_time_range';
import { isValidTimeField, validateTimeRange } from '../validate_time_range';

import mockTimeField from './mock_time_field';
import mockTimeFieldNested from './mock_time_field_nested';
import mockTimeRange from './mock_time_range';

const mockSearchResponse = {
Expand All @@ -26,6 +27,58 @@ const callWithRequestFactory = (resp) => {
};
};

function getMinimalValidJob() {
return {
analysis_config: {
bucket_span: '15m',
detectors: [],
influencers: []
},
data_description: { time_field: '@timestamp' },
datafeed_config: {
indices: []
}
};
}

describe('ML - isValidTimeField', () => {
it('called without job config argument triggers Promise rejection', (done) => {
isValidTimeField(callWithRequestFactory(mockSearchResponse)).then(
() => done(new Error('Promise should not resolve for this test without job argument.')),
() => done()
);
});

it('time_field `@timestamp`', (done) => {
isValidTimeField(callWithRequestFactory(mockSearchResponse), getMinimalValidJob()).then(
(valid) => {
expect(valid).to.be(true);
done();
},
() => done(new Error('isValidTimeField Promise failed for time_field `@timestamp`.'))
);
});

it('time_field `metadata.timestamp`', (done) => {
const mockJobConfigNestedDate = getMinimalValidJob();
mockJobConfigNestedDate.data_description.time_field = 'metadata.timestamp';

const mockSearchResponseNestedDate = {
fieldCaps: mockTimeFieldNested,
search: mockTimeRange
};

isValidTimeField(callWithRequestFactory(mockSearchResponseNestedDate), mockJobConfigNestedDate).then(
(valid) => {
expect(valid).to.be(true);
done();
},
() => done(new Error('isValidTimeField Promise failed for time_field `metadata.timestamp`.'))
);
});

});

describe('ML - validateTimeRange', () => {

it('called without arguments', (done) => {
Expand Down Expand Up @@ -66,23 +119,11 @@ describe('ML - validateTimeRange', () => {
);
});

const minimumValidJob = {
analysis_config: {
bucket_span: '15m',
detectors: [],
influencers: []
},
data_description: { time_field: '@timestamp' },
datafeed_config: {
indices: []
}
};

it('invalid time field', () => {
const mockSearchResponseInvalid = _.cloneDeep(mockSearchResponse);
mockSearchResponseInvalid.fieldCaps = undefined;
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponseInvalid), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponseInvalid), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_field_invalid']);
Expand All @@ -91,7 +132,7 @@ describe('ML - validateTimeRange', () => {
});

it('too short time range, 25x bucket span is less than 2h', () => {
const jobShortTimeRange = _.cloneDeep(minimumValidJob);
const jobShortTimeRange = getMinimalValidJob();
jobShortTimeRange.analysis_config.bucket_span = '1s';
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), jobShortTimeRange, duration).then(
Expand All @@ -104,7 +145,7 @@ describe('ML - validateTimeRange', () => {

it('too short time range, 25x bucket span is more than 2h', () => {
const duration = { start: 0, end: 1 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_short']);
Expand All @@ -114,7 +155,7 @@ describe('ML - validateTimeRange', () => {

it('time range between 2h and 25x bucket span', () => {
const duration = { start: 0, end: 8000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_short']);
Expand All @@ -124,7 +165,7 @@ describe('ML - validateTimeRange', () => {

it('valid time range', () => {
const duration = { start: 0, end: 100000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['success_time_range']);
Expand All @@ -134,7 +175,7 @@ describe('ML - validateTimeRange', () => {

it('invalid time range, start time is before the UNIX epoch', () => {
const duration = { start: -1, end: 100000000 };
return validateTimeRange(callWithRequestFactory(mockSearchResponse), minimumValidJob, duration).then(
return validateTimeRange(callWithRequestFactory(mockSearchResponse), getMinimalValidJob(), duration).then(
(messages) => {
const ids = messages.map(m => m.id);
expect(ids).to.eql(['time_range_before_epoch']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export async function isValidTimeField(callWithRequest, job) {
index,
fields: [timeField]
});
const fieldType = _.get(fieldCaps, `fields.${timeField}.date.type`);
// get the field's type with the following notation
// because a nested field could contain dots and confuse _.get
const fieldType = _.get(fieldCaps, `fields['${timeField}'].date.type`);
return fieldType === ES_FIELD_TYPES.DATE;
}

Expand Down

0 comments on commit 1f98dd7

Please sign in to comment.