[Lens] Fix rollup related bugs (#75314)

Co-authored-by: Marta Bondyra <marta.bondyra@elastic.co>
This commit is contained in:
Joe Reuter 2020-08-26 09:09:40 +02:00 committed by GitHub
parent eecf4aa71f
commit ddf99b64db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1765 additions and 34 deletions

View file

@ -84,6 +84,7 @@ const initialState: IndexPatternPrivateState = {
id: '1',
title: 'idx1',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -134,6 +135,7 @@ const initialState: IndexPatternPrivateState = {
id: '2',
title: 'idx2',
timeFieldName: 'timestamp',
hasRestrictions: true,
fields: [
{
name: 'timestamp',
@ -191,6 +193,7 @@ const initialState: IndexPatternPrivateState = {
id: '3',
title: 'idx3',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -322,8 +325,20 @@ describe('IndexPattern Data Panel', () => {
isFirstExistenceFetch: false,
currentIndexPatternId: 'a',
indexPatterns: {
a: { id: 'a', title: 'aaa', timeFieldName: 'atime', fields: [] },
b: { id: 'b', title: 'bbb', timeFieldName: 'btime', fields: [] },
a: {
id: 'a',
title: 'aaa',
timeFieldName: 'atime',
fields: [],
hasRestrictions: false,
},
b: {
id: 'b',
title: 'bbb',
timeFieldName: 'btime',
fields: [],
hasRestrictions: false,
},
},
layers: {
1: {

View file

@ -126,6 +126,7 @@ export function IndexPatternDataPanel({
title: indexPatterns[id].title,
timeFieldName: indexPatterns[id].timeFieldName,
fields: indexPatterns[id].fields,
hasRestrictions: indexPatterns[id].hasRestrictions,
}));
const dslQuery = buildSafeEsQuery(
@ -422,6 +423,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
]
);
const fieldInfoUnavailable = existenceFetchFailed || currentIndexPattern.hasRestrictions;
return (
<ChildDragDropProvider {...dragDropContext}>
<EuiFlexGroup
@ -568,7 +571,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
initialIsOpen={localState.isAvailableAccordionOpen}
id="lnsIndexPatternAvailableFields"
label={
existenceFetchFailed
fieldInfoUnavailable
? i18n.translate('xpack.lens.indexPattern.allFieldsLabel', {
defaultMessage: 'All fields',
})
@ -577,6 +580,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
})
}
exists={true}
hideDetails={fieldInfoUnavailable}
hasLoaded={!!hasSyncedExistingFields}
fieldsCount={filteredFieldGroups.availableFields.length}
isFiltered={
@ -609,7 +613,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
}
/>
<EuiSpacer size="m" />
{!existenceFetchFailed && (
{!fieldInfoUnavailable && (
<FieldsAccordion
initialIsOpen={localState.isEmptyAccordionOpen}
isFiltered={

View file

@ -42,6 +42,7 @@ const expectedIndexPatterns = {
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasExistence: true,
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -1256,6 +1257,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
foo: {
id: 'foo',
title: 'Foo pattern',
hasRestrictions: false,
fields: [
{
aggregatable: true,

View file

@ -47,6 +47,7 @@ export interface FieldsAccordionProps {
renderCallout: JSX.Element;
exists: boolean;
showExistenceFetchError?: boolean;
hideDetails?: boolean;
}
export const InnerFieldsAccordion = function InnerFieldsAccordion({
@ -61,13 +62,20 @@ export const InnerFieldsAccordion = function InnerFieldsAccordion({
fieldProps,
renderCallout,
exists,
hideDetails,
showExistenceFetchError,
}: FieldsAccordionProps) {
const renderField = useCallback(
(field: IndexPatternField) => (
<FieldItem {...fieldProps} key={field.name} field={field} exists={!!exists} />
<FieldItem
{...fieldProps}
key={field.name}
field={field}
exists={exists}
hideDetails={hideDetails}
/>
),
[fieldProps, exists]
[fieldProps, exists, hideDetails]
);
return (

View file

@ -21,6 +21,7 @@ const expectedIndexPatterns = {
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -70,6 +71,7 @@ const expectedIndexPatterns = {
id: '2',
title: 'my-fake-restricted-pattern',
timeFieldName: 'timestamp',
hasRestrictions: true,
fields: [
{
name: 'timestamp',

View file

@ -20,6 +20,7 @@ const expectedIndexPatterns = {
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -68,6 +69,7 @@ const expectedIndexPatterns = {
2: {
id: '2',
title: 'my-fake-restricted-pattern',
hasRestrictions: true,
timeFieldName: 'timestamp',
fields: [
{
@ -322,6 +324,7 @@ describe('IndexPattern Data Source suggestions', () => {
1: {
id: '1',
title: 'no timefield',
hasRestrictions: false,
fields: [
{
name: 'bytes',
@ -532,6 +535,7 @@ describe('IndexPattern Data Source suggestions', () => {
1: {
id: '1',
title: 'no timefield',
hasRestrictions: false,
fields: [
{
name: 'bytes',
@ -1350,6 +1354,7 @@ describe('IndexPattern Data Source suggestions', () => {
1: {
id: '1',
title: 'my-fake-index-pattern',
hasRestrictions: false,
fields: [
{
name: 'field1',
@ -1493,6 +1498,7 @@ describe('IndexPattern Data Source suggestions', () => {
1: {
id: '1',
title: 'my-fake-index-pattern',
hasRestrictions: false,
fields: [
{
name: 'field1',
@ -1555,6 +1561,7 @@ describe('IndexPattern Data Source suggestions', () => {
1: {
id: '1',
title: 'my-fake-index-pattern',
hasRestrictions: false,
fields: [
{
name: 'field1',

View file

@ -62,6 +62,7 @@ const initialState: IndexPatternPrivateState = {
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -103,6 +104,7 @@ const initialState: IndexPatternPrivateState = {
'2': {
id: '2',
title: 'my-fake-restricted-pattern',
hasRestrictions: true,
timeFieldName: 'timestamp',
fields: [
{
@ -160,6 +162,7 @@ const initialState: IndexPatternPrivateState = {
id: '3',
title: 'my-compatible-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',

View file

@ -40,6 +40,7 @@ const indexPattern1 = ({
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -105,6 +106,7 @@ const indexPattern2 = ({
id: '2',
title: 'my-fake-restricted-pattern',
timeFieldName: 'timestamp',
hasRestrictions: true,
fields: [
{
name: 'timestamp',
@ -733,9 +735,9 @@ describe('loader', () => {
dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' },
fetchJson,
indexPatterns: [
{ id: '1', title: '1', fields: [] },
{ id: '2', title: '1', fields: [] },
{ id: '3', title: '1', fields: [] },
{ id: '1', title: '1', fields: [], hasRestrictions: false },
{ id: '2', title: '1', fields: [], hasRestrictions: false },
{ id: '3', title: '1', fields: [], hasRestrictions: false },
],
setState,
dslQuery,
@ -783,9 +785,9 @@ describe('loader', () => {
dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' },
fetchJson,
indexPatterns: [
{ id: '1', title: '1', fields: [] },
{ id: '2', title: '1', fields: [] },
{ id: 'c', title: '1', fields: [] },
{ id: '1', title: '1', fields: [], hasRestrictions: false },
{ id: '2', title: '1', fields: [], hasRestrictions: false },
{ id: 'c', title: '1', fields: [], hasRestrictions: false },
],
setState,
dslQuery,
@ -817,6 +819,7 @@ describe('loader', () => {
{
id: '1',
title: '1',
hasRestrictions: false,
fields: [{ name: 'field1' }, { name: 'field2' }] as IndexPatternField[],
},
],

View file

@ -91,6 +91,7 @@ export async function loadIndexPatterns({
timeFieldName,
fieldFormatMap,
fields: newFields,
hasRestrictions: !!typeMeta?.aggs,
};
return {
@ -334,6 +335,7 @@ export async function syncExistingFields({
title: string;
fields: IndexPatternField[];
timeFieldName?: string | null;
hasRestrictions: boolean;
}>;
fetchJson: HttpSetup['post'];
setState: SetState;
@ -343,6 +345,12 @@ export async function syncExistingFields({
showNoDataPopover: () => void;
}) {
const existenceRequests = indexPatterns.map((pattern) => {
if (pattern.hasRestrictions) {
return {
indexPatternTitle: pattern.title,
existingFieldNames: pattern.fields.map((field) => field.name),
};
}
const body: Record<string, string | object> = {
dslQuery,
fromDate: dateRange.fromDate,

View file

@ -11,6 +11,7 @@ export const createMockedIndexPattern = (): IndexPattern => ({
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -70,6 +71,7 @@ export const createMockedRestrictedIndexPattern = () => ({
id: '2',
title: 'my-fake-restricted-pattern',
timeFieldName: 'timestamp',
hasRestrictions: true,
fields: [
{
name: 'timestamp',

View file

@ -55,6 +55,7 @@ describe('date_histogram', () => {
id: '1',
title: 'Mock Indexpattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
@ -69,6 +70,7 @@ describe('date_histogram', () => {
2: {
id: '2',
title: 'Mock Indexpattern 2',
hasRestrictions: false,
fields: [
{
name: 'other_timestamp',
@ -229,13 +231,50 @@ describe('date_histogram', () => {
it('should reflect params correctly', () => {
const esAggsConfig = dateHistogramOperation.toEsAggsConfig(
state.layers.first.columns.col1 as DateHistogramIndexPatternColumn,
'col1'
'col1',
state.indexPatterns['1']
);
expect(esAggsConfig).toEqual(
expect.objectContaining({
params: expect.objectContaining({
interval: '42w',
field: 'timestamp',
useNormalizedEsInterval: true,
}),
})
);
});
it('should not use normalized es interval for rollups', () => {
const esAggsConfig = dateHistogramOperation.toEsAggsConfig(
state.layers.first.columns.col1 as DateHistogramIndexPatternColumn,
'col1',
{
...state.indexPatterns['1'],
fields: [
{
name: 'timestamp',
displayName: 'timestamp',
aggregatable: true,
searchable: true,
type: 'date',
aggregationRestrictions: {
date_histogram: {
agg: 'date_histogram',
time_zone: 'UTC',
calendar_interval: '42w',
},
},
},
],
}
);
expect(esAggsConfig).toEqual(
expect.objectContaining({
params: expect.objectContaining({
interval: '42w',
field: 'timestamp',
useNormalizedEsInterval: false,
}),
})
);
@ -300,6 +339,7 @@ describe('date_histogram', () => {
{
title: '',
id: '',
hasRestrictions: true,
fields: [
{
name: 'dateField',
@ -343,6 +383,7 @@ describe('date_histogram', () => {
{
title: '',
id: '',
hasRestrictions: false,
fields: [
{
name: 'dateField',

View file

@ -119,21 +119,24 @@ export const dateHistogramOperation: OperationDefinition<DateHistogramIndexPatte
sourceField: field.name,
};
},
toEsAggsConfig: (column, columnId) => ({
id: columnId,
enabled: true,
type: 'date_histogram',
schema: 'segment',
params: {
field: column.sourceField,
time_zone: column.params.timeZone,
useNormalizedEsInterval: true,
interval: column.params.interval,
drop_partials: false,
min_doc_count: 0,
extended_bounds: {},
},
}),
toEsAggsConfig: (column, columnId, indexPattern) => {
const usedField = indexPattern.fields.find((field) => field.name === column.sourceField);
return {
id: columnId,
enabled: true,
type: 'date_histogram',
schema: 'segment',
params: {
field: column.sourceField,
time_zone: column.params.timeZone,
useNormalizedEsInterval: !usedField || !usedField.aggregationRestrictions?.date_histogram,
interval: column.params.interval,
drop_partials: false,
min_doc_count: 0,
extended_bounds: {},
},
};
},
paramEditor: ({ state, setState, currentColumn: currentColumn, layerId, dateRange, data }) => {
const field =
currentColumn &&

View file

@ -84,7 +84,7 @@ interface BaseOperationDefinitionProps<C extends BaseIndexPatternColumn> {
* Function turning a column into an agg config passed to the `esaggs` function
* together with the agg configs returned from other columns.
*/
toEsAggsConfig: (column: C, columnId: string) => unknown;
toEsAggsConfig: (column: C, columnId: string, indexPattern: IndexPattern) => unknown;
/**
* Returns true if the `column` can also be used on `newIndexPattern`.
* If this function returns false, the column is removed when switching index pattern

View file

@ -68,7 +68,7 @@ function buildMetricOperation<T extends MetricColumn<string>>({
sourceField: field.name,
};
},
toEsAggsConfig: (column, columnId) => ({
toEsAggsConfig: (column, columnId, _indexPattern) => ({
id: columnId,
enabled: true,
type: column.operationType,

View file

@ -13,7 +13,7 @@ import { dataPluginMock } from '../../../../../../../src/plugins/data/public/moc
import { createMockedIndexPattern } from '../../mocks';
import { TermsIndexPatternColumn } from './terms';
import { termsOperation } from './index';
import { IndexPatternPrivateState } from '../../types';
import { IndexPatternPrivateState, IndexPattern } from '../../types';
const defaultProps = {
storage: {} as IStorageWrapper,
@ -69,7 +69,8 @@ describe('terms', () => {
it('should reflect params correctly', () => {
const esAggsConfig = termsOperation.toEsAggsConfig(
state.layers.first.columns.col1 as TermsIndexPatternColumn,
'col1'
'col1',
{} as IndexPattern
);
expect(esAggsConfig).toEqual(
expect.objectContaining({

View file

@ -95,7 +95,7 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn> = {
},
};
},
toEsAggsConfig: (column, columnId) => ({
toEsAggsConfig: (column, columnId, _indexPattern) => ({
id: columnId,
enabled: true,
type: 'terms',

View file

@ -16,6 +16,7 @@ const expectedIndexPatterns = {
id: '1',
title: 'my-fake-index-pattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',

View file

@ -570,6 +570,7 @@ describe('state_helpers', () => {
const indexPattern: IndexPattern = {
id: 'test',
title: '',
hasRestrictions: true,
fields: [
{
name: 'fieldA',

View file

@ -21,7 +21,11 @@ function getExpressionForLayer(
}
function getEsAggsConfig<C extends IndexPatternColumn>(column: C, columnId: string) {
return operationDefinitionMap[column.operationType].toEsAggsConfig(column, columnId);
return operationDefinitionMap[column.operationType].toEsAggsConfig(
column,
columnId,
indexPattern
);
}
const columnEntries = columnOrder.map((colId) => [colId, columns[colId]] as const);

View file

@ -19,6 +19,7 @@ export interface IndexPattern {
params: unknown;
}
>;
hasRestrictions: boolean;
}
export interface IndexPatternField {

View file

@ -31,6 +31,9 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./dashboard'));
loadTestFile(require.resolve('./persistent_context'));
loadTestFile(require.resolve('./lens_reporting'));
// has to be last one in the suite because it overrides saved objects
loadTestFile(require.resolve('./rollup'));
});
});
}

View file

@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['visualize', 'lens']);
const find = getService('find');
const listingTable = getService('listingTable');
const esArchiver = getService('esArchiver');
describe('lens rollup tests', () => {
before(async () => {
await esArchiver.loadIfNeeded('lens/rollup/data');
await esArchiver.loadIfNeeded('lens/rollup/config');
});
after(async () => {
await esArchiver.unload('lens/rollup/data');
await esArchiver.unload('lens/rollup/config');
});
it('should allow creation of lens xy chart', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
operation: 'date_histogram',
field: '@timestamp',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'sum',
field: 'bytes',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension',
operation: 'terms',
field: 'geo.src',
});
expect(await find.allByCssSelector('.echLegendItem')).to.have.length(2);
await PageObjects.lens.save('Afancilenstest');
// Ensure the visualization shows up in the visualize list, and takes
// us back to the visualization as we configured it.
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Afancilenstest');
await PageObjects.lens.clickVisualizeListItemTitle('Afancilenstest');
await PageObjects.lens.goToTimeRange();
expect(await PageObjects.lens.getTitle()).to.eql('Afancilenstest');
// .echLegendItem__title is the only viable way of getting the xy chart's
// legend item(s), so we're using a class selector here.
expect(await find.allByCssSelector('.echLegendItem')).to.have.length(2);
});
it('should allow seamless transition to and from table view', async () => {
await PageObjects.lens.switchToVisualization('lnsMetric');
await PageObjects.lens.assertMetric('Sum of bytes', '16,788');
await PageObjects.lens.switchToVisualization('lnsDatatable');
expect(await PageObjects.lens.getDatatableHeaderText()).to.eql('Sum of bytes');
expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('16,788');
});
});
}

View file

@ -0,0 +1,65 @@
{
"type": "doc",
"value": {
"id": "space:default",
"index": ".kibana_1",
"source": {
"migrationVersion": {
"space": "6.6.0"
},
"references": [],
"space": {
"_reserved": true,
"description": "This is the default space!",
"disabledFeatures": [],
"name": "Default"
},
"type": "space"
},
"type": "_doc"
}
}
{
"type": "doc",
"value": {
"id": "index-pattern:lens-rolled-up-data",
"index": ".kibana_1",
"source": {
"index-pattern" : {
"title" : "lens_rolled_up_data",
"timeFieldName" : "@timestamp",
"fields" : "[{\"count\":0,\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_score\",\"type\":\"number\",\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"geo.src\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]",
"type" : "rollup",
"typeMeta" : "{\"params\":{\"rollup_index\":\"lens_rolled_up_data\"},\"aggs\":{\"date_histogram\":{\"@timestamp\":{\"agg\":\"date_histogram\",\"fixed_interval\":\"60m\",\"time_zone\":\"UTC\"}},\"sum\":{\"bytes\":{\"agg\":\"sum\"}},\"max\":{\"bytes\":{\"agg\":\"max\"}},\"terms\":{\"geo.src\":{\"agg\":\"terms\"}}}}"
},
"type" : "index-pattern",
"references" : [ ],
"migrationVersion" : {
"index-pattern" : "7.6.0"
},
"updated_at" : "2020-08-19T08:39:09.998Z"
},
"type": "_doc"
}
}
{
"type": "doc",
"value": {
"id": "config:8.0.0",
"index": ".kibana_1",
"source": {
"config": {
"accessibility:disableAnimations": true,
"buildNum": 9007199254740991,
"dateFormat:tz": "UTC",
"defaultIndex": "logstash-*"
},
"references": [],
"type": "config",
"updated_at": "2019-09-04T18:47:24.761Z"
},
"type": "_doc"
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
{
"type": "doc",
"value": {
"index": "lens_rolled_up_data",
"id": "lens_rolled_up_data$vuSq1a9Ph2Nq-2yfGpE34g",
"source": {
"@timestamp.date_histogram.time_zone": "UTC",
"@timestamp.date_histogram.timestamp": 1442710800000,
"geo.src.terms.value": "CN",
"bytes.max.value": 5678.0,
"_rollup.version": 2,
"bytes.sum.value": 5678.0,
"@timestamp.date_histogram.interval": "60m",
"geo.src.terms._count": 1,
"@timestamp.date_histogram._count": 1,
"_rollup.id": "lens_rolled_up_data"
}
}
}
{
"type": "doc",
"value": {
"index": "lens_rolled_up_data",
"id": "lens_rolled_up_data$QFyUWoecErSYPMrIb6CgZA",
"source": {
"@timestamp.date_histogram.time_zone": "UTC",
"@timestamp.date_histogram.timestamp": 1442710800000,
"geo.src.terms.value": "US",
"bytes.max.value": 1234.0,
"_rollup.version": 2,
"bytes.sum.value": 1234.0,
"@timestamp.date_histogram.interval": "60m",
"geo.src.terms._count": 1,
"@timestamp.date_histogram._count": 1,
"_rollup.id": "lens_rolled_up_data"
}
}
}
{
"type": "doc",
"value": {
"index": "lens_rolled_up_data",
"id": "lens_rolled_up_data$cKCjv1OPjYiyv5WPPblohw",
"source": {
"@timestamp.date_histogram.time_zone": "UTC",
"@timestamp.date_histogram.timestamp": 1442714400000,
"geo.src.terms.value": "CN",
"bytes.max.value": 9876.0,
"_rollup.version": 2,
"bytes.sum.value": 9876.0,
"@timestamp.date_histogram.interval": "60m",
"geo.src.terms._count": 1,
"@timestamp.date_histogram._count": 1,
"_rollup.id": "lens_rolled_up_data"
}
}
}

View file

@ -0,0 +1,129 @@
{
"type": "index",
"value": {
"index": "lens_rolled_up_data",
"mappings": {
"_meta": {
"_rollup": {
"lens_rolled_up_data": {
"cron": "0 * * * * ?",
"rollup_index": "lens_rolled_up_data",
"groups": {
"date_histogram": {
"fixed_interval": "60m",
"field": "@timestamp",
"time_zone": "UTC"
},
"terms": {
"fields": ["geo.src", "ip"]
}
},
"id": "lens_rolled_up_data",
"metrics": [
{
"field": "bytes",
"metrics": ["sum", "max"]
}
],
"index_pattern": "lens_raw",
"timeout": "20s",
"page_size": 1000
}
},
"rollup-version": "8.0.0"
},
"dynamic_templates": [
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
},
{
"date_histograms": {
"path_match": "*.date_histogram.timestamp",
"mapping": {
"type": "date"
}
}
}
],
"properties": {
"@timestamp": {
"properties": {
"date_histogram": {
"properties": {
"_count": {
"type": "long"
},
"interval": {
"type": "keyword"
},
"time_zone": {
"type": "keyword"
},
"timestamp": {
"type": "date"
}
}
}
}
},
"_rollup": {
"properties": {
"id": {
"type": "keyword"
},
"version": {
"type": "long"
}
}
},
"bytes": {
"properties": {
"max": {
"properties": {
"value": {
"type": "float"
}
}
},
"sum": {
"properties": {
"value": {
"type": "float"
}
}
}
}
},
"geo": {
"properties": {
"src": {
"properties": {
"terms": {
"properties": {
"_count": {
"type": "long"
},
"value": {
"type": "keyword"
}
}
}
}
}
}
}
}
},
"settings": {
"index": {
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}
}