mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transformers: Support inner vs outer join (#53913)
This commit is contained in:
@@ -9,6 +9,7 @@ import { filterByValueTransformer } from './transformers/filterByValue';
|
||||
import { groupByTransformer } from './transformers/groupBy';
|
||||
import { groupingToMatrixTransformer } from './transformers/groupingToMatrix';
|
||||
import { histogramTransformer } from './transformers/histogram';
|
||||
import { joinByFieldTransformer } from './transformers/joinByField';
|
||||
import { labelsToFieldsTransformer } from './transformers/labelsToFields';
|
||||
import { limitTransformer } from './transformers/limit';
|
||||
import { mergeTransformer } from './transformers/merge';
|
||||
@@ -18,7 +19,6 @@ import { organizeFieldsTransformer } from './transformers/organize';
|
||||
import { reduceTransformer } from './transformers/reduce';
|
||||
import { renameFieldsTransformer } from './transformers/rename';
|
||||
import { renameByRegexTransformer } from './transformers/renameByRegex';
|
||||
import { seriesToColumnsTransformer } from './transformers/seriesToColumns';
|
||||
import { seriesToRowsTransformer } from './transformers/seriesToRows';
|
||||
import { sortByTransformer } from './transformers/sortBy';
|
||||
|
||||
@@ -34,7 +34,9 @@ export const standardTransformers = {
|
||||
reduceTransformer,
|
||||
concatenateTransformer,
|
||||
calculateFieldTransformer,
|
||||
seriesToColumnsTransformer,
|
||||
joinByFieldTransformer,
|
||||
/** @deprecated */
|
||||
seriesToColumnsTransformer: joinByFieldTransformer,
|
||||
seriesToRowsTransformer,
|
||||
renameFieldsTransformer,
|
||||
labelsToFieldsTransformer,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { transformDataFrame } from '../transformDataFrame';
|
||||
|
||||
import { ensureColumnsTransformer } from './ensureColumns';
|
||||
import { DataTransformerID } from './ids';
|
||||
import { seriesToColumnsTransformer } from './seriesToColumns';
|
||||
import { joinByFieldTransformer } from './joinByField';
|
||||
|
||||
const seriesA = toDataFrame({
|
||||
fields: [
|
||||
@@ -33,7 +33,7 @@ const seriesNoTime = toDataFrame({
|
||||
|
||||
describe('ensureColumns transformer', () => {
|
||||
beforeAll(() => {
|
||||
mockTransformationsRegistry([ensureColumnsTransformer, seriesToColumnsTransformer]);
|
||||
mockTransformationsRegistry([ensureColumnsTransformer, joinByFieldTransformer]);
|
||||
});
|
||||
|
||||
it('will transform to columns if time field exists and multiple frames', async () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DataFrame } from '../../types/dataFrame';
|
||||
import { SynchronousDataTransformerInfo } from '../../types/transformations';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { seriesToColumnsTransformer } from './seriesToColumns';
|
||||
import { joinByFieldTransformer } from './joinByField';
|
||||
|
||||
export const ensureColumnsTransformer: SynchronousDataTransformerInfo = {
|
||||
id: DataTransformerID.ensureColumns,
|
||||
@@ -19,7 +19,7 @@ export const ensureColumnsTransformer: SynchronousDataTransformerInfo = {
|
||||
const timeFieldName = findConsistentTimeFieldName(frames);
|
||||
|
||||
if (frames.length > 1 && timeFieldName) {
|
||||
return seriesToColumnsTransformer.transformer({
|
||||
return joinByFieldTransformer.transformer({
|
||||
byField: timeFieldName,
|
||||
})(frames);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export enum DataTransformerID {
|
||||
// join = 'join', // Pick a field and merge all series based on that field
|
||||
append = 'append',
|
||||
// rotate = 'rotate', // Columns to rows
|
||||
reduce = 'reduce',
|
||||
@@ -7,6 +6,7 @@ export enum DataTransformerID {
|
||||
organize = 'organize',
|
||||
rename = 'rename',
|
||||
calculateField = 'calculateField',
|
||||
/** @deprecated use joinByField */
|
||||
seriesToColumns = 'seriesToColumns',
|
||||
seriesToRows = 'seriesToRows',
|
||||
merge = 'merge',
|
||||
@@ -30,6 +30,7 @@ export enum DataTransformerID {
|
||||
fieldLookup = 'fieldLookup',
|
||||
heatmap = 'heatmap',
|
||||
spatial = 'spatial',
|
||||
joinByField = 'joinByField',
|
||||
joinByLabels = 'joinByLabels',
|
||||
extractFields = 'extractFields',
|
||||
groupingToMatrix = 'groupingToMatrix',
|
||||
|
||||
@@ -5,11 +5,11 @@ import { ArrayVector } from '../../vector';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { JoinMode, SeriesToColumnsOptions, seriesToColumnsTransformer } from './seriesToColumns';
|
||||
import { JoinMode, JoinByFieldOptions, joinByFieldTransformer } from './joinByField';
|
||||
|
||||
describe('SeriesToColumns Transformer', () => {
|
||||
describe('JOIN Transformer', () => {
|
||||
beforeAll(() => {
|
||||
mockTransformationsRegistry([seriesToColumnsTransformer]);
|
||||
mockTransformationsRegistry([joinByFieldTransformer]);
|
||||
});
|
||||
|
||||
describe('outer join', () => {
|
||||
@@ -32,7 +32,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('joins by time field', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -134,7 +134,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('joins by temperature field', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'temperature',
|
||||
@@ -250,7 +250,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('joins by time field in reverse order', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -375,7 +375,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('when dataframe and field share the same name then use the field name', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -438,7 +438,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('joins if fields are missing', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -516,7 +516,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('handles duplicate field name', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -597,7 +597,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('inner joins by time field', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -678,7 +678,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('inner joins by temperature field', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'temperature',
|
||||
@@ -763,7 +763,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('inner joins by time field in reverse order', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -867,7 +867,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('when dataframe and field share the same name then use the field name', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -931,7 +931,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('joins if fields are missing', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -1010,7 +1010,7 @@ describe('SeriesToColumns Transformer', () => {
|
||||
});
|
||||
|
||||
it('handles duplicate field name', async () => {
|
||||
const cfg: DataTransformerConfig<SeriesToColumnsOptions> = {
|
||||
const cfg: DataTransformerConfig<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
options: {
|
||||
byField: 'time',
|
||||
@@ -12,23 +12,25 @@ export enum JoinMode {
|
||||
inner = 'inner',
|
||||
}
|
||||
|
||||
export interface SeriesToColumnsOptions {
|
||||
export interface JoinByFieldOptions {
|
||||
byField?: string; // empty will pick the field automatically
|
||||
mode?: JoinMode;
|
||||
}
|
||||
|
||||
export const seriesToColumnsTransformer: SynchronousDataTransformerInfo<SeriesToColumnsOptions> = {
|
||||
id: DataTransformerID.seriesToColumns,
|
||||
name: 'Series as columns', // Called 'Outer join' in the UI!
|
||||
description: 'Groups series by field and returns values as columns',
|
||||
export const joinByFieldTransformer: SynchronousDataTransformerInfo<JoinByFieldOptions> = {
|
||||
id: DataTransformerID.joinByField,
|
||||
aliasIds: [DataTransformerID.seriesToColumns],
|
||||
name: 'Join by field',
|
||||
description:
|
||||
'Combine rows from two or more tables, based on a related field between them. This can be used to outer join multiple time series on the _time_ field to show many time series in one table.',
|
||||
defaultOptions: {
|
||||
byField: undefined, // DEFAULT_KEY_FIELD,
|
||||
mode: JoinMode.outer,
|
||||
},
|
||||
|
||||
operator: (options) => (source) => source.pipe(map((data) => seriesToColumnsTransformer.transformer(options)(data))),
|
||||
operator: (options) => (source) => source.pipe(map((data) => joinByFieldTransformer.transformer(options)(data))),
|
||||
|
||||
transformer: (options: SeriesToColumnsOptions) => {
|
||||
transformer: (options: JoinByFieldOptions) => {
|
||||
let joinBy: FieldMatcher | undefined = undefined;
|
||||
return (data: DataFrame[]) => {
|
||||
if (data.length > 1) {
|
||||
@@ -4,8 +4,8 @@ import { mockTransformationsRegistry } from '../../utils/tests/mockTransformatio
|
||||
import { ArrayVector } from '../../vector';
|
||||
|
||||
import { calculateFieldTransformer } from './calculateField';
|
||||
import { JoinMode } from './joinByField';
|
||||
import { isLikelyAscendingVector, joinDataFrames } from './joinDataFrames';
|
||||
import { JoinMode } from './seriesToColumns';
|
||||
|
||||
describe('align frames', () => {
|
||||
beforeAll(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ArrayVector } from '../../vector';
|
||||
import { fieldMatchers } from '../matchers';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
|
||||
import { JoinMode } from './seriesToColumns';
|
||||
import { JoinMode } from './joinByField';
|
||||
|
||||
export function pickBestJoinField(data: DataFrame[]): FieldMatcher {
|
||||
const { timeField } = getTimeField(data[0]);
|
||||
@@ -34,7 +34,7 @@ export function pickBestJoinField(data: DataFrame[]): FieldMatcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @internal
|
||||
*/
|
||||
export interface JoinOptions {
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ export const mockTransformationsRegistry = (transformers: Array<DataTransformerI
|
||||
return transformers.map((t) => {
|
||||
return {
|
||||
id: t.id,
|
||||
aliasIds: t.aliasIds,
|
||||
name: t.name,
|
||||
transformation: t,
|
||||
description: t.description,
|
||||
|
||||
Reference in New Issue
Block a user