"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.combineQueryAndFilters = combineQueryAndFilters; exports.getLayerMetaInfo = getLayerMetaInfo; var _esQuery = require("@kbn/es-query"); var _i18n = require("@kbn/i18n"); var _lodash = require("lodash"); var _public = require("@kbn/expression-xy-plugin/public"); var _lens_ui_errors = require("../lens_ui_errors"); /* * 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. */ function getLayerType(visualization, state, layerId) { return visualization.getLayerType(layerId, state) || _public.LayerTypes.DATA; } /** * Joins a series of queries. * * Uses "AND" along dimension 1 and "OR" along dimension 2 */ function joinQueries(queries) { // leave a single query alone if (queries.length === 1 && queries[0].length === 1) { return queries[0][0].query; } const expression = queries.filter(subQueries => subQueries.length).map(subQueries => // reduce the amount of round brackets in case of one query subQueries.length > 1 ? `( ${subQueries.map(({ query: filterQuery }) => `( ${filterQuery} )`).join(' OR ')} )` : `( ${subQueries[0].query} )`).join(' AND '); return queries.length > 1 ? `( ${expression} )` : expression; } const sortByDateFieldsFirst = (datasourceAPI, fields, indexPatterns) => { const dataViewId = datasourceAPI.getSourceId(); if (!dataViewId) return; // for usability reasons we want to order the date fields first // the fields order responds to the columns order in Discover const dateFieldsFirst = fields.reduce((acc, fieldName) => { var _indexPatterns$dataVi; const field = (_indexPatterns$dataVi = indexPatterns[dataViewId]) === null || _indexPatterns$dataVi === void 0 ? void 0 : _indexPatterns$dataVi.getFieldByName(fieldName); if ((field === null || field === void 0 ? void 0 : field.type) === 'date') { return [fieldName, ...acc]; } return [...acc, fieldName]; }, []); return dateFieldsFirst; }; function getLayerMetaInfo(currentDatasource, datasourceState, activeVisualization, visualizationState, activeData, indexPatterns, timeRange, capabilities) { var _capabilities$navLink, _capabilities$discove; const isVisible = Boolean(((_capabilities$navLink = capabilities.navLinks) === null || _capabilities$navLink === void 0 ? void 0 : _capabilities$navLink.discover) && ((_capabilities$discove = capabilities.discover) === null || _capabilities$discove === void 0 ? void 0 : _capabilities$discove.show)); // If Multiple tables, return // If there are time shifts, return // If dataViews have not loaded yet, return const datatables = Object.values(activeData || {}); if (!datatables.length || !currentDatasource || !datasourceState || !activeVisualization || !Object.keys(indexPatterns).length) { return { meta: undefined, error: _i18n.i18n.translate('xpack.lens.app.showUnderlyingDataNoData', { defaultMessage: 'Visualization has no data available to show' }), isVisible }; } let datasourceAPI; try { const layerIds = currentDatasource.getLayers(datasourceState); const dataLayerIds = layerIds.filter(layerId => getLayerType(activeVisualization, visualizationState, layerId) === _public.LayerTypes.DATA); if (dataLayerIds.length > 1) { return { meta: undefined, error: _i18n.i18n.translate('xpack.lens.app.showUnderlyingDataMultipleLayers', { defaultMessage: 'Cannot show underlying data for visualizations with multiple layers' }), isVisible }; } datasourceAPI = currentDatasource.getPublicAPI({ layerId: dataLayerIds[0], state: datasourceState, indexPatterns }); } catch (error) { (0, _lens_ui_errors.showMemoizedErrorNotification)(error); return { meta: undefined, error: error.message, isVisible }; } const tableSpec = datasourceAPI.getTableSpec(); const columnsWithNoTimeShifts = tableSpec.filter(({ columnId }) => { var _datasourceAPI$getOpe; return !((_datasourceAPI$getOpe = datasourceAPI.getOperationForColumnId(columnId)) !== null && _datasourceAPI$getOpe !== void 0 && _datasourceAPI$getOpe.hasTimeShift); }); if (columnsWithNoTimeShifts.length < tableSpec.length) { return { meta: undefined, error: _i18n.i18n.translate('xpack.lens.app.showUnderlyingDataTimeShifts', { defaultMessage: "Cannot show underlying data when there's a time shift configured" }), isVisible }; } const filtersOrError = datasourceAPI.getFilters(activeData, timeRange); if ('error' in filtersOrError) { return { meta: undefined, error: filtersOrError.error, isVisible }; } const uniqueFields = [...new Set(columnsWithNoTimeShifts.map(({ fields }) => fields).flat())]; const dateFieldsFirst = sortByDateFieldsFirst(datasourceAPI, uniqueFields, indexPatterns); return { meta: { id: datasourceAPI.getSourceId(), columns: dateFieldsFirst !== null && dateFieldsFirst !== void 0 ? dateFieldsFirst : uniqueFields, filters: filtersOrError }, error: undefined, isVisible }; } // This enforces on assignment time that the two props are not the same /** * Translates an arbitrarily-large set of @type {Query}s (including those supplied in @type {LayerMetaInfo}) * and existing Kibana @type {Filter}s into a single query and a new set of @type {Filter}s. This allows them to * function as an equivalent context in Discover. * * If some of the queries are in KQL and some in Lucene, all the queries in one language will be merged into * a large query to be shown in the query bar, while the queries in the other language will be encoded as an * extra filter pill. */ function combineQueryAndFilters(query, filters, meta, dataViews, esQueryConfig) { var _nonEmptyQueries$, _meta$filters$enabled; const queries = { kuery: [], lucene: [] }; const allQueries = Array.isArray(query) ? query : query && (0, _esQuery.isOfQueryType)(query) ? [query] : []; const nonEmptyQueries = allQueries.filter(q => Boolean(typeof q.query === 'string' ? q.query.trim() : q.query)); [queries.lucene, queries.kuery] = (0, _lodash.partition)(nonEmptyQueries, q => q.language === 'lucene'); const queryLanguage = ((_nonEmptyQueries$ = nonEmptyQueries[0]) === null || _nonEmptyQueries$ === void 0 ? void 0 : _nonEmptyQueries$.language) || 'kuery'; const newQuery = { language: queryLanguage, query: joinQueries([...queries[queryLanguage].map(q => [q]), ...(meta.filters.enabled[queryLanguage] || [])]) }; const filtersLanguage = queryLanguage === 'lucene' ? 'kuery' : 'lucene'; // make a copy as the original filters are readonly const newFilters = [...filters]; const dataView = dataViews === null || dataViews === void 0 ? void 0 : dataViews.find(({ id }) => id === meta.id); const hasQueriesInFiltersLanguage = Boolean(((_meta$filters$enabled = meta.filters.enabled[filtersLanguage]) === null || _meta$filters$enabled === void 0 ? void 0 : _meta$filters$enabled.length) || queries[filtersLanguage].length); if (hasQueriesInFiltersLanguage) { const queryExpression = joinQueries([...queries[filtersLanguage].map(q => [q]), ...(meta.filters.enabled[filtersLanguage] || [])]); // Create new filter to encode the rest of the query information newFilters.push((0, _esQuery.buildCustomFilter)(meta.id, (0, _esQuery.buildEsQuery)(dataView, { language: filtersLanguage, query: queryExpression }, [], esQueryConfig), false, false, _i18n.i18n.translate('xpack.lens.app.lensContext', { defaultMessage: 'Lens context ({language})', values: { language: filtersLanguage } }), _esQuery.FilterStateStore.APP_STATE)); } // for each disabled filter create a new custom filter and disable it // note that both languages go into the filter bar for (const language of ['lucene', 'kuery']) { const [disabledQueries] = meta.filters.disabled[language] || []; for (const disabledQuery of disabledQueries || []) { let label = disabledQuery.query; if (language === 'lucene') { label += ` (${language})`; } newFilters.push((0, _esQuery.buildCustomFilter)(meta.id, (0, _esQuery.buildEsQuery)(dataView, disabledQuery, [], esQueryConfig), true, false, label, _esQuery.FilterStateStore.APP_STATE)); } } return { filters: newFilters, query: newQuery }; }