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

[Search] [Playground] [Bug] Add playground index validation #201032

Merged
merged 14 commits into from
Nov 25, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('SelectIndicesFlyout', () => {
mockedUseQueryIndices.mockReturnValue({
indices: ['index1', 'index2', 'index3'],
isLoading: false,
isFetched: true,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface SelectIndicesFlyout {

export const SelectIndicesFlyout: React.FC<SelectIndicesFlyout> = ({ onClose }) => {
const [query, setQuery] = useState<string>('');
const { indices, isLoading: isIndicesLoading } = useQueryIndices(query);
const { indices, isLoading: isIndicesLoading } = useQueryIndices({ query });
const { indices: selectedIndices, setIndices: setSelectedIndices } = useSourceIndicesFields();
const [selectedTempIndices, setSelectedTempIndices] = useState<string[]>(selectedIndices);
const handleSelectOptions = (options: EuiSelectableOption[]) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { useEffect, useState } from 'react';
import { useQueryIndices } from './use_query_indices';

export const useIndicesValidation = (unvalidatedIndices: string[]) => {
const [isValidated, setIsValidated] = useState<boolean>(false);
const [validIndices, setValidIndices] = useState<string[]>([]);
const { indices, isFetched: isIndicesLoaded } = useQueryIndices({
targetIndices: unvalidatedIndices,
});

useEffect(() => {
if (isIndicesLoaded) {
setValidIndices(indices.filter((index) => unvalidatedIndices.includes(index)));
Copy link
Member

@joemcelroy joemcelroy Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useQueryIndices gets the top 10 indices, what happens if its not in this list but does exist?

I would do an explicit API call for all the unvalidatedIndices to see if they exist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setIsValidated(true);
}
}, [unvalidatedIndices, indices, isIndicesLoaded]);

return { isValidated, validIndices };
};
17 changes: 11 additions & 6 deletions x-pack/plugins/search_playground/public/hooks/use_query_indices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ import { IndexName } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { useKibana } from './use_kibana';
import { APIRoutes } from '../types';

export const useQueryIndices = (
query: string = ''
): { indices: IndexName[]; isLoading: boolean } => {
export const useQueryIndices = ({
query,
targetIndices = [],
}: {
query?: string;
targetIndices?: string[];
} = {}): { indices: IndexName[]; isLoading: boolean; isFetched: boolean } => {
const { services } = useKibana();

const { data, isLoading } = useQuery({
queryKey: ['indices', query],
const { data, isLoading, isFetched } = useQuery({
queryKey: ['indices', query, targetIndices.join(',')],
queryFn: async () => {
const response = await services.http.get<{
indices: string[];
}>(APIRoutes.GET_INDICES, {
query: {
search_query: query,
target_indices: targetIndices.join(','),
size: 10,
},
});
Expand All @@ -32,5 +37,5 @@ export const useQueryIndices = (
initialData: [],
});

return { indices: data, isLoading };
return { indices: data, isLoading, isFetched };
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { FormProvider as ReactHookFormProvider, useForm } from 'react-hook-form';
import React, { useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom-v5-compat';
import { useIndicesValidation } from '../hooks/use_indices_validation';
import { useLoadFieldsByIndices } from '../hooks/use_load_fields_by_indices';
import { ChatForm, ChatFormFields } from '../types';
import { useLLMsModels } from '../hooks/use_llms_models';
Expand Down Expand Up @@ -53,16 +54,27 @@ export const FormProvider: React.FC<React.PropsWithChildren<FormProviderProps>>
}) => {
const models = useLLMsModels();
const [searchParams] = useSearchParams();
const index = useMemo(() => searchParams.get('default-index'), [searchParams]);
const defaultIndex = useMemo(() => {
const index = searchParams.get('default-index');

return index ? [index] : null;
}, [searchParams]);
const sessionState = useMemo(() => getLocalSession(storage), [storage]);
const form = useForm<ChatForm>({
defaultValues: {
...sessionState,
indices: index ? [index] : sessionState.indices,
indices: [],
search_query: '',
},
});
useLoadFieldsByIndices({ watch: form.watch, setValue: form.setValue, getValues: form.getValues });
const { isValidated: isValidatedIndices, validIndices } = useIndicesValidation(
defaultIndex || sessionState.indices || []
);
useLoadFieldsByIndices({
watch: form.watch,
setValue: form.setValue,
getValues: form.getValues,
});

useEffect(() => {
const subscription = form.watch((values) =>
Expand All @@ -80,5 +92,11 @@ export const FormProvider: React.FC<React.PropsWithChildren<FormProviderProps>>
}
}, [form, models]);

useEffect(() => {
if (isValidatedIndices) {
form.setValue(ChatFormFields.indices, validIndices);
}
}, [form, isValidatedIndices, validIndices]);

return <ReactHookFormProvider {...form}>{children}</ReactHookFormProvider>;
};
14 changes: 10 additions & 4 deletions x-pack/plugins/search_playground/server/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,22 +198,28 @@ export function defineRoutes({
query: schema.object({
search_query: schema.maybe(schema.string()),
size: schema.number({ defaultValue: 10, min: 0 }),
target_indices: schema.maybe(schema.string()),
}),
},
},
errorHandler(logger)(async (context, request, response) => {
const { search_query: searchQuery, size } = request.query;
const { search_query: searchQuery, target_indices: targetIndices, size } = request.query;
const {
client: { asCurrentUser },
} = (await context.core).elasticsearch;
const targetIndexArray = targetIndices ? targetIndices.split(',') : [];

const { indexNames } = await fetchIndices(asCurrentUser, searchQuery);

const indexNameSlice = indexNames.slice(0, size).filter(isNotNullish);
const validTargetIndices = targetIndexArray.filter((targetIndex) =>
indexNames.includes(targetIndex)
);
const combinedIndices = Array.from(new Set([...validTargetIndices, ...indexNames]))
.slice(0, size)
.filter(isNotNullish);

return response.ok({
body: {
indices: indexNameSlice,
indices: combinedIndices,
},
headers: { 'content-type': 'application/json' },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});

describe('without any indices', () => {
it('hide no create index button when index added', async () => {
it('hide create index button when index added', async () => {
await createIndex();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenFlyoutAndSelectIndex();
});
Expand All @@ -129,6 +129,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndLoadChat();
});

it('load start page after removing selected index', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndLoadChat();
await esArchiver.unload(esArchiveIndex);
await browser.refresh();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists();
});

after(async () => {
await removeOpenAIConnector?.();
await esArchiver.unload(esArchiveIndex);
Expand Down