Skip to content

Es pattern 优化 #1404

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

Merged
merged 9 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions public/font/iconfont.js

Large diffs are not rendered by default.

96 changes: 11 additions & 85 deletions src/pages/explorer/Elasticsearch/HighLightJSON.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { useState, useEffect, useRef } from 'react';
import { Modal, Popover } from 'antd';
import { basePrefix } from '@/App';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { ILogExtract, ILogURL } from '@/pages/log/IndexPatterns/types';
import Links from '@/pages/explorer/components/Links';
const Indent = 30;
const colonSpace = 10;

interface IProps {
value: object;
features: { field: string; name: string; urlTemplate: string }[];
urlTemplates?: ILogURL[];
extractArr?: ILogExtract[];
query: any;
}
export default function highlightJson(props: IProps) {
Expand All @@ -20,103 +20,29 @@ export default function highlightJson(props: IProps) {
// veryNest: [{ v: '112' }],
// };

const { value, features, query } = props;
const { value, urlTemplates, query, extractArr } = props;
return (
<div className='highlight-json'>
<div>{'{'}</div>
{renderObject(value, [], { features, rawValue: value, query })}
{renderObject(value, [], { urlTemplates, rawValue: value, query, extractArr })}
<div>{'}'}</div>
</div>
);
}

interface IParam {
features: { field: string; name: string; urlTemplate: string }[];
urlTemplates?: ILogURL[];
rawValue: object;
query: any;
extractArr?: ILogExtract[];
}

function renderValue(value, keyPath: string[], param: IParam) {
const { t } = useTranslation();
const { features, rawValue, query } = param;
const { urlTemplates, rawValue, query, extractArr } = param;
const keyPathStr = keyPath.join('.');
const links = features.filter((i) => i.field === keyPathStr);
const [hoveringIndex, setHoveringIndex] = useState(0);
const [relations, setRelations] = useState<
{
name: string;
link: string;
}[]
>([]);
const handleNav = (link: string) => {
const param = new URLSearchParams(link);
const startMargin = param.get('${__start_time_margin__}');
const endMargin = param.get('${__end_time_margin__}');
const startMarginNum = startMargin && !isNaN(Number(startMargin)) ? Number(startMargin) : 0;
const endMarginNum = endMargin && !isNaN(Number(endMargin)) ? Number(endMargin) : 0;
let reallink = link
.replace('${local_protocol}', location.protocol)
.replace('${local_domain}', location.host)
.replace('${local_url}', location.origin)
.replace('${__start_time__}', typeof query.start === 'number' ? String(1000 * query.start + startMarginNum) : '')
.replace('${__end_time__}', typeof query.end === 'number' ? String(1000 * query.end + endMarginNum) : '');
const links = urlTemplates?.filter((i) => i.field === keyPathStr) || [];

if (startMargin) {
reallink = reallink.replace('&${__start_time_margin__}' + '=' + startMargin, '');
}
if (endMargin) {
reallink = reallink.replace('&${__end_time_margin__}' + '=' + endMargin, '');
}
const unReplaceKeyReg = /\$\{(.+?)\}/g;
reallink = reallink.replace(unReplaceKeyReg, function (a, b) {
const wholeWord = rawValue[b];
return wholeWord || _.get(rawValue, b.split('.'));
});
window.open(basePrefix + reallink.replace(unReplaceKeyReg, ''), '_blank');
};

const renderLink = (links, value) => (
<>
{/* {links.length === 1 ? (
<a
onClick={() => {
handleNav(links[0].relations[0].link);
}}
>
{value}
</a>
) : ( */}
<Popover
placement='right'
overlayClassName='popover-json'
onVisibleChange={(visible) => {
if (!visible) {
setHoveringIndex(0);
}
}}
content={relations.map((item, i) => (
<div key={i} style={{ lineHeight: '24px' }}>
<a onClick={() => handleNav(item.link)}>{item.name}</a>
</div>
))}
>
<a
style={{ textDecoration: 'underline', fontWeight: 'bold' }}
onMouseEnter={() => {
setRelations(links.map((i) => ({ name: i.name, link: i.urlTemplate })));
}}
onClick={() => {
if (relations.length > 0) {
handleNav(relations[0].link);
}
}}
>
{value}
</a>
</Popover>
{/* )} */}
</>
);
const renderLink = (links, value) => <Links rawValue={rawValue} range={query} text={value} paramsArr={links} />;

if (_.isNumber(value)) {
return links.length > 0 ? renderLink(links, value) : <span style={{ color: '#164' }}>{value}</span>;
Expand Down
36 changes: 21 additions & 15 deletions src/pages/explorer/Elasticsearch/LogView.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { Space, Table, Tabs } from 'antd';
import { QuestionOutlined, CopyOutlined } from '@ant-design/icons';
import { EditorView } from '@codemirror/view';
import { json } from '@codemirror/lang-json';
import { defaultHighlightStyle } from '@codemirror/highlight';
import purify from 'dompurify';
import { copyToClipBoard } from '@/utils';
import HighLightJSON from './HighLightJSON';
import { Field, getFieldLabel, getFieldValue, RenderValue } from './utils';
import { Field, getFieldLabel } from './utils';
import { getHighlightHtml } from './utils/highlight';
import RenderValue from '@/pages/explorer/components/RenderValue';
import { typeIconMap } from './FieldsSidebar/Field';
import { typeMap } from '../Elasticsearch/services';
import { IRawTimeRange, parseRange } from '@/components/TimeRangePicker';
import { FieldConfigVersion2, ILogExtract } from '@/pages/log/IndexPatterns/types';
import moment from 'moment';
interface Props {
value: Record<string, any>;
fieldConfig: any;
fieldConfig?: FieldConfigVersion2;
fields: Field[];
highlight: any;
range: IRawTimeRange;
Expand All @@ -25,12 +29,6 @@ export default function LogView(props: Props) {
const { t } = useTranslation('explorer');
const { value, fieldConfig, fields, highlight, range } = props;

const allParamsArr = fieldConfig?.formatMap
? Object.keys(fieldConfig.formatMap).reduce((prev, cur) => {
return fieldConfig.formatMap[cur].paramsArr?.length > 0 ? [...prev, ...fieldConfig.formatMap[cur].paramsArr] : [];
}, [])
: [];

const [type, setType] = useState<string>('table');
const parsedRange = range ? parseRange(range) : null;
let start = parsedRange ? moment(parsedRange.start).unix() : 0;
Expand Down Expand Up @@ -98,12 +96,20 @@ export default function LogView(props: Props) {
key: 'value',
render: (val: any, record: { field: string }) => {
const field = record.field;
const fieldVal = getFieldValue(field, val, fieldConfig, value, range);
const v = _.isArray(fieldVal) ? _.join(fieldVal, ',') : fieldVal;
return (
<div>
<RenderValue value={v} highlights={highlight?.[field]} />
</div>
<RenderValue
fieldKey={field}
fieldValue={val}
fieldConfig={fieldConfig}
rawValue={value}
range={range}
adjustFieldValue={(formatedValue) => {
if (highlight?.[field]) {
return <span dangerouslySetInnerHTML={{ __html: purify.sanitize(getHighlightHtml(formatedValue, highlight[field])) }} />;
}
return formatedValue;
}}
/>
);
},
},
Expand All @@ -113,7 +119,7 @@ export default function LogView(props: Props) {
/>
</Tabs.TabPane>
<Tabs.TabPane tab='JSON' key='json'>
<HighLightJSON value={value} query={{ start, end }} features={allParamsArr} />
<HighLightJSON value={value} query={{ start, end }} urlTemplates={fieldConfig?.linkArr} extractArr={fieldConfig?.regExtractArr} />
</Tabs.TabPane>
</Tabs>
);
Expand Down
11 changes: 0 additions & 11 deletions src/pages/explorer/Elasticsearch/QueryBuilderWithIndexPatterns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,10 @@ export default function QueryBuilder(props: Props) {
const finded = indexPatterns.find((i) => i.id === indexPattern || i.name === indexPattern); //从url上带过来时indexPattern不是id,是name,兼容下这种情况
if (finded) {
const formValuesQuery = form.getFieldValue('query');
let fieldConfig;
try {
if (finded.fields_format) {
fieldConfig = standardizeFieldConfig(JSON.parse(finded.fields_format));
form.setFields([{ name: 'fieldConfig', value: undefined }]);
}
} catch (error) {
console.warn(error);
}

formValuesQuery.date_field = finded.time_field;
formValuesQuery.index = finded.name;
form.setFieldsValue({
query: formValuesQuery,
fieldConfig,
});
onIndexChange();
getFullFields(datasourceValue, finded.name, {
Expand Down
10 changes: 6 additions & 4 deletions src/pages/explorer/Elasticsearch/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Field } from './utils';
import { getColumnsFromFields } from './utils/getColumnsFromFields';
import LogView from './LogView';

import useFieldConfig from '../components/RenderValue/useFieldConfig';
interface Props {
data: any[];
onChange: (pagination, filters, sorter, extra) => void;
Expand All @@ -16,9 +16,11 @@ interface Props {
function Table(props: Props) {
const { data, onChange, getFields, selectedFields } = props;
const form = Form.useFormInstance();
const indexPatternId = Form.useWatch(['query', 'indexPattern']);
const fieldConfig = useFieldConfig('elasticsearch', indexPatternId);
const columns = useMemo(() => {
return getColumnsFromFields(selectedFields, form.getFieldValue(['query']), form.getFieldValue(['fieldConfig']));
}, [selectedFields]);
return getColumnsFromFields(selectedFields, form.getFieldValue(['query']), fieldConfig);
}, [selectedFields, fieldConfig]);

return (
<AntdTable
Expand All @@ -30,7 +32,7 @@ function Table(props: Props) {
dataSource={data}
expandable={{
expandedRowRender: (record) => {
return <LogView value={record.json} fieldConfig={form.getFieldValue(['fieldConfig'])} fields={getFields()} highlight={record.highlight} range={form.getFieldValue(['query','range'])} />;
return <LogView value={record.json} fieldConfig={fieldConfig} fields={getFields()} highlight={record.highlight} range={form.getFieldValue(['query', 'range'])} />;
},
expandIcon: ({ expanded, onExpand, record }) => (expanded ? <DownOutlined onClick={(e) => onExpand(record, e)} /> : <RightOutlined onClick={(e) => onExpand(record, e)} />),
}}
Expand Down
58 changes: 34 additions & 24 deletions src/pages/explorer/Elasticsearch/utils/getColumnsFromFields.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import _ from 'lodash';
import purify from 'dompurify';
import { getFieldLabel, getFieldValue, RenderValue } from './index';
import { getFieldLabel } from './index';
import { getHighlightHtml } from './highlight';
import RenderValue from '@/pages/explorer/components/RenderValue';

export const typeMap: Record<string, string> = {
float: 'number',
Expand Down Expand Up @@ -50,17 +51,24 @@ export function getColumnsFromFields(selectedFields: { name: string; type: strin
{_.map(_.slice(fieldKeys, 0, 20), (key) => {
const val = fields[key];
const label = getFieldLabel(key, fieldConfig);
if (!_.isPlainObject(val) && fieldConfig?.formatMap?.[key]) {
const value = getFieldValue(key, val, fieldConfig, record.json, range);
return (
<React.Fragment key={label}>
<dt>{label}:</dt> <dd>{value}</dd>
</React.Fragment>
);
}
return (
<React.Fragment key={label}>
<dt>{label}:</dt> <dd dangerouslySetInnerHTML={{ __html: purify.sanitize(getHighlightHtml(val, highlight?.[key])) }}></dd>
<dt>{label}:</dt>{' '}
<dd>
<RenderValue
fieldKey={key}
fieldValue={val}
fieldConfig={fieldConfig}
rawValue={record.json}
range={range}
adjustFieldValue={(formatedValue) => {
if (highlight?.[key]) {
return <span dangerouslySetInnerHTML={{ __html: purify.sanitize(getHighlightHtml(formatedValue, highlight[key])) }} />;
}
return formatedValue;
}}
/>
</dd>
</React.Fragment>
);
})}
Expand All @@ -79,9 +87,21 @@ export function getColumnsFromFields(selectedFields: { name: string; type: strin
key: fieldKey,
render: (fields, record) => {
const { highlight } = record;
const fieldVal = getFieldValue(item.name, fields[fieldKey], fieldConfig, record.json, range);
const value = _.isArray(fieldVal) ? _.join(fieldVal, ',') : fieldVal;
return <RenderValue value={value} highlights={highlight?.[fieldKey]} />;
return (
<RenderValue
fieldKey={item.name}
fieldValue={fields[item.name]}
fieldConfig={fieldConfig}
rawValue={record.json}
range={range}
adjustFieldValue={(formatedValue) => {
if (highlight?.[item.name]) {
return <span dangerouslySetInnerHTML={{ __html: purify.sanitize(getHighlightHtml(formatedValue, highlight[item.name])) }} />;
}
return formatedValue;
}}
/>
);
},
sorter: _.includes(['date', 'number'], typeMap[item.type])
? {
Expand All @@ -99,17 +119,7 @@ export function getColumnsFromFields(selectedFields: { name: string; type: strin
key: dateField,
width: 200,
render: (fields, record) => {
const format = fieldConfig?.formatMap?.[dateField];
return getFieldValue(dateField, fields[dateField], {
formatMap: {
[dateField]: {
type: 'date',
params: {
pattern: format?.params?.pattern || 'YYYY-MM-DD HH:mm:ss',
},
},
},
});
return <RenderValue fieldKey={dateField} fieldValue={fields[dateField]} fieldConfig={fieldConfig} rawValue={record.json} range={range} />;
},
defaultSortOrder: 'descend',
sorter: {
Expand Down
Loading