[Maps] do not track total hits for elasticsearch search requests (#91754)

* [Maps] do not track total hits for elasticsearch search requests

* set track_total_hits for es_search_source tooltip fetch

* tslint

* searchSource doc updates, set track_total_hits in MVT requests

* revert changes made to searchsourcefields docs

* tslint

* review feedback

* tslint

* remove Hits (Total) from functional tests

* remove sleep in functional test

* tslint

* fix method name

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2021-03-25 13:00:16 -06:00 committed by GitHub
parent 6b6404954e
commit c5e3e78de8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 198 additions and 137 deletions

View file

@ -8,3 +8,4 @@
export * from './es_agg_utils';
export * from './convert_to_geojson';
export * from './elasticsearch_geo_utils';
export { isTotalHitsGreaterThan, TotalHits } from './total_hits';

View file

@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { isTotalHitsGreaterThan, TotalHits } from './total_hits';
describe('total.relation: eq', () => {
const totalHits = {
value: 100,
relation: 'eq' as TotalHits['relation'],
};
test('total.value: 100 should be more than 90', () => {
expect(isTotalHitsGreaterThan(totalHits, 90)).toBe(true);
});
test('total.value: 100 should not be more than 100', () => {
expect(isTotalHitsGreaterThan(totalHits, 100)).toBe(false);
});
test('total.value: 100 should not be more than 110', () => {
expect(isTotalHitsGreaterThan(totalHits, 110)).toBe(false);
});
});
describe('total.relation: gte', () => {
const totalHits = {
value: 100,
relation: 'gte' as TotalHits['relation'],
};
test('total.value: 100 should be more than 90', () => {
expect(isTotalHitsGreaterThan(totalHits, 90)).toBe(true);
});
test('total.value: 100 should be more than 100', () => {
expect(isTotalHitsGreaterThan(totalHits, 100)).toBe(true);
});
test('total.value: 100 should throw error when value is more than 100', () => {
expect(() => {
isTotalHitsGreaterThan(totalHits, 110);
}).toThrow();
});
});

View file

@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
export interface TotalHits {
value: number;
relation: 'eq' | 'gte';
}
export function isTotalHitsGreaterThan(totalHits: TotalHits, value: number) {
if (totalHits.relation === 'eq') {
return totalHits.value > value;
}
if (value > totalHits.value) {
throw new Error(
i18n.translate('xpack.maps.totalHits.lowerBoundPrecisionExceeded', {
defaultMessage: `Unable to determine if total hits is greater than value. Total hits precision is lower then value. Total hits: {totalHitsString}, value: {value}. Ensure _search.body.track_total_hits is at least as large as value.`,
values: {
totalHitsString: JSON.stringify(totalHits, null, ''),
value,
},
})
);
}
return true;
}

View file

@ -22,6 +22,7 @@ import {
LAYER_STYLE_TYPE,
FIELD_ORIGIN,
} from '../../../../common/constants';
import { isTotalHitsGreaterThan, TotalHits } from '../../../../common/elasticsearch_util';
import { ESGeoGridSource } from '../../sources/es_geo_grid_source/es_geo_grid_source';
import { canSkipSourceUpdate } from '../../util/can_skip_fetch';
import { IESSource } from '../../sources/es_source';
@ -323,13 +324,18 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
syncContext.startLoading(dataRequestId, requestToken, searchFilters);
const abortController = new AbortController();
syncContext.registerCancelCallback(requestToken, () => abortController.abort());
const maxResultWindow = await this._documentSource.getMaxResultWindow();
const searchSource = await this._documentSource.makeSearchSource(searchFilters, 0);
searchSource.setField('trackTotalHits', maxResultWindow + 1);
const resp = await searchSource.fetch({
abortSignal: abortController.signal,
sessionId: syncContext.dataFilters.searchSessionId,
legacyHitsTotal: false,
});
const maxResultWindow = await this._documentSource.getMaxResultWindow();
isSyncClustered = resp.hits.total > maxResultWindow;
isSyncClustered = isTotalHitsGreaterThan(
(resp.hits.total as unknown) as TotalHits,
maxResultWindow
);
const countData = { isSyncClustered } as CountData;
syncContext.stopLoading(dataRequestId, requestToken, countData, searchFilters);
} catch (error) {

View file

@ -368,6 +368,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle
): Promise<GeoJsonWithMeta> {
const indexPattern: IndexPattern = await this.getIndexPattern();
const searchSource: ISearchSource = await this.makeSearchSource(searchFilters, 0);
searchSource.setField('trackTotalHits', false);
let bucketsPerGrid = 1;
this.getMetricFields().forEach((metricField) => {

View file

@ -190,6 +190,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
// Fetch entities
//
const entitySearchSource = await this.makeSearchSource(searchFilters, 0);
entitySearchSource.setField('trackTotalHits', false);
const splitField = getField(indexPattern, this._descriptor.splitField);
const cardinalityAgg = { precision_threshold: 1 };
const termsAgg = { size: MAX_TRACKS };
@ -250,6 +251,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
const tracksSearchFilters = { ...searchFilters };
delete tracksSearchFilters.buffer;
const tracksSearchSource = await this.makeSearchSource(tracksSearchFilters, 0);
tracksSearchSource.setField('trackTotalHits', false);
tracksSearchSource.setField('aggs', {
tracks: {
filters: {

View file

@ -109,6 +109,7 @@ export class ESPewPewSource extends AbstractESAggSource {
async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) {
const indexPattern = await this.getIndexPattern();
const searchSource = await this.makeSearchSource(searchFilters, 0);
searchSource.setField('trackTotalHits', false);
searchSource.setField('aggs', {
destSplit: {
terms: {
@ -168,6 +169,7 @@ export class ESPewPewSource extends AbstractESAggSource {
async getBoundsForFilters(boundsFilters, registerCancelCallback) {
const searchSource = await this.makeSearchSource(boundsFilters, 0);
searchSource.setField('trackTotalHits', false);
searchSource.setField('aggs', {
destFitToBounds: {
geo_bounds: {
@ -185,7 +187,10 @@ export class ESPewPewSource extends AbstractESAggSource {
try {
const abortController = new AbortController();
registerCancelCallback(() => abortController.abort());
const esResp = await searchSource.fetch({ abortSignal: abortController.signal });
const esResp = await searchSource.fetch({
abortSignal: abortController.signal,
legacyHitsTotal: false,
});
if (esResp.aggregations.destFitToBounds.bounds) {
corners.push([
esResp.aggregations.destFitToBounds.bounds.top_left.lon,

View file

@ -18,6 +18,7 @@ import {
addFieldToDSL,
getField,
hitsToGeoJson,
isTotalHitsGreaterThan,
PreIndexedShape,
} from '../../../../common/elasticsearch_util';
// @ts-expect-error
@ -313,6 +314,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
};
const searchSource = await this.makeSearchSource(searchFilters, 0);
searchSource.setField('trackTotalHits', false);
searchSource.setField('aggs', {
totalEntities: {
cardinality: addFieldToDSL(cardinalityAgg, topHitsSplitField),
@ -343,11 +345,10 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
const areEntitiesTrimmed = entityBuckets.length >= DEFAULT_MAX_BUCKETS_LIMIT;
let areTopHitsTrimmed = false;
entityBuckets.forEach((entityBucket: any) => {
const total = _.get(entityBucket, 'entityHits.hits.total', 0);
const hits = _.get(entityBucket, 'entityHits.hits.hits', []);
// Reverse hits list so top documents by sort are drawn on top
allHits.push(...hits.reverse());
if (total > hits.length) {
if (isTotalHitsGreaterThan(entityBucket.entityHits.hits.total, hits.length)) {
areTopHitsTrimmed = true;
}
});
@ -385,6 +386,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
maxResultWindow,
initialSearchContext
);
searchSource.setField('trackTotalHits', maxResultWindow + 1);
searchSource.setField('fieldsFromSource', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
if (sourceOnlyFields.length === 0) {
searchSource.setField('source', false); // do not need anything from _source
@ -408,7 +410,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
hits: resp.hits.hits.reverse(), // Reverse hits so top documents by sort are drawn on top
meta: {
resultsCount: resp.hits.hits.length,
areResultsTrimmed: resp.hits.total > resp.hits.hits.length,
areResultsTrimmed: isTotalHitsGreaterThan(resp.hits.total, resp.hits.hits.length),
},
};
}
@ -508,6 +510,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
const initialSearchContext = { docvalue_fields: docValueFields }; // Request fields in docvalue_fields insted of _source
const searchService = getSearchService();
const searchSource = await searchService.searchSource.create(initialSearchContext as object);
searchSource.setField('trackTotalHits', false);
searchSource.setField('index', indexPattern);
searchSource.setField('size', 1);
@ -520,7 +523,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
searchSource.setField('query', query);
searchSource.setField('fieldsFromSource', this._getTooltipPropertyNames());
const resp = await searchSource.fetch();
const resp = await searchSource.fetch({ legacyHitsTotal: false });
const hit = _.get(resp, 'hits.hits[0]');
if (!hit) {

View file

@ -195,6 +195,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
resp = await searchSource.fetch({
abortSignal: abortController.signal,
sessionId: searchSessionId,
legacyHitsTotal: false,
});
if (inspectorRequest) {
const responseStats = search.getResponseInspectorStats(resp, searchSource);
@ -247,6 +248,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
}
}
const searchService = getSearchService();
const searchSource = await searchService.searchSource.create(initialSearchContext);
searchSource.setField('index', indexPattern);
@ -272,6 +274,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
const searchSource = await this.makeSearchSource(boundsFilters, 0);
searchSource.setField('trackTotalHits', false);
searchSource.setField('aggs', {
fitToBounds: {
geo_bounds: {
@ -284,7 +287,10 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
try {
const abortController = new AbortController();
registerCancelCallback(() => abortController.abort());
const esResp = await searchSource.fetch({ abortSignal: abortController.signal });
const esResp = await searchSource.fetch({
abortSignal: abortController.signal,
legacyHitsTotal: false,
});
if (!esResp.aggregations) {
return null;

View file

@ -127,6 +127,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource
const indexPattern = await this.getIndexPattern();
const searchSource: ISearchSource = await this.makeSearchSource(searchFilters, 0);
searchSource.setField('trackTotalHits', false);
const termsField = getField(indexPattern, this._termField.getName());
const termsAgg = {
size: this._descriptor.size !== undefined ? this._descriptor.size : DEFAULT_MAX_BUCKETS_LIMIT,

View file

@ -23,7 +23,12 @@ import {
SUPER_FINE_ZOOM_DELTA,
} from '../../common/constants';
import { convertRegularRespToGeoJson, hitsToGeoJson } from '../../common/elasticsearch_util';
import {
convertRegularRespToGeoJson,
hitsToGeoJson,
isTotalHitsGreaterThan,
TotalHits,
} from '../../common/elasticsearch_util';
import { flattenHit } from './util';
import { ESBounds, tileToESBbox } from '../../common/geo_tile_utils';
import { getCentroidFeatures } from '../../common/get_centroid_features';
@ -67,6 +72,7 @@ export async function getGridTile({
MAX_ZOOM
);
requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.bounds = tileBounds;
requestBody.track_total_hits = false;
const response = await context
.search!.search(
@ -78,6 +84,7 @@ export async function getGridTile({
},
{
sessionId: searchSessionId,
legacyHitsTotal: false,
abortSignal,
}
)
@ -130,6 +137,7 @@ export async function getTile({
const searchOptions = {
sessionId: searchSessionId,
legacyHitsTotal: false,
abortSignal,
};
@ -141,6 +149,7 @@ export async function getTile({
body: {
size: 0,
query: requestBody.query,
track_total_hits: requestBody.size + 1,
},
},
},
@ -148,7 +157,12 @@ export async function getTile({
)
.toPromise();
if (countResponse.rawResponse.hits.total > requestBody.size) {
if (
isTotalHitsGreaterThan(
(countResponse.rawResponse.hits.total as unknown) as TotalHits,
requestBody.size
)
) {
// Generate "too many features"-bounds
const bboxResponse = await context
.search!.search(
@ -165,6 +179,7 @@ export async function getTile({
},
},
},
track_total_hits: false,
},
},
},
@ -191,7 +206,10 @@ export async function getTile({
{
params: {
index,
body: requestBody,
body: {
...requestBody,
track_total_hits: false,
},
},
},
searchOptions

View file

@ -27,28 +27,21 @@ export default function ({ getPageObjects, getService }) {
});
it('should request documents when zoomed to smaller regions showing less data', async () => {
const hits = await PageObjects.maps.getHits();
const response = await PageObjects.maps.getResponse();
// Allow a range of hits to account for variances in browser window size.
expect(parseInt(hits)).to.be.within(30, 40);
expect(response.hits.hits.length).to.be.within(30, 40);
});
it('should request clusters when zoomed to larger regions showing lots of data', async () => {
await PageObjects.maps.setView(20, -90, 2);
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
await inspector.close();
expect(hits).to.equal('0');
expect(totalHits).to.equal('14000');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(17);
});
it('should request documents when query narrows data', async () => {
await PageObjects.maps.setAndSubmitQuery('bytes > 19000');
const hits = await PageObjects.maps.getHits();
expect(hits).to.equal('75');
const response = await PageObjects.maps.getResponse();
expect(response.hits.hits.length).to.equal(75);
});
});
}

View file

@ -9,9 +9,6 @@ import expect from '@kbn/expect';
export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['maps']);
const inspector = getService('inspector');
const monacoEditor = getService('monacoEditor');
const testSubjects = getService('testSubjects');
const security = getService('security');
describe('docvalue_fields', () => {
@ -24,18 +21,9 @@ export default function ({ getPageObjects, getService }) {
await security.testUser.restoreDefaults();
});
async function getResponse() {
await inspector.open();
await inspector.openInspectorRequestsView();
await testSubjects.click('inspectorRequestDetailResponse');
const responseBody = await monacoEditor.getCodeEditorValue();
await inspector.close();
return JSON.parse(responseBody);
}
it('should only fetch geo_point field and nothing else when source does not have data driven styling', async () => {
await PageObjects.maps.loadSavedMap('document example');
const response = await getResponse();
const response = await PageObjects.maps.getResponse();
const firstHit = response.hits.hits[0];
expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']);
expect(firstHit.fields).to.only.have.keys(['geo.coordinates']);
@ -43,7 +31,7 @@ export default function ({ getPageObjects, getService }) {
it('should only fetch geo_point field and data driven styling fields', async () => {
await PageObjects.maps.loadSavedMap('document example with data driven styles');
const response = await getResponse();
const response = await PageObjects.maps.getResponse();
const firstHit = response.hits.hits[0];
expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']);
expect(firstHit.fields).to.only.have.keys(['bytes', 'geo.coordinates', 'hour_of_day']);
@ -51,7 +39,7 @@ export default function ({ getPageObjects, getService }) {
it('should format date fields as epoch_millis when data driven styling is applied to a date field', async () => {
await PageObjects.maps.loadSavedMap('document example with data driven styles on date field');
const response = await getResponse();
const response = await PageObjects.maps.getResponse();
const firstHit = response.hits.hits[0];
expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']);
expect(firstHit.fields).to.only.have.keys(['@timestamp', 'bytes', 'geo.coordinates']);

View file

@ -14,7 +14,6 @@ export default function ({ getPageObjects, getService }) {
const filterBar = getService('filterBar');
const dashboardPanelActions = getService('dashboardPanelActions');
const inspector = getService('inspector');
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const retry = getService('retry');
const security = getService('security');
@ -81,11 +80,10 @@ export default function ({ getPageObjects, getService }) {
});
it('should apply container state (time, query, filters) to embeddable when loaded', async () => {
await dashboardPanelActions.openInspectorByTitle('geo grid vector grid example');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
await inspector.close();
expect(totalHits).to.equal('6');
const response = await PageObjects.maps.getResponseFromDashboardPanel(
'geo grid vector grid example'
);
expect(response.aggregations.gridSplit.buckets.length).to.equal(6);
});
it('should apply new container state (time, query, filters) to embeddable', async () => {
@ -94,25 +92,16 @@ export default function ({ getPageObjects, getService }) {
await filterBar.selectIndexPattern('meta_for_geo_shapes*');
await filterBar.addFilter('shape_name', 'is', 'alpha');
await dashboardPanelActions.openInspectorByTitle('geo grid vector grid example');
const geoGridRequestStats = await inspector.getTableData();
const geoGridTotalHits = PageObjects.maps.getInspectorStatRowHit(
geoGridRequestStats,
'Hits (total)'
const gridResponse = await PageObjects.maps.getResponseFromDashboardPanel(
'geo grid vector grid example'
);
await inspector.close();
expect(geoGridTotalHits).to.equal('1');
expect(gridResponse.aggregations.gridSplit.buckets.length).to.equal(1);
await dashboardPanelActions.openInspectorByTitle('join example');
await testSubjects.click('inspectorRequestChooser');
await testSubjects.click('inspectorRequestChoosermeta_for_geo_shapes*.shape_name');
const joinRequestStats = await inspector.getTableData();
const joinTotalHits = PageObjects.maps.getInspectorStatRowHit(
joinRequestStats,
'Hits (total)'
const joinResponse = await PageObjects.maps.getResponseFromDashboardPanel(
'join example',
'meta_for_geo_shapes*.shape_name'
);
await inspector.close();
expect(joinTotalHits).to.equal('3');
expect(joinResponse.aggregations.join.buckets.length).to.equal(1);
});
it('should re-fetch query when "refresh" is clicked', async () => {

View file

@ -141,12 +141,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should apply query to geotile_grid aggregation request', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
await inspector.close();
expect(hits).to.equal('1');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(1);
});
});
@ -156,18 +152,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should contain geotile_grid aggregation elasticsearch request', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('6');
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(
requestStats,
'Index pattern'
);
expect(indexPatternName).to.equal('logstash-*');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(4);
});
it('should not contain any elasticsearch request after layer is deleted', async () => {
@ -218,12 +204,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should apply query to geotile_grid aggregation request', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
await inspector.close();
expect(hits).to.equal('1');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(1);
});
});
@ -233,18 +215,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should contain geotile_grid aggregation elasticsearch request', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('6');
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(
requestStats,
'Index pattern'
);
expect(indexPatternName).to.equal('logstash-*');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(4);
});
it('should not contain any elasticsearch request after layer is deleted', async () => {
@ -272,18 +244,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should contain geotile_grid aggregation elasticsearch request', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('4'); //4 geometries result in 13 cells due to way they overlap geotile_grid cells
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(
requestStats,
'Index pattern'
);
expect(indexPatternName).to.equal('geo_shapes*');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.gridSplit.buckets.length).to.equal(13);
});
});
});

View file

@ -9,7 +9,6 @@ import expect from '@kbn/expect';
export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['maps']);
const inspector = getService('inspector');
const security = getService('security');
const VECTOR_SOURCE_ID = '67c1de2c-2fc5-4425-8983-094b589afe61';
@ -25,15 +24,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should request source clusters for destination locations', async () => {
await inspector.open();
await inspector.openInspectorRequestsView();
const requestStats = await inspector.getTableData();
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
await inspector.close();
expect(hits).to.equal('0');
expect(totalHits).to.equal('4');
const response = await PageObjects.maps.getResponse();
expect(response.aggregations.destSplit.buckets.length).to.equal(2);
});
it('should render lines', async () => {

View file

@ -121,17 +121,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should not apply query to source and apply query to join', async () => {
await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('3');
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(
requestStats,
'Index pattern'
);
expect(indexPatternName).to.equal('meta_for_geo_shapes*');
const joinResponse = await PageObjects.maps.getResponse('meta_for_geo_shapes*.shape_name');
expect(joinResponse.aggregations.join.buckets.length).to.equal(2);
});
});
@ -145,13 +136,8 @@ export default function ({ getPageObjects, getService }) {
});
it('should apply query to join request', async () => {
await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('2');
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
await inspector.close();
const joinResponse = await PageObjects.maps.getResponse('meta_for_geo_shapes*.shape_name');
expect(joinResponse.aggregations.join.buckets.length).to.equal(1);
});
it('should update dynamic data range in legend with new results', async () => {

View file

@ -23,6 +23,8 @@ export function GisPageProvider({ getService, getPageObjects }: FtrProviderConte
const browser = getService('browser');
const MenuToggle = getService('MenuToggle');
const listingTable = getService('listingTable');
const monacoEditor = getService('monacoEditor');
const dashboardPanelActions = getService('dashboardPanelActions');
const setViewPopoverToggle = new MenuToggle({
name: 'SetView Popover',
@ -614,6 +616,31 @@ export function GisPageProvider({ getService, getPageObjects }: FtrProviderConte
return mapboxStyle;
}
async getResponse(requestName: string) {
await inspector.open();
const response = await this._getResponse(requestName);
await inspector.close();
return response;
}
async _getResponse(requestName: string) {
if (requestName) {
await testSubjects.click('inspectorRequestChooser');
await testSubjects.click(`inspectorRequestChooser${requestName}`);
}
await inspector.openInspectorRequestsView();
await testSubjects.click('inspectorRequestDetailResponse');
const responseBody = await monacoEditor.getCodeEditorValue();
return JSON.parse(responseBody);
}
async getResponseFromDashboardPanel(panelTitle: string, requestName: string) {
await dashboardPanelActions.openInspectorByTitle(panelTitle);
const response = await this._getResponse(requestName);
await inspector.close();
return response;
}
getInspectorStatRowHit(stats: string[][], rowName: string) {
const STATS_ROW_NAME_INDEX = 0;
const STATS_ROW_VALUE_INDEX = 1;