Transformers: Support inner vs outer join (#53913)

This commit is contained in:
Ryan McKinley
2022-08-23 10:14:03 -07:00
committed by GitHub
parent 1766ea9fdf
commit 1d4e01f8ba
18 changed files with 1183 additions and 125 deletions

View File

@@ -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,

View File

@@ -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 () => {

View File

@@ -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);
}

View File

@@ -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',

View File

@@ -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',

View File

@@ -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) {

View File

@@ -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(() => {

View File

@@ -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 {
/**

View File

@@ -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,