DataFrame: Handle nanosecond-precision timestamp fields (#64529)

handle nanosec-precision timestamps
This commit is contained in:
Gábor Farkas 2023-04-06 09:14:21 +02:00 committed by GitHub
parent 45df536263
commit c4c406aacd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 144 additions and 5 deletions

View File

@ -1,6 +1,7 @@
import { FieldType } from '../types/dataFrame';
import { ArrayVector } from '..';
import { DataFrame, FieldType } from '../types/dataFrame';
import { DataFrameJSON, dataFrameFromJSON } from './DataFrameJSON';
import { DataFrameJSON, dataFrameFromJSON, dataFrameToJSON } from './DataFrameJSON';
describe('DataFrame JSON', () => {
describe('when called with a DataFrame', () => {
@ -137,5 +138,104 @@ describe('DataFrame JSON', () => {
}
`);
});
it('should decode fields with nanos', () => {
const json: DataFrameJSON = {
schema: {
fields: [
{ name: 'time1', type: FieldType.time },
{ name: 'time2', type: FieldType.time },
],
},
data: {
values: [
[1, 2, 3],
[4, 5, 6],
],
nanos: [null, [7, 8, 9]],
},
};
const frame = dataFrameFromJSON(json);
expect(frame).toMatchInlineSnapshot(`
{
"fields": [
{
"config": {},
"entities": {},
"name": "time1",
"type": "time",
"values": [
1,
2,
3,
],
},
{
"config": {},
"entities": {},
"name": "time2",
"nanos": [
7,
8,
9,
],
"type": "time",
"values": [
4,
5,
6,
],
},
],
"length": 3,
}
`);
});
it('should encode fields with nanos', () => {
const inputFrame: DataFrame = {
refId: 'A',
meta: {},
name: 'f1',
fields: [
{
name: 'time1',
type: FieldType.time,
config: {},
values: new ArrayVector([11, 12, 13]),
},
{
name: 'time2',
type: FieldType.time,
config: {},
values: new ArrayVector([14, 15, 16]),
nanos: [17, 18, 19],
},
],
length: 3,
};
const expectedJSON: DataFrameJSON = {
schema: {
fields: [
{ name: 'time1', type: FieldType.time, config: {} },
{ name: 'time2', type: FieldType.time, config: {} },
],
meta: {},
name: 'f1',
refId: 'A',
},
data: {
nanos: [null, [17, 18, 19]],
values: [
[11, 12, 13],
[14, 15, 16],
],
},
};
expect(dataFrameToJSON(inputFrame)).toStrictEqual(expectedJSON);
});
});
});

View File

@ -1,4 +1,4 @@
import { DataFrame, FieldType, FieldConfig, Labels, QueryResultMeta } from '../types';
import { DataFrame, FieldType, FieldConfig, Labels, QueryResultMeta, Field } from '../types';
import { ArrayVector } from '../vector';
import { guessFieldTypeFromNameAndValue } from './processDataFrame';
@ -56,6 +56,13 @@ export interface DataFrameData {
* NOTE: currently only decoding is implemented
*/
enums?: Array<string[] | null>;
/**
* Holds integers between 0 and 999999, used by time-fields
* to store the nanosecond-precision that cannot be represented
* by the millisecond-based base value.
*/
nanos?: Array<number[] | null>;
}
/**
@ -188,9 +195,11 @@ export function dataFrameFromJSON(dto: DataFrameJSON): DataFrame {
type = FieldType.string;
}
const nanos = data?.nanos?.[index];
// TODO: expand arrays further using bases,factors
return {
const dataFrameField: Field & { entities: FieldValueEntityLookup } = {
...f,
type: type ?? guessFieldType(f.name, buffer),
config: f.config ?? {},
@ -198,6 +207,12 @@ export function dataFrameFromJSON(dto: DataFrameJSON): DataFrame {
// the presence of this prop is an optimization signal & lookup for consumers
entities: entities ?? {},
};
if (nanos != null) {
dataFrameField.nanos = nanos;
}
return dataFrameField;
});
return {
@ -216,18 +231,34 @@ export function dataFrameToJSON(frame: DataFrame): DataFrameJSON {
const data: DataFrameData = {
values: [],
};
const allNanos: Array<number[] | null> = [];
let hasNanos = false;
const schema: DataFrameSchema = {
refId: frame.refId,
meta: frame.meta,
name: frame.name,
fields: frame.fields.map((f) => {
const { values, state, display, ...sfield } = f;
const { values, nanos, state, display, ...sfield } = f;
delete (sfield as any).entities;
data.values.push(values.toArray());
if (nanos != null) {
allNanos.push(nanos);
hasNanos = true;
} else {
allNanos.push(null);
}
return sfield;
}),
};
if (hasNanos) {
data.nanos = allNanos;
}
return {
schema,
data,

View File

@ -137,6 +137,14 @@ export interface Field<T = any, V = Vector<T>> {
*/
config: FieldConfig;
values: V; // The raw field values
/**
* When type === FieldType.Time, this can optionally store
* the nanosecond-precison fractions as integers between
* 0 and 999999.
*/
nanos?: number[];
labels?: Labels;
/**