Transformations: Take nanos into account for Sort by (#108564)

* chore(sortDataFrame): sort by nanos
This commit is contained in:
Galen Kistler
2025-07-24 18:51:21 +00:00
committed by GitHub
parent 0723a51e87
commit ab92e2cbb6
2 changed files with 69 additions and 9 deletions
@@ -388,6 +388,59 @@ describe('sorted DataFrame', () => {
});
});
describe('sorted DataFrame by nanos', () => {
it('Should sort nanos with numeric timestamp', () => {
const frame = toDataFrame({
fields: [
{ name: 'first', type: FieldType.time, values: [1, 1, 2, 2, 3, 3], nanos: [100, 102, 1, 2, 1000, 999] },
{ name: 'second', type: FieldType.string, values: ['a', 'b', 'c', 'd', 'e', 'f'] },
],
});
const sorted = sortDataFrame(frame, 0, true);
expect(sorted.length).toEqual(6);
expect(sorted.fields[0].values).toEqual([3, 3, 2, 2, 1, 1]);
expect(sorted.fields[1].values).toEqual(['e', 'f', 'd', 'c', 'b', 'a']);
});
it('Should sort by nanos with dateTime timestamp', () => {
const frame = toDataFrame({
fields: [
{
name: 'first',
type: FieldType.time,
values: [dateTime(50), dateTime(50), dateTime(100)],
nanos: [1, 0, 100],
},
{ name: 'second', type: FieldType.string, values: ['a', 'b', 'c'] },
],
});
const sorted = sortDataFrame(frame, 0);
expect(sorted.length).toEqual(3);
expect(sorted.fields[0].values).toEqual([dateTime(50), dateTime(50), dateTime(100)]);
expect(sorted.fields[1].values).toEqual(['b', 'a', 'c']);
});
// Not sure if we expect nanos to exist on any field type besides time fields, but the schema allows it.
// Keep in mind if sorting by a non time field, the nanos is expected to present on the field that is being sorted
it('Should sort by nanos with string timestamp', () => {
const frame = toDataFrame({
fields: [
{ name: 'fist', type: FieldType.time, values: [1, 2, 3, 3] },
{ name: 'second', type: FieldType.string, values: ['a', 'b', 'b', 'c'], nanos: [100, 0, 1, 0] },
{ name: 'third', type: FieldType.number, values: [2000, 3000, 1000] },
],
});
const sorted = sortDataFrame(frame, 1, true);
expect(sorted.length).toEqual(4);
expect(sorted.fields[0].values).toEqual([3, 2, 3, 1]);
expect(sorted.fields[0].nanos).toBeUndefined();
expect(sorted.fields[1].values).toEqual(['c', 'b', 'b', 'a']);
});
});
describe('reverse DataFrame', () => {
const frame = toDataFrame({
fields: [
@@ -17,9 +17,9 @@ export const fieldIndexComparer = (field: Field, reverse = false): IndexComparer
return booleanIndexComparer(values, reverse);
case FieldType.time:
if (typeof field.values[0] === 'number') {
return timestampIndexComparer(values, reverse);
return timestampIndexComparer(values, reverse, field.nanos);
}
return timeIndexComparer(values, reverse);
return timeIndexComparer(values, reverse, field.nanos);
default:
return naturalIndexComparer(reverse);
}
@@ -79,17 +79,24 @@ const falsyComparer = (a: unknown, b: unknown): number => {
return 0;
};
const timestampIndexComparer = (values: number[], reverse: boolean): IndexComparer => {
const timestampIndexComparer = (values: number[], reverse: boolean, nanos?: number[]): IndexComparer => {
let mult = reverse ? -1 : 1;
if (nanos !== undefined) {
return (a: number, b: number): number => mult * (values[a] - values[b] || nanos[a] - nanos[b]);
}
return (a: number, b: number): number => mult * (values[a] - values[b]);
};
const timeIndexComparer = (values: unknown[], reverse: boolean): IndexComparer => {
return (a: number, b: number): number => {
const vA = values[a];
const vB = values[b];
return reverse ? timeComparer(vB, vA) : timeComparer(vA, vB);
};
const timeIndexComparer = (values: unknown[], reverse: boolean, nanos?: number[]): IndexComparer => {
const mult = reverse ? -1 : 1;
if (nanos !== undefined) {
return (a: number, b: number): number => mult * (timeComparer(values[a], values[b]) || nanos[a] - nanos[b]);
}
return (a: number, b: number): number => mult * timeComparer(values[a], values[b]);
};
const booleanIndexComparer = (values: boolean[], reverse: boolean): IndexComparer => {