2022-04-22 21:33:13 +08:00
|
|
|
import { css } from '@emotion/css';
|
2023-03-28 14:59:39 +08:00
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
import { SemVer } from 'semver';
|
2022-04-22 21:33:13 +08:00
|
|
|
|
2022-01-14 22:58:25 +08:00
|
|
|
import { getDefaultTimeRange, GrafanaTheme2, QueryEditorProps } from '@grafana/data';
|
2022-05-05 22:16:34 +08:00
|
|
|
import { Alert, InlineField, InlineLabel, Input, QueryField, useStyles2 } from '@grafana/ui';
|
2022-04-22 21:33:13 +08:00
|
|
|
|
2020-12-04 22:29:40 +08:00
|
|
|
import { ElasticDatasource } from '../../datasource';
|
2022-04-22 21:33:13 +08:00
|
|
|
import { useNextId } from '../../hooks/useNextId';
|
|
|
|
|
import { useDispatch } from '../../hooks/useStatelessReducer';
|
2020-12-04 22:29:40 +08:00
|
|
|
import { ElasticsearchOptions, ElasticsearchQuery } from '../../types';
|
2023-03-28 14:59:39 +08:00
|
|
|
import { isSupportedVersion, unsupportedVersionMessage } from '../../utils';
|
2022-04-22 21:33:13 +08:00
|
|
|
|
|
|
|
|
import { BucketAggregationsEditor } from './BucketAggregationsEditor';
|
2020-12-04 22:29:40 +08:00
|
|
|
import { ElasticsearchProvider } from './ElasticsearchQueryContext';
|
|
|
|
|
import { MetricAggregationsEditor } from './MetricAggregationsEditor';
|
2021-05-18 21:40:53 +08:00
|
|
|
import { metricAggregationConfig } from './MetricAggregationsEditor/utils';
|
2022-04-22 21:33:13 +08:00
|
|
|
import { changeAliasPattern, changeQuery } from './state';
|
2020-12-04 22:29:40 +08:00
|
|
|
|
|
|
|
|
export type ElasticQueryEditorProps = QueryEditorProps<ElasticDatasource, ElasticsearchQuery, ElasticsearchOptions>;
|
|
|
|
|
|
2023-03-28 14:59:39 +08:00
|
|
|
// a react hook that returns the elasticsearch database version,
|
|
|
|
|
// or `null`, while loading, or if it is not possible to determine the value.
|
|
|
|
|
function useElasticVersion(datasource: ElasticDatasource): SemVer | null {
|
|
|
|
|
const [version, setVersion] = useState<SemVer | null>(null);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
let canceled = false;
|
|
|
|
|
datasource.getDatabaseVersion().then(
|
|
|
|
|
(version) => {
|
|
|
|
|
if (!canceled) {
|
|
|
|
|
setVersion(version);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
(error) => {
|
|
|
|
|
// we do nothing
|
|
|
|
|
console.log(error);
|
|
|
|
|
}
|
2022-05-05 22:16:34 +08:00
|
|
|
);
|
2023-03-28 14:59:39 +08:00
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
canceled = true;
|
|
|
|
|
};
|
|
|
|
|
}, [datasource]);
|
|
|
|
|
|
|
|
|
|
return version;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range }: ElasticQueryEditorProps) => {
|
|
|
|
|
const elasticVersion = useElasticVersion(datasource);
|
|
|
|
|
const showUnsupportedMessage = elasticVersion != null && !isSupportedVersion(elasticVersion);
|
2022-05-05 22:16:34 +08:00
|
|
|
return (
|
|
|
|
|
<ElasticsearchProvider
|
|
|
|
|
datasource={datasource}
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
onRunQuery={onRunQuery}
|
|
|
|
|
query={query}
|
|
|
|
|
range={range || getDefaultTimeRange()}
|
|
|
|
|
>
|
2023-03-28 14:59:39 +08:00
|
|
|
{showUnsupportedMessage && <Alert title={unsupportedVersionMessage} />}
|
2022-05-05 22:16:34 +08:00
|
|
|
<QueryEditorForm value={query} />
|
|
|
|
|
</ElasticsearchProvider>
|
|
|
|
|
);
|
|
|
|
|
};
|
2020-12-04 22:29:40 +08:00
|
|
|
|
2022-01-14 22:58:25 +08:00
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({
|
|
|
|
|
root: css`
|
|
|
|
|
display: flex;
|
|
|
|
|
`,
|
|
|
|
|
queryFieldWrapper: css`
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
margin: 0 ${theme.spacing(0.5)} ${theme.spacing(0.5)} 0;
|
|
|
|
|
`,
|
|
|
|
|
});
|
|
|
|
|
|
2020-12-04 22:29:40 +08:00
|
|
|
interface Props {
|
|
|
|
|
value: ElasticsearchQuery;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-31 16:26:40 +08:00
|
|
|
export const ElasticSearchQueryField = ({ value, onChange }: { value?: string; onChange: (v: string) => void }) => {
|
|
|
|
|
const styles = useStyles2(getStyles);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={styles.queryFieldWrapper}>
|
|
|
|
|
<QueryField
|
|
|
|
|
query={value}
|
|
|
|
|
// By default QueryField calls onChange if onBlur is not defined, this will trigger a rerender
|
|
|
|
|
// And slate will claim the focus, making it impossible to leave the field.
|
|
|
|
|
onBlur={() => {}}
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
placeholder="Lucene Query"
|
|
|
|
|
portalOrigin="elasticsearch"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-04 23:23:19 +08:00
|
|
|
const QueryEditorForm = ({ value }: Props) => {
|
2020-12-04 22:29:40 +08:00
|
|
|
const dispatch = useDispatch();
|
|
|
|
|
const nextId = useNextId();
|
2022-01-14 22:58:25 +08:00
|
|
|
const styles = useStyles2(getStyles);
|
2020-12-04 22:29:40 +08:00
|
|
|
|
2021-04-02 18:06:34 +08:00
|
|
|
// To be considered a time series query, the last bucked aggregation must be a Date Histogram
|
2022-05-31 16:26:40 +08:00
|
|
|
const isTimeSeriesQuery = value?.bucketAggs?.slice(-1)[0]?.type === 'date_histogram';
|
2021-04-02 18:06:34 +08:00
|
|
|
|
2021-05-18 21:40:53 +08:00
|
|
|
const showBucketAggregationsEditor = value.metrics?.every(
|
|
|
|
|
(metric) => !metricAggregationConfig[metric.type].isSingleMetric
|
|
|
|
|
);
|
|
|
|
|
|
2020-12-04 22:29:40 +08:00
|
|
|
return (
|
|
|
|
|
<>
|
2022-01-14 22:58:25 +08:00
|
|
|
<div className={styles.root}>
|
|
|
|
|
<InlineLabel width={17}>Query</InlineLabel>
|
2022-05-31 16:26:40 +08:00
|
|
|
<ElasticSearchQueryField onChange={(query) => dispatch(changeQuery(query))} value={value?.query} />
|
|
|
|
|
|
2021-04-02 18:06:34 +08:00
|
|
|
<InlineField
|
|
|
|
|
label="Alias"
|
|
|
|
|
labelWidth={15}
|
|
|
|
|
disabled={!isTimeSeriesQuery}
|
|
|
|
|
tooltip="Aliasing only works for timeseries queries (when the last group is 'Date Histogram'). For all other query types this field is ignored."
|
|
|
|
|
>
|
2021-02-09 21:55:42 +08:00
|
|
|
<Input
|
|
|
|
|
id={`ES-query-${value.refId}_alias`}
|
|
|
|
|
placeholder="Alias Pattern"
|
|
|
|
|
onBlur={(e) => dispatch(changeAliasPattern(e.currentTarget.value))}
|
|
|
|
|
defaultValue={value.alias}
|
|
|
|
|
/>
|
2020-12-04 22:29:40 +08:00
|
|
|
</InlineField>
|
2022-01-14 22:58:25 +08:00
|
|
|
</div>
|
2020-12-04 22:29:40 +08:00
|
|
|
|
|
|
|
|
<MetricAggregationsEditor nextId={nextId} />
|
2021-05-18 21:40:53 +08:00
|
|
|
{showBucketAggregationsEditor && <BucketAggregationsEditor nextId={nextId} />}
|
2020-12-04 22:29:40 +08:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|