Skip to content

Commit

Permalink
[Data Explorer] Implement data fetch logic in Discover 2.0
Browse files Browse the repository at this point in the history
Signed-off-by: ananzh <[email protected]>
  • Loading branch information
ananzh committed Jul 13, 2023
1 parent 0265216 commit 29a775c
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 27 deletions.
35 changes: 35 additions & 0 deletions src/plugins/data/common/search/search_source/search_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
*/

import { setWith } from '@elastic/safer-lodash-set';
import { Observable, from } from 'rxjs';
import { switchMap, tap, of } from 'rxjs/operators';
import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash';
import { normalizeSortRequest } from './normalize_sort_request';
import { filterDocvalueFields } from './filter_docvalue_fields';
Expand Down Expand Up @@ -296,6 +298,39 @@ export class SearchSource {
return response;
}

/**
* Fetch this source and return Observable
*/
fetch$(options: ISearchOptions = {}) {
const { getConfig } = this.dependencies;

return from(this.requestIsStarting(options)).pipe(
switchMap(() => this.flatten()),
tap((searchRequest) => {
this.history = [searchRequest];
let response$: Observable<any>;

if (getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES)) {
response$ = from(this.legacyFetch(searchRequest, options));
} else {
const indexPattern = this.getField('index');
searchRequest.dataSourceId = indexPattern?.dataSourceRef?.id;

response$ = from(this.fetchSearch(searchRequest, options));
}

return response$;
}),
switchMap((response) => {
if ((response as any).error) {
throw new RequestFailure(null, response);
}

return of(response);
})
);
}

/**
* Add a handler that will be notified whenever requests start
* @param {Function} handler
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
// ToDo: impletment DiscoverTableComponent which return
// should handle data columns, rows
{
/* <DataGridTable
columns={state.columns || []}
indexPattern={indexPattern}
rows={rows}
sort={(state.sort as Array<[any, any]>) || []}
onAddColumn={addColumn}
onFilter={onAddFilter}
onRemoveColumn={onRemoveColumn}
onSetColumns={onSetColumns}
onSort={onSort}
displayTimeColumn={displayTimeColumn}
services={services}
/>;*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { History } from 'history';
import { SavedObject } from 'src/core/types/saved_objects';
import { DiscoverServices } from '../../../build_services';
import { SavedSearch } from '../../../saved_searches';
import { DiscoverCanvasApp } from './discover_canvas_app';
import { DiscoverCanvasService } from './discover_canvas_service';
import { fetchIndexPattern, fetchSavedSearch } from './utils/index_pattern_helper';

export interface DiscoverCanvasProps {
Expand Down Expand Up @@ -60,7 +60,7 @@ export const DiscoverCanvas = ({ history, services }: DiscoverCanvasProps) => {
}, [data, config, core, chrome, toastNotifications, history, savedSearchId, services, basePath]);

return (
<DiscoverCanvasApp
<DiscoverCanvasService
history={history}
services={services}
savedSearch={savedSearch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,32 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { SavedObject } from 'src/core/types/saved_objects';
import { History } from 'history';
import { SearchSource } from 'src/plugins/data/common';
import { DiscoverServices } from '../../../build_services';
import { SavedSearch } from '../../../saved_searches';
import { IndexPatternData } from './utils/index_pattern_helper';

export interface DiscoverCanvasAppProps {
services: DiscoverServices;
history: History;
savedSearch: SavedSearch | undefined;
indexPatternList: Array<SavedObject<any>>;
}
import { EuiPage, EuiPageBody, EuiFlexGroup, EuiFlexItem, EuiPageContent } from '@elastic/eui';
import { DiscoverTableComponent } from '../../components/data_grid/discover_table_component';

export const DiscoverCanvasApp = ({
history,
services,
savedSearch,
indexPatternList,
}: DiscoverCanvasAppProps) => {
if (savedSearch && savedSearch.searchSource) {
const ip = (savedSearch.searchSource as SearchSource).getField('index') as IndexPatternData;
return <div>{ip.loaded.id}</div>;
}
return <div>{'DiscoverCanvasApp'}</div>;
export const DiscoverCanvasApplication = ({}) => {
return (
<EuiPage className="dscCanvasAppPage">
<EuiPageBody className="dscCanvasAppPageBody">
<EuiFlexGroup className="dscCanvasAppPageBody__contents">
<EuiFlexItem>
<EuiPageContent>
{
<div className="dscDiscoverGrid">
<DiscoverTableComponent
data={data$}
indexPattern={indexPattern}
savedSearch={savedSearch}
onAddFilter={onAddFilter}
services={services}
/>
</div>
}
</EuiPageContent>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageBody>
</EuiPage>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { SavedObject } from 'src/core/types/saved_objects';
import { History } from 'history';
import { SearchSource } from 'src/plugins/data/common';
import { DiscoverServices } from '../../../build_services';
import { SavedSearch } from '../../../saved_searches';
import { IndexPatternData } from '../utils/index_pattern_helper';
import { useDiscoverCanvasService } from './use_discover_canvas_service';

export interface DiscoverCanvasAppProps {
services: DiscoverServices;
history: History;
savedSearch: SavedSearch | undefined;
indexPatternList: Array<SavedObject<any>>;
}

export const DiscoverCanvasService = ({
history,
services,
savedSearch,
indexPatternList,
}: DiscoverCanvasAppProps) => {
const { data$ } = useDiscoverCanvasService({ services, savedSearch, history });
// if (savedSearch && savedSearch.searchSource) {
// const ip = (savedSearch.searchSource as SearchSource).getField('index') as IndexPatternData;
// }
// ToDo: DiscoverCanvasApplication
return <div>{'DiscoverCanvasApp'}</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { useMemo } from 'react';
import { DiscoverServices } from '../../../../build_services';
import { SavedSearch } from '../../../../saved_searches';
import { useSavedSearch } from '../utils/use_saved_search';

export interface DiscoverCanvasServiceProps {
services: DiscoverServices;
history: History;
savedSearch: SavedSearch;
}

export const useDiscoverCanvasService = ({
services,
history,
savedSearch,
}: DiscoverCanvasServiceProps) => {
const indexPattern = savedSearch.searchSource.getField('index');
const searchSource = useMemo(() => {
savedSearch.searchSource.setField('index', indexPattern);
return savedSearch.searchSource.createChild();
}, [savedSearch, indexPattern]);

const { data$, refetch$ } = useSavedSearch({
indexPattern,
searchSource,
services,
});

return {
data$,
refetch$,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { useCallback, useMemo, useRef } from 'react';
import { SearchSource, IndexPattern } from 'src/plugins/data/public';
import { BehaviorSubject, Subject } from 'rxjs';
import { useEffect } from 'react';
import { DiscoverServices } from '../../../build_services';

import { validateTimeRange } from '../../../application/helpers/validate_time_range';
import { RequestAdapter } from '../../../../inspector/public';

export enum FetchStatus {
UNINITIALIZED = 'uninitialized',
LOADING = 'loading',
COMPLETE = 'complete',
ERROR = 'error',
}

export interface SavedSearchData {
status: FetchStatus;
fetchCounter?: number;
fieldCounts?: Record<string, number>;
fetchError?: Error;
hits?: number;
rows?: any[]; // ToDo: type
}

export type SavedSearchRefetch = 'refetch' | undefined;

export type DataSubject = BehaviorSubject<SavedSearchData>;
export type RefetchSubject = BehaviorSubject<SavedSearchRefetch>;

export const useSavedSearch = ({
indexPattern,
searchSource,
services,
}: {
indexPattern: IndexPattern;
searchSource: SearchSource;
services: DiscoverServices;
}) => {
const { data, filterManager } = services;
const timefilter = data.query.timefilter.timefilter;
const fetchStateRef = useRef<{
abortController: AbortController | undefined;
fieldCounts: Record<string, number>;
fetchStatus: FetchStatus;
}>({
abortController: undefined,
fieldCounts: {},
fetchStatus: FetchStatus.UNINITIALIZED,
});

const data$ = useMemo(
() => new BehaviorSubject<any>({ state: FetchStatus.UNINITIALIZED }),
[]
);
const refetch$ = useMemo(() => new Subject<any>(), []);

const fetch = useCallback(() => {
if (!validateTimeRange(timefilter.getTime(), services.toastNotifications)) {
return Promise.reject();
}
const inspectorAdapters = { requests: new RequestAdapter() };

if (fetchStateRef.current.abortController) fetchStateRef.current.abortController.abort();
fetchStateRef.current.abortController = new AbortController();

updateDataSource(searchSource, { indexPattern, services, sort });

const fetchResp = searchSource.fetch$({
abortSignal: fetchStateRef.current.abortController.signal,
});

fetchResp.pipe().subscribe(
(resp) => {
const hits = resp.rawResponse.hits.total as number;
const rows = resp.rawResponse.hits.hits;

for (const row of rows) {
const fields = Object.keys(indexPattern.flattenHit(row));
for (const fieldName of fields) {
fetchStateRef.current.fieldCounts[fieldName] =
(fetchStateRef.current.fieldCounts[fieldName] || 0) + 1;
}
}
fetchStateRef.current.fieldCounts = fetchStateRef.current.fieldCounts!;
fetchStateRef.current.fetchStatus = FetchStatus.COMPLETE;
data$.next({
status: FetchStatus.COMPLETE,
fieldCounts: fetchStateRef.current.fieldCounts,
hits,
rows,
});
},
(error) => {
// ToDo: implement error handling in react
// export function addFatalError(
// fatalErrors: FatalErrorsSetup,
// error: AngularHttpError | Error | string,
// location?: string
// ) {
// // add support for angular http errors to newPlatformFatalErrors
// if (isAngularHttpError(error)) {
// error = formatAngularHttpError(error);
// }
// fatalErrors.add(error, location);
// }
},
() => {}
);
}, [data$, timefilter, services, searchSource, indexPattern, fetchStateRef]);

useEffect(() => {
// ToDo: implement
// const fetch$ = merge(
// refetch$,
// filterManager.getFetches$(),
// timefilter.getFetch$(),
// timefilter.getAutoRefreshFetch$(),
// data.query.queryString.getUpdates$()
// ).pipe(debounceTime(100));
// subscriptions.add(
// subscribeWithScope(
// $scope,
// fetch$,
// {
// next: $scope.fetch,
// },
// (error) => addFatalError(core.fatalErrors, error)
// )
// );
// subscriptions.add(
// subscribeWithScope(
// $scope,
// timefilter.getTimeUpdate$(),
// {
// next: () => {
// $scope.updateTime();
// },
// },
// (error) => addFatalError(core.fatalErrors, error)
// )
// );
});

return {
data$,
refetch$,
};
};

0 comments on commit 29a775c

Please sign in to comment.