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

Allow creating filters from fields with null values in discover #70936

Merged
merged 11 commits into from Jul 9, 2020
Expand Up @@ -78,7 +78,20 @@ function decorateFlattenedWrapper(hit: Record<string, any>, metaFields: Record<s
// unwrap computed fields
_.forOwn(hit.fields, function (val, key: any) {
if (key[0] === '_' && !_.includes(metaFields, key)) return;
flattened[key] = Array.isArray(val) && val.length === 1 ? val[0] : val;
if (Array.isArray(val)) {
lizozom marked this conversation as resolved.
Show resolved Hide resolved
switch (val.length) {
case 0:
flattened[key] = undefined;
break;
case 1:
flattened[key] = val[0];
break;
default:
flattened[key] = val;
}
} else {
flattened[key] = val;
}
});

return flattened;
Expand Down
Expand Up @@ -92,7 +92,7 @@ export function generateFilters(
const newFilters: Filter[] = [];
const appFilters = filterManager.getAppFilters();

const negate = operation === '-';
let negate = operation === '-';
let filter;

_.each(values, function (value) {
Expand All @@ -104,8 +104,15 @@ export function generateFilters(
} else {
const tmpIndexPattern = { id: index } as IIndexPattern;
// exists filter special case: fieldname = '_exists' and value = fieldname
const filterType = fieldName === '_exists_' ? FILTERS.EXISTS : FILTERS.PHRASE;
let filterType = fieldName === '_exists_' ? FILTERS.EXISTS : FILTERS.PHRASE;
const actualFieldObj = fieldName === '_exists_' ? ({ name: value } as IFieldType) : fieldObj;

// Fix for #7189 - if value is emtpy, phrase filters become exists filters.
lizozom marked this conversation as resolved.
Show resolved Hide resolved
if (value === null || value === undefined) {
filterType = FILTERS.EXISTS;
negate = !negate;
}

filter = buildFilter(
tmpIndexPattern,
actualFieldObj,
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/data/public/ui/filter_bar/filter_item.tsx
Expand Up @@ -135,9 +135,10 @@ export function FilterItem(props: Props) {
const dataTestSubjValue = filter.meta.value
? `filter-value-${isValidLabel(labelConfig) ? labelConfig.title : labelConfig.status}`
: '';
const dataTestSubjNegated = filter.meta.negate ? 'filter-negated' : '';
const dataTestSubjDisabled = `filter-${isDisabled(labelConfig) ? 'disabled' : 'enabled'}`;
const dataTestSubjPinned = `filter-${isFilterPinned(filter) ? 'pinned' : 'unpinned'}`;
return `filter ${dataTestSubjDisabled} ${dataTestSubjKey} ${dataTestSubjValue} ${dataTestSubjPinned}`;
return `filter ${dataTestSubjDisabled} ${dataTestSubjKey} ${dataTestSubjValue} ${dataTestSubjPinned} ${dataTestSubjNegated}`;
}

function getPanels() {
Expand Down
24 changes: 23 additions & 1 deletion test/functional/apps/discover/_doc_navigation.js
Expand Up @@ -21,8 +21,9 @@ import expect from '@kbn/expect';

export default function ({ getService, getPageObjects }) {
const docTable = getService('docTable');
const filterBar = getService('filterBar');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'context']);
const esArchiver = getService('esArchiver');
const retry = getService('retry');

Expand Down Expand Up @@ -50,5 +51,26 @@ export default function ({ getService, getPageObjects }) {
const hasDocHit = await testSubjects.exists('doc-hit');
expect(hasDocHit).to.be(true);
});

it('add filter should create an exists filter if value is null (#7189)', async function () {
// Filter special document
await filterBar.addFilter('agent', 'is', 'Missing/Fields');
await PageObjects.discover.waitUntilSearchingHasFinished();

// navigate to the doc view
await docTable.clickRowToggle({ rowIndex: 0 });

const details = await docTable.getDetailsRow();
await docTable.addInclusiveFilter(details, 'referer');
await PageObjects.discover.waitUntilSearchingHasFinished();

const hasInclusiveFilter = await filterBar.hasFilter('referer', 'exists', true, false, true);
expect(hasInclusiveFilter).to.be(true);

await docTable.removeInclusiveFilter(details, 'referer');
await PageObjects.discover.waitUntilSearchingHasFinished();
const hasExcludeFilter = await filterBar.hasFilter('referer', 'exists', true, false, false);
expect(hasExcludeFilter).to.be(true);
});
});
}
Binary file not shown.
21 changes: 21 additions & 0 deletions test/functional/services/doc_table.ts
Expand Up @@ -58,6 +58,11 @@ export function DocTableProvider({ getService, getPageObjects }: FtrProviderCont
: (await this.getBodyRows())[options.rowIndex];
}

public async getDetailsRow(): Promise<WebElementWrapper> {
const table = await this.getTable();
return await table.findByCssSelector('[data-test-subj~="docTableDetailsRow"]');
}

public async getAnchorDetailsRow(): Promise<WebElementWrapper> {
const table = await this.getTable();
return await table.findByCssSelector(
Expand Down Expand Up @@ -133,6 +138,22 @@ export function DocTableProvider({ getService, getPageObjects }: FtrProviderCont
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
}

public async getRemoveInclusiveFilterButton(
tableDocViewRow: WebElementWrapper
): Promise<WebElementWrapper> {
return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`);
}

public async removeInclusiveFilter(
detailsRow: WebElementWrapper,
fieldName: WebElementWrapper
): Promise<void> {
const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow);
await addInclusiveFilterButton.click();
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
}

public async getAddExistsFilterButton(
tableDocViewRow: WebElementWrapper
): Promise<WebElementWrapper> {
Expand Down
8 changes: 6 additions & 2 deletions test/functional/services/filter_bar.ts
Expand Up @@ -31,17 +31,21 @@ export function FilterBarProvider({ getService, getPageObjects }: FtrProviderCon
* @param key field name
* @param value filter value
* @param enabled filter status
* @param pinned filter pinned status
* @param negated filter including or excluding value
*/
public async hasFilter(
key: string,
value: string,
enabled: boolean = true,
pinned: boolean = false
pinned: boolean = false,
negated: boolean = false
): Promise<boolean> {
const filterActivationState = enabled ? 'enabled' : 'disabled';
const filterPinnedState = pinned ? 'pinned' : 'unpinned';
const filterNegatedState = negated ? 'filter-negated' : '';
return testSubjects.exists(
`filter filter-${filterActivationState} filter-key-${key} filter-value-${value} filter-${filterPinnedState}`,
`filter filter-${filterActivationState} filter-key-${key} filter-value-${value} filter-${filterPinnedState} ${filterNegatedState}`,
{
allowHidden: true,
}
Expand Down