mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: Investigate frontend search options (again) (#55526)
This commit is contained in:
parent
c3ca5405ce
commit
0c38a3ba97
104
public/app/features/search/service/frontend.ts
Normal file
104
public/app/features/search/service/frontend.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { DataFrameView, SelectableValue, ArrayVector } from '@grafana/data';
|
||||||
|
import { TermCount } from 'app/core/components/TagFilter/TagFilter';
|
||||||
|
|
||||||
|
import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery } from '.';
|
||||||
|
|
||||||
|
export class FrontendSearcher implements GrafanaSearcher {
|
||||||
|
readonly cache = new Map<string, FullResultCache>();
|
||||||
|
|
||||||
|
constructor(private parent: GrafanaSearcher) {}
|
||||||
|
|
||||||
|
async search(query: SearchQuery): Promise<QueryResponse> {
|
||||||
|
if (query.facet?.length) {
|
||||||
|
throw new Error('facets not supported!');
|
||||||
|
}
|
||||||
|
// Don't bother... not needed for this exercise
|
||||||
|
if (query.tags?.length || query.ds_uid?.length) {
|
||||||
|
return this.parent.search(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO -- make sure we refresh after a while
|
||||||
|
const all = await this.getCache(query.kind);
|
||||||
|
const view = all.search(query.query);
|
||||||
|
return {
|
||||||
|
isItemLoaded: () => true,
|
||||||
|
loadMoreItems: async (startIndex: number, stopIndex: number): Promise<void> => {},
|
||||||
|
totalRows: view.length,
|
||||||
|
view,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCache(kind?: string[]): Promise<FullResultCache> {
|
||||||
|
const key = kind ? kind.join(',') : '*';
|
||||||
|
let res = this.cache.get(key);
|
||||||
|
if (res) {
|
||||||
|
return Promise.resolve(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const v = await this.parent.search({
|
||||||
|
kind, // match the request
|
||||||
|
limit: 5000, // max for now
|
||||||
|
});
|
||||||
|
|
||||||
|
res = new FullResultCache(v.view);
|
||||||
|
this.cache.set(key, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async starred(query: SearchQuery): Promise<QueryResponse> {
|
||||||
|
return this.parent.starred(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the appropriate sorting options
|
||||||
|
async getSortOptions(): Promise<SelectableValue[]> {
|
||||||
|
return this.parent.getSortOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async tags(query: SearchQuery): Promise<TermCount[]> {
|
||||||
|
return this.parent.tags(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullResultCache {
|
||||||
|
readonly lower: string[];
|
||||||
|
empty: DataFrameView<DashboardQueryResult>;
|
||||||
|
|
||||||
|
constructor(private full: DataFrameView<DashboardQueryResult>) {
|
||||||
|
this.lower = this.full.fields.name.values.toArray().map((v) => (v ? v.toLowerCase() : ''));
|
||||||
|
|
||||||
|
// Copy with empty values
|
||||||
|
this.empty = new DataFrameView<DashboardQueryResult>({
|
||||||
|
...this.full.dataFrame, // copy folder metadata
|
||||||
|
fields: this.full.dataFrame.fields.map((v) => ({ ...v, values: new ArrayVector([]) })),
|
||||||
|
length: 0, // for now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// single instance that is mutated for each response (not great, but OK for now)
|
||||||
|
search(query?: string): DataFrameView<DashboardQueryResult> {
|
||||||
|
if (!query?.length || query === '*') {
|
||||||
|
return this.full;
|
||||||
|
}
|
||||||
|
const match = query.toLowerCase();
|
||||||
|
const allFields = this.full.dataFrame.fields;
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const values = allFields.map((v) => [] as any[]); // empty value for each field
|
||||||
|
|
||||||
|
for (let i = 0; i < this.lower.length; i++) {
|
||||||
|
if (this.lower[i].indexOf(match) >= 0) {
|
||||||
|
for (let c = 0; c < allFields.length; c++) {
|
||||||
|
values[c].push(allFields[c].values.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutates the search object
|
||||||
|
this.empty.dataFrame.fields.forEach((f, idx) => {
|
||||||
|
f.values = new ArrayVector(values[idx]); // or just set it?
|
||||||
|
});
|
||||||
|
this.empty.dataFrame.length = this.empty.dataFrame.fields[0].values.length;
|
||||||
|
|
||||||
|
return this.empty;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,21 @@
|
|||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
|
|
||||||
import { BlugeSearcher } from './bluge';
|
import { BlugeSearcher } from './bluge';
|
||||||
|
import { FrontendSearcher } from './frontend';
|
||||||
import { SQLSearcher } from './sql';
|
import { SQLSearcher } from './sql';
|
||||||
import { GrafanaSearcher } from './types';
|
import { GrafanaSearcher } from './types';
|
||||||
|
|
||||||
let searcher: GrafanaSearcher | undefined = undefined;
|
let searcher: GrafanaSearcher | undefined = undefined;
|
||||||
|
|
||||||
export function getGrafanaSearcher(): GrafanaSearcher {
|
export function getGrafanaSearcher(): GrafanaSearcher {
|
||||||
const sqlSearcher = new SQLSearcher();
|
|
||||||
if (!searcher) {
|
if (!searcher) {
|
||||||
|
const sqlSearcher = new SQLSearcher();
|
||||||
const useBluge = config.featureToggles.panelTitleSearch;
|
const useBluge = config.featureToggles.panelTitleSearch;
|
||||||
searcher = useBluge ? new BlugeSearcher(sqlSearcher) : sqlSearcher;
|
searcher = useBluge ? new BlugeSearcher(sqlSearcher) : sqlSearcher;
|
||||||
|
|
||||||
|
if (useBluge && location.search.indexOf('do-frontend-query')) {
|
||||||
|
searcher = new FrontendSearcher(searcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return searcher!;
|
return searcher!;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user