mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: pass the 'explain' flag from the UI and debug paging issues (#51847)
This commit is contained in:
parent
25edee88a7
commit
eb6d6d0d2b
@ -429,7 +429,8 @@ func doSearchQuery(
|
||||
hasConstraints = true
|
||||
}
|
||||
|
||||
if q.Query == "*" || q.Query == "" {
|
||||
isMatchAllQuery := q.Query == "*" || q.Query == ""
|
||||
if isMatchAllQuery {
|
||||
if !hasConstraints {
|
||||
fullQuery.AddShould(bluge.NewMatchAllQuery())
|
||||
}
|
||||
@ -600,7 +601,11 @@ func doSearchQuery(
|
||||
}
|
||||
|
||||
if q.Explain {
|
||||
fScore.Append(match.Score)
|
||||
if isMatchAllQuery {
|
||||
fScore.Append(float64(fieldLen + q.From))
|
||||
} else {
|
||||
fScore.Append(match.Score)
|
||||
}
|
||||
if match.Explanation != nil {
|
||||
js, _ := json.Marshal(&match.Explanation)
|
||||
jsb := json.RawMessage(js)
|
||||
|
@ -0,0 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { DataFrame } from '@grafana/data';
|
||||
import { CodeEditor, Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
|
||||
import { DataHoverView } from 'app/plugins/panel/geomap/components/DataHoverView';
|
||||
|
||||
export interface Props {
|
||||
name: string;
|
||||
explain: {};
|
||||
frame: DataFrame;
|
||||
row: number;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{ label: 'Score', value: 'score' },
|
||||
{ label: 'Fields', value: 'fields' },
|
||||
];
|
||||
|
||||
export function ExplainScorePopup({ name, explain, frame, row }: Props) {
|
||||
const [isOpen, setOpen] = useState<boolean>(true);
|
||||
const [activeTab, setActiveTab] = useState('score');
|
||||
|
||||
const modalHeader = (
|
||||
<ModalTabsHeader
|
||||
title={name}
|
||||
icon={'info'}
|
||||
tabs={tabs}
|
||||
activeTab={activeTab}
|
||||
onChangeTab={(t) => {
|
||||
setActiveTab(t.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal title={modalHeader} isOpen={isOpen} onDismiss={() => setOpen(false)} closeOnBackdropClick closeOnEscape>
|
||||
<TabContent>
|
||||
{activeTab === tabs[0].value && (
|
||||
<CodeEditor
|
||||
width="100%"
|
||||
height="70vh"
|
||||
language="json"
|
||||
showLineNumbers={false}
|
||||
showMiniMap={true}
|
||||
value={JSON.stringify(explain, null, 2)}
|
||||
readOnly={false}
|
||||
/>
|
||||
)}
|
||||
{activeTab === tabs[1].value && (
|
||||
<div>
|
||||
<DataHoverView data={frame} rowIndex={row} />
|
||||
</div>
|
||||
)}
|
||||
</TabContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -282,6 +282,11 @@ const getColumnStyles = (theme: GrafanaTheme2) => {
|
||||
text-align: right;
|
||||
padding: ${theme.spacing(1)} ${theme.spacing(3)} ${theme.spacing(1)} ${theme.spacing(1)};
|
||||
`,
|
||||
explainItem: css`
|
||||
text-align: right;
|
||||
padding: ${theme.spacing(1)} ${theme.spacing(3)} ${theme.spacing(1)} ${theme.spacing(1)};
|
||||
cursor: pointer;
|
||||
`,
|
||||
locationCellStyle: css`
|
||||
padding-top: ${theme.spacing(1)};
|
||||
padding-right: ${theme.spacing(1)};
|
||||
|
@ -64,6 +64,7 @@ export const SearchView = ({
|
||||
ds_uid: query.datasource as string,
|
||||
location: folderDTO?.uid, // This will scope all results to the prefix
|
||||
sort: query.sort?.value,
|
||||
explain: query.explain,
|
||||
};
|
||||
|
||||
// Only dashboards have additional properties
|
||||
|
@ -5,11 +5,14 @@ import SVG from 'react-inlinesvg';
|
||||
import { Field, FieldType, formattedValueToString, getDisplayProcessor, getFieldDisplayName } from '@grafana/data';
|
||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { Checkbox, Icon, IconButton, IconName, TagList } from '@grafana/ui';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { PluginIconName } from 'app/features/plugins/admin/types';
|
||||
import { ShowModalReactEvent } from 'app/types/events';
|
||||
|
||||
import { QueryResponse, SearchResultMeta } from '../../service';
|
||||
import { SelectionChecker, SelectionToggle } from '../selection';
|
||||
|
||||
import { ExplainScorePopup } from './ExplainScorePopup';
|
||||
import { TableColumn } from './SearchResultsTable';
|
||||
|
||||
const TYPE_COLUMN_WIDTH = 175;
|
||||
@ -40,6 +43,10 @@ export const generateColumns = (
|
||||
availableWidth -= sortFieldWith; // pre-allocate the space for the last column
|
||||
}
|
||||
|
||||
if (access.explain && access.score) {
|
||||
availableWidth -= 100; // pre-allocate the space for the last column
|
||||
}
|
||||
|
||||
let width = 50;
|
||||
if (selection && selectionToggle) {
|
||||
width = 30;
|
||||
@ -107,7 +114,8 @@ export const generateColumns = (
|
||||
let classNames = cx(styles.nameCellStyle);
|
||||
let name = access.name.values.get(p.row.index);
|
||||
if (!name?.length) {
|
||||
name = 'Missing title'; // normal for panels
|
||||
const loading = p.row.index >= response.view.dataFrame.length;
|
||||
name = loading ? 'Loading...' : 'Missing title'; // normal for panels
|
||||
classNames += ' ' + styles.missingTitleText;
|
||||
}
|
||||
return (
|
||||
@ -196,6 +204,37 @@ export const generateColumns = (
|
||||
});
|
||||
}
|
||||
|
||||
if (access.explain && access.score) {
|
||||
const vals = access.score.values;
|
||||
const showExplainPopup = (row: number) => {
|
||||
appEvents.publish(
|
||||
new ShowModalReactEvent({
|
||||
component: ExplainScorePopup,
|
||||
props: {
|
||||
name: access.name.values.get(row),
|
||||
explain: access.explain.values.get(row),
|
||||
frame: response.view.dataFrame,
|
||||
row: row,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
columns.push({
|
||||
Header: () => <div className={styles.sortedHeader}>Score</div>,
|
||||
Cell: (p) => {
|
||||
return (
|
||||
<div {...p.cellProps} className={styles.explainItem} onClick={() => showExplainPopup(p.row.index)}>
|
||||
{vals.get(p.row.index)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
id: `column-score-field`,
|
||||
field: access.score,
|
||||
width: 100,
|
||||
});
|
||||
}
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
|
@ -17,9 +17,6 @@ export const defaultQuery: DashboardQuery = {
|
||||
query: '',
|
||||
tag: [],
|
||||
starred: false,
|
||||
skipRecent: false,
|
||||
skipStarred: false,
|
||||
folderIds: [],
|
||||
sort: null,
|
||||
layout: SearchLayout.Folders,
|
||||
prevSort: null,
|
||||
|
@ -121,15 +121,12 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
|
||||
}
|
||||
}
|
||||
|
||||
const view = new DataFrameView<DashboardQueryResult>(first);
|
||||
return {
|
||||
totalRows: meta.count ?? first.length,
|
||||
view,
|
||||
loadMoreItems: async (startIndex: number, stopIndex: number): Promise<void> => {
|
||||
console.log('LOAD NEXT PAGE', { startIndex, stopIndex, length: view.dataFrame.length });
|
||||
let loadMax = 0;
|
||||
let pending: Promise<void> | undefined = undefined;
|
||||
const getNextPage = async () => {
|
||||
while (loadMax > view.dataFrame.length) {
|
||||
const from = view.dataFrame.length;
|
||||
const limit = stopIndex - from;
|
||||
if (limit < 0) {
|
||||
if (from >= meta.count) {
|
||||
return;
|
||||
}
|
||||
const frame = (
|
||||
@ -141,7 +138,7 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
|
||||
search: {
|
||||
...(target?.search ?? {}),
|
||||
from,
|
||||
limit: Math.max(limit, nextPageSizes),
|
||||
limit: nextPageSizes,
|
||||
},
|
||||
refId: 'Page',
|
||||
facet: undefined,
|
||||
@ -175,7 +172,20 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
|
||||
meta.locationInfo[key] = value;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
pending = undefined;
|
||||
};
|
||||
|
||||
const view = new DataFrameView<DashboardQueryResult>(first);
|
||||
return {
|
||||
totalRows: meta.count ?? first.length,
|
||||
view,
|
||||
loadMoreItems: async (startIndex: number, stopIndex: number): Promise<void> => {
|
||||
loadMax = Math.max(loadMax, stopIndex);
|
||||
if (!pending) {
|
||||
pending = getNextPage();
|
||||
}
|
||||
return pending;
|
||||
},
|
||||
isItemLoaded: (index: number): boolean => {
|
||||
return index < view.dataFrame.length;
|
||||
|
@ -33,6 +33,10 @@ export interface DashboardQueryResult {
|
||||
tags: string[];
|
||||
location: string; // url that can be split
|
||||
ds_uid: string[];
|
||||
|
||||
// debugging fields
|
||||
score: number;
|
||||
explain: {};
|
||||
}
|
||||
|
||||
export interface LocationInfo {
|
||||
|
@ -61,9 +61,7 @@ export interface DashboardQuery {
|
||||
query: string;
|
||||
tag: string[];
|
||||
starred: boolean;
|
||||
skipRecent: boolean;
|
||||
skipStarred: boolean;
|
||||
folderIds: number[];
|
||||
explain?: boolean; // adds debug info
|
||||
datasource?: string;
|
||||
sort: SelectableValue | null;
|
||||
// Save sorting data between layouts
|
||||
|
Loading…
Reference in New Issue
Block a user