mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Elasticsearch: Guess field type from first non-empty value (#32290)
This commit is contained in:
parent
5ce9fa360d
commit
85a6544198
@ -451,7 +451,7 @@ export class ElasticResponse {
|
|||||||
const { propNames, docs } = flattenHits(response.hits.hits);
|
const { propNames, docs } = flattenHits(response.hits.hits);
|
||||||
if (docs.length > 0) {
|
if (docs.length > 0) {
|
||||||
let series = createEmptyDataFrame(
|
let series = createEmptyDataFrame(
|
||||||
propNames,
|
propNames.map(toNameTypePair(docs)),
|
||||||
this.targets[0].timeField!,
|
this.targets[0].timeField!,
|
||||||
isLogsRequest,
|
isLogsRequest,
|
||||||
logMessageField,
|
logMessageField,
|
||||||
@ -635,7 +635,7 @@ const flattenHits = (hits: Doc[]): { docs: Array<Record<string, any>>; propNames
|
|||||||
* @param logLevelField
|
* @param logLevelField
|
||||||
*/
|
*/
|
||||||
const createEmptyDataFrame = (
|
const createEmptyDataFrame = (
|
||||||
propNames: string[],
|
props: Array<[string, FieldType]>,
|
||||||
timeField: string,
|
timeField: string,
|
||||||
isLogsRequest: boolean,
|
isLogsRequest: boolean,
|
||||||
logMessageField?: string,
|
logMessageField?: string,
|
||||||
@ -671,13 +671,13 @@ const createEmptyDataFrame = (
|
|||||||
|
|
||||||
const fieldNames = series.fields.map((field) => field.name);
|
const fieldNames = series.fields.map((field) => field.name);
|
||||||
|
|
||||||
for (const propName of propNames) {
|
for (const [name, type] of props) {
|
||||||
// Do not duplicate fields. This can mean that we will shadow some fields.
|
// Do not duplicate fields. This can mean that we will shadow some fields.
|
||||||
if (fieldNames.includes(propName)) {
|
if (fieldNames.includes(name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Do not add _source field (besides logs) as we are showing each _source field in table instead.
|
// Do not add _source field (besides logs) as we are showing each _source field in table instead.
|
||||||
if (!isLogsRequest && propName === '_source') {
|
if (!isLogsRequest && name === '_source') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,8 +685,8 @@ const createEmptyDataFrame = (
|
|||||||
config: {
|
config: {
|
||||||
filterable: true,
|
filterable: true,
|
||||||
},
|
},
|
||||||
name: propName,
|
name,
|
||||||
type: FieldType.string,
|
type,
|
||||||
}).parse = (v: any) => {
|
}).parse = (v: any) => {
|
||||||
return v || '';
|
return v || '';
|
||||||
};
|
};
|
||||||
@ -705,3 +705,23 @@ const addPreferredVisualisationType = (series: any, type: PreferredVisualisation
|
|||||||
|
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toNameTypePair = (docs: Array<Record<string, any>>) => (propName: string): [string, FieldType] => [
|
||||||
|
propName,
|
||||||
|
guessType(docs.find((doc) => doc[propName] !== undefined)?.[propName]),
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trying to guess data type from its value. This is far from perfect, as in order to have accurate guess
|
||||||
|
* we should have access to the elasticsearch mapping, but it covers the most common use cases for numbers, strings & arrays.
|
||||||
|
*/
|
||||||
|
const guessType = (value: unknown): FieldType => {
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'number':
|
||||||
|
return FieldType.number;
|
||||||
|
case 'string':
|
||||||
|
return FieldType.string;
|
||||||
|
default:
|
||||||
|
return FieldType.other;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1244,6 +1244,7 @@ describe('ElasticResponse', () => {
|
|||||||
_source: {
|
_source: {
|
||||||
'@timestamp': '2019-06-24T09:51:19.765Z',
|
'@timestamp': '2019-06-24T09:51:19.765Z',
|
||||||
host: 'djisaodjsoad',
|
host: 'djisaodjsoad',
|
||||||
|
number: 1,
|
||||||
message: 'hello, i am a message',
|
message: 'hello, i am a message',
|
||||||
level: 'debug',
|
level: 'debug',
|
||||||
fields: {
|
fields: {
|
||||||
@ -1263,6 +1264,7 @@ describe('ElasticResponse', () => {
|
|||||||
_source: {
|
_source: {
|
||||||
'@timestamp': '2019-06-24T09:52:19.765Z',
|
'@timestamp': '2019-06-24T09:52:19.765Z',
|
||||||
host: 'dsalkdakdop',
|
host: 'dsalkdakdop',
|
||||||
|
number: 2,
|
||||||
message: 'hello, i am also message',
|
message: 'hello, i am also message',
|
||||||
level: 'error',
|
level: 'error',
|
||||||
fields: {
|
fields: {
|
||||||
@ -1344,5 +1346,21 @@ describe('ElasticResponse', () => {
|
|||||||
const field = fieldCache.getFieldByName('level');
|
const field = fieldCache.getFieldByName('level');
|
||||||
expect(field?.values.toArray()).toEqual(['debug', 'info']);
|
expect(field?.values.toArray()).toEqual(['debug', 'info']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should correctly guess field types', () => {
|
||||||
|
const result = new ElasticResponse(targets, response).getLogs();
|
||||||
|
const logResults = result.data[0] as MutableDataFrame;
|
||||||
|
|
||||||
|
const fields = logResults.fields.map((f) => {
|
||||||
|
return {
|
||||||
|
name: f.name,
|
||||||
|
type: f.type,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fields).toContainEqual({ name: '@timestamp', type: 'time' });
|
||||||
|
expect(fields).toContainEqual({ name: 'number', type: 'number' });
|
||||||
|
expect(fields).toContainEqual({ name: 'message', type: 'string' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user