Skip to content

Commit

Permalink
[8.14] [Search] [Playground] Improve UX + fix vector field issue (#18…
Browse files Browse the repository at this point in the history
…2342) (#182383)

# Backport

This will backport the following commits from `main` to `8.14`:
- [[Search] [Playground] Improve UX + fix vector field issue
(#182342)](#182342)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Joe
McElroy","email":"joseph.mcelroy@elastic.co"},"sourceCommit":{"committedDate":"2024-05-02T14:45:07Z","message":"[Search]
[Playground] Improve UX + fix vector field issue (#182342)\n\nImproves
the experience for Edit context and view query.\r\n\r\nNow select boxes
for context
fields\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6b9c9ef4-2cb1-4bec-9047-9736dbffc34d)\r\n\r\nand
now view query is checkboxes and a label for when fields are
hidden\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6ae4a2a2-41c2-4cbf-8ff9-954910272c4e)\r\n\r\nAlso
fixes an issue where if an index has a dense vector field but\r\ndoesn't
have an inference processor setup. we now skip over these type\r\nof
fields.","sha":"8cc8be50d75ac64ebe05985c0916aa248a78b61d","branchLabelMapping":{"^v8.15.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:EnterpriseSearch","v8.14.0","v8.15.0"],"title":"[Search]
[Playground] Improve UX + fix vector field
issue","number":182342,"url":"#182342
[Playground] Improve UX + fix vector field issue (#182342)\n\nImproves
the experience for Edit context and view query.\r\n\r\nNow select boxes
for context
fields\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6b9c9ef4-2cb1-4bec-9047-9736dbffc34d)\r\n\r\nand
now view query is checkboxes and a label for when fields are
hidden\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6ae4a2a2-41c2-4cbf-8ff9-954910272c4e)\r\n\r\nAlso
fixes an issue where if an index has a dense vector field but\r\ndoesn't
have an inference processor setup. we now skip over these type\r\nof
fields.","sha":"8cc8be50d75ac64ebe05985c0916aa248a78b61d"}},"sourceBranch":"main","suggestedTargetBranches":["8.14"],"targetPullRequestStates":[{"branch":"8.14","label":"v8.14.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.15.0","branchLabelMappingKey":"^v8.15.0$","isSourceBranch":true,"state":"MERGED","url":"#182342
[Playground] Improve UX + fix vector field issue (#182342)\n\nImproves
the experience for Edit context and view query.\r\n\r\nNow select boxes
for context
fields\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6b9c9ef4-2cb1-4bec-9047-9736dbffc34d)\r\n\r\nand
now view query is checkboxes and a label for when fields are
hidden\r\n\r\n\r\n![image](https://github.com/elastic/kibana/assets/49480/6ae4a2a2-41c2-4cbf-8ff9-954910272c4e)\r\n\r\nAlso
fixes an issue where if an index has a dense vector field but\r\ndoesn't
have an inference processor setup. we now skip over these type\r\nof
fields.","sha":"8cc8be50d75ac64ebe05985c0916aa248a78b61d"}}]}]
BACKPORT-->

Co-authored-by: Joe McElroy <joseph.mcelroy@elastic.co>
  • Loading branch information
kibanamachine and joemcelroy committed May 8, 2024
1 parent 6785bb1 commit 9e1d156
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 107 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/search_playground/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface QuerySourceFields {
dense_vector_query_fields: ModelFields[];
bm25_query_fields: string[];
source_fields: string[];
skipped_fields: number;
}

export enum APIRoutes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ jest.mock('../../hooks/use_indices_fields', () => ({
bm25_query_fields: ['field1', 'field2'],
source_fields: ['context_field1', 'context_field2'],
},
index2: {
elser_query_fields: [],
dense_vector_query_fields: [],
bm25_query_fields: ['field1', 'field2'],
source_fields: ['context_field1', 'context_field2'],
},
},
}),
}));
Expand All @@ -36,6 +42,11 @@ const MockFormProvider = ({ children }: { children: React.ReactElement }) => {
const methods = useForm({
values: {
indices: ['index1'],
docSize: 1,
sourceFields: {
index1: ['context_field1'],
index2: ['context_field2'],
},
},
});
return <FormProvider {...methods}>{children}</FormProvider>;
Expand All @@ -55,13 +66,14 @@ describe('EditContextFlyout component tests', () => {
});

it('calls onClose when the close button is clicked', () => {
fireEvent.click(screen.getByTestId('euiFlyoutCloseButton'));
fireEvent.click(screen.getByRole('button', { name: 'Close' }));
expect(onCloseMock).toHaveBeenCalledTimes(1);
});

it('should see the context fields', async () => {
expect(screen.getByTestId('contextFieldsSelectable')).toBeInTheDocument();
expect(screen.getByTestId('contextFieldsSelectable')).toHaveTextContent(`context_field2`);
expect(screen.getByTestId('contextFieldsSelectable')).toHaveTextContent(`context_field1`);
expect(screen.getByTestId('contextFieldsSelectable_index1')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('contextFieldsSelectable_index1'));
expect(screen.getByRole('option', { name: 'context_field1' })).toBeInTheDocument();
expect(screen.getByRole('option', { name: 'context_field2' })).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ import {
EuiLink,
EuiSpacer,
EuiText,
EuiPanel,
EuiAccordion,
EuiSelectable,
EuiSelect,
EuiSelectableOption,
EuiSuperSelect,
EuiFormRow,
} from '@elastic/eui';
import { useController, useFormContext } from 'react-hook-form';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -62,10 +60,10 @@ export const EditContextFlyout: React.FC<EditContextFlyoutProps> = ({ onClose })

const [tempSourceFields, setTempSourceFields] = useState(sourceFields);

const toggleSourceField = (index: string, f: EuiSelectableOption[]) => {
const updateSourceField = (index: string, field: string) => {
setTempSourceFields({
...tempSourceFields,
[index]: f.filter(({ checked }) => checked === 'on').map(({ label }) => label),
[index]: [field],
});
usageTracker?.click(AnalyticsEvents.editContextFieldToggled);
};
Expand All @@ -76,6 +74,7 @@ export const EditContextFlyout: React.FC<EditContextFlyoutProps> = ({ onClose })
onChangeSize(docSize);
onClose();
};

const handleDocSizeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
usageTracker?.click(AnalyticsEvents.editContextDocSizeChanged);
setDocSize(Number(e.target.value));
Expand Down Expand Up @@ -119,71 +118,69 @@ export const EditContextFlyout: React.FC<EditContextFlyoutProps> = ({ onClose })
<EuiFlyoutBody>
<EuiFlexGroup>
<EuiFlexItem grow={3}>
<EuiFlexGroup direction="column">
<EuiFlexGroup direction="column" gutterSize="l">
<EuiFlexItem grow={false}>
<EuiSelect
prepend={i18n.translate(
<EuiFormRow
label={i18n.translate(
'xpack.searchPlayground.editContext.flyout.docsRetrievedCount',
{
defaultMessage: 'Retrieved documents',
defaultMessage: 'Number of documents to retrieve',
}
)}
options={[
{
value: 3,
text: '3',
},
{
value: 5,
text: '5',
},
{
value: 10,
text: '10',
},
]}
value={docSize}
onChange={handleDocSizeChange}
/>
</EuiFlexItem>
<EuiText>
<h5>
<FormattedMessage
id="xpack.searchPlayground.editContext.flyout.table.title"
defaultMessage="Selected fields"
>
<EuiSelect
options={[
{
value: 1,
text: '1',
},
{
value: 3,
text: '3',
},
{
value: 5,
text: '5',
},
{
value: 10,
text: '10',
},
]}
value={docSize}
onChange={handleDocSizeChange}
/>
</h5>
</EuiText>
{Object.entries(fields).map(([index, group], i) => (
<EuiFlexItem grow={false} key={index}>
<EuiPanel grow={false} hasShadow={false} hasBorder>
<EuiAccordion
initialIsOpen={i === 0}
id={index}
buttonContent={
<EuiText>
<h5>{index}</h5>
</EuiText>
}
>
<EuiSpacer size="s" />
<EuiSelectable
aria-label="Select context fields"
data-test-subj="contextFieldsSelectable"
options={group.source_fields.map((field) => ({
label: field,
checked: tempSourceFields[index]?.includes(field) ? 'on' : undefined,
}))}
onChange={(newOptions) => toggleSourceField(index, newOptions)}
listProps={{ bordered: false }}
singleSelection="always"
>
{(list) => list}
</EuiSelectable>
</EuiAccordion>
</EuiPanel>
</EuiFlexItem>
))}
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="m">
<EuiFlexItem>
<EuiText>
<h5>
<FormattedMessage
id="xpack.searchPlayground.editContext.flyout.table.title"
defaultMessage="Select fields"
/>
</h5>
</EuiText>
</EuiFlexItem>
{Object.entries(fields).map(([index, group]) => (
<EuiFlexItem grow={false} key={index}>
<EuiFormRow label={index}>
<EuiSuperSelect
data-test-subj={`contextFieldsSelectable_${index}`}
options={group.source_fields.map((field) => ({
value: field,
inputDisplay: field,
}))}
valueOfSelected={tempSourceFields[index]?.[0]}
onChange={(value) => updateSourceField(index, value)}
/>
</EuiFormRow>
</EuiFlexItem>
))}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ jest.mock('../../hooks/use_indices_fields', () => ({
elser_query_fields: [],
dense_vector_query_fields: [],
bm25_query_fields: ['field1', 'field2'],
skipped_fields: 1,
},
index2: {
elser_query_fields: [],
dense_vector_query_fields: [],
bm25_query_fields: ['field1', 'field2'],
skipped_fields: 0,
},
},
}),
Expand All @@ -34,7 +41,7 @@ jest.mock('../../hooks/use_usage_tracker', () => ({
const MockFormProvider = ({ children }: { children: React.ReactElement }) => {
const methods = useForm({
values: {
indices: ['index1'],
indices: ['index1', 'index2'],
},
});
return <FormProvider {...methods}>{children}</FormProvider>;
Expand All @@ -61,7 +68,19 @@ describe('ViewQueryFlyout component tests', () => {
it('should see the view elasticsearch query', async () => {
expect(screen.getByTestId('ViewElasticsearchQueryResult')).toBeInTheDocument();
expect(screen.getByTestId('ViewElasticsearchQueryResult')).toHaveTextContent(
`{ "retriever": { "standard": { "query": { "multi_match": { "query": "{query}", "fields": [ "field1" ] } } } } }`
`{ "retriever": { "rrf": { "retrievers": [ { "standard": { "query": { "multi_match": { "query": "{query}", "fields": [ "field1" ] } } } }, { "standard": { "query": { "multi_match": { "query": "{query}", "fields": [ "field1" ] } } } } ] } } }`
);
});

it('displays query fields and indicates hidden fields', () => {
expect(screen.getByTestId('queryFieldsSelectable_index1')).toBeInTheDocument();
expect(screen.getByTestId('queryFieldsSelectable_index2')).toBeInTheDocument();

// Check if hidden fields indicator is shown
expect(screen.getByTestId('skipped_fields_index1')).toBeInTheDocument();
expect(screen.getByTestId('skipped_fields_index1')).toHaveTextContent('1 fields are hidden.');

// Check if hidden fields indicator is shown
expect(screen.queryByTestId('skipped_fields_index2')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ import {
EuiSelectableOption,
EuiText,
EuiTitle,
EuiCheckbox,
EuiLink,
EuiIcon,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useEffect, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { AnalyticsEvents } from '../../analytics/constants';
import { docLinks } from '../../../common/doc_links';
import { useIndicesFields } from '../../hooks/use_indices_fields';
import { useUsageTracker } from '../../hooks/use_usage_tracker';
import { ChatForm, ChatFormFields, IndicesQuerySourceFields } from '../../types';
Expand Down Expand Up @@ -157,7 +161,7 @@ export const ViewQueryFlyout: React.FC<ViewQueryFlyoutProps> = ({ onClose }) =>
</EuiCodeBlock>
</EuiFlexItem>
<EuiFlexItem grow={3}>
<EuiFlexGroup direction="column">
<EuiFlexGroup direction="column" gutterSize="s">
<EuiText>
<h5>
<FormattedMessage
Expand All @@ -166,12 +170,12 @@ export const ViewQueryFlyout: React.FC<ViewQueryFlyoutProps> = ({ onClose }) =>
/>
</h5>
</EuiText>
{Object.entries(fields).map(([index, group], i) => (
{Object.entries(fields).map(([index, group]) => (
<EuiFlexItem grow={false} key={index}>
<EuiPanel grow={false} hasShadow={false} hasBorder>
<EuiAccordion
id={index}
initialIsOpen={i === 0}
initialIsOpen
buttonContent={
<EuiText>
<h5>{index}</h5>
Expand All @@ -181,24 +185,70 @@ export const ViewQueryFlyout: React.FC<ViewQueryFlyoutProps> = ({ onClose }) =>
<EuiSpacer size="s" />
<EuiSelectable
aria-label="Select query fields"
data-test-subj={`queryFieldsSelectable_${index}`}
options={[
...group.elser_query_fields,
...group.dense_vector_query_fields,
...group.bm25_query_fields,
].map((field) => ({
label: typeof field === 'string' ? field : field.field,
checked: isQueryFieldSelected(
].map((field, idx) => {
const checked = isQueryFieldSelected(
index,
typeof field === 'string' ? field : field.field
)
? 'on'
: undefined,
}))}
);
return {
label: typeof field === 'string' ? field : field.field,
prepend: (
<EuiCheckbox
id={`checkbox_${idx}`}
checked={checked}
onChange={() => {}}
/>
),
checked: checked ? 'on' : undefined,
};
})}
listProps={{
bordered: false,
showIcons: false,
}}
onChange={(newOptions) => updateFields(index, newOptions)}
listProps={{ bordered: false }}
>
{(list) => list}
</EuiSelectable>
{group.skipped_fields > 0 && (
<>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiText
size="s"
color="subdued"
data-test-subj={`skipped_fields_${index}`}
>
<EuiIcon type="eyeClosed" />
{` `}
<FormattedMessage
id="xpack.searchPlayground.viewQuery.flyout.hiddenFields"
defaultMessage="{skippedFields} fields are hidden."
values={{ skippedFields: group.skipped_fields }}
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink
href={docLinks.chatPlayground}
target="_blank"
data-test-subj="context-optimization-documentation-link"
>
<FormattedMessage
id="xpack.searchPlayground.viewQuery.flyout.learnMoreLink"
defaultMessage="Learn more."
/>
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</EuiAccordion>
</EuiPanel>
</EuiFlexItem>
Expand Down

0 comments on commit 9e1d156

Please sign in to comment.