or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api

features

charts

charts.mdconditional-formatting.mdvisualizations.md
authorization.mdchangesets.mdcharts-as-code.mdcompiler.mddashboards.mddbt.mdee-features.mdformatting.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdwarehouse.md
index.md
tile.json

period-over-period.mddocs/api/features/queries/

Period-over-Period Comparison

Period-over-Period (PoP) comparison enables time-based metric comparison, allowing you to analyze changes relative to previous time periods (e.g., this month vs. last month, this year vs. last year).

Package Information

import {
  type PeriodOverPeriodComparison,
  validPeriodOverPeriodGranularities,
  periodOverPeriodGranularityLabels,
  isSupportedPeriodOverPeriodGranularity,
  POP_PREVIOUS_PERIOD_SUFFIX,
  getPopFieldId,
  getBaseFieldIdFromPop,
} from "@lightdash/common";

Capabilities

Period-over-Period Configuration

Configure period-over-period comparisons for time-based analysis.

/**
 * Configuration for period-over-period comparison
 */
interface PeriodOverPeriodComparison {
  /** Comparison type (currently only 'previousPeriod' supported) */
  type: 'previousPeriod';

  /** Time granularity for comparison (DAY, WEEK, MONTH, QUARTER, YEAR) */
  granularity: TimeFrames;

  /** Number of periods to offset (e.g., 3 for "compare to 3 months ago") */
  periodOffset?: number;

  /** Time dimension to use for comparison */
  field: {
    name: string;
    table: string;
  };
}

Usage Example:

import { type PeriodOverPeriodComparison, TimeFrames } from "@lightdash/common";

// Compare this month to last month
const monthOverMonth: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.MONTH,
  field: {
    name: 'created_at',
    table: 'orders'
  }
};

// Compare this quarter to the same quarter last year (4 quarters ago)
const quarterYearOverYear: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.QUARTER,
  periodOffset: 4,
  field: {
    name: 'order_date',
    table: 'sales'
  }
};

// Compare this week to last week
const weekOverWeek: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.WEEK,
  periodOffset: 1,
  field: {
    name: 'created_date',
    table: 'events'
  }
};

Supported Granularities

Valid time granularities for period-over-period comparisons.

/**
 * Array of valid granularities for period-over-period comparison
 */
const validPeriodOverPeriodGranularities: TimeFrames[];
// Contains: [TimeFrames.DAY, TimeFrames.WEEK, TimeFrames.MONTH, TimeFrames.QUARTER, TimeFrames.YEAR]

/**
 * Human-readable labels for each granularity
 */
const periodOverPeriodGranularityLabels: Record<TimeFrames, string>;
// Example values: { [TimeFrames.MONTH]: 'Month', [TimeFrames.QUARTER]: 'Quarter', ... }

/**
 * Check if a granularity is supported for period-over-period comparison
 * @param granularity - Time frame to check
 * @returns true if granularity is valid for PoP comparison
 */
function isSupportedPeriodOverPeriodGranularity(granularity: TimeFrames): boolean;

Usage Example:

import {
  validPeriodOverPeriodGranularities,
  periodOverPeriodGranularityLabels,
  isSupportedPeriodOverPeriodGranularity,
  TimeFrames
} from "@lightdash/common";

// Get all valid granularities
console.log(validPeriodOverPeriodGranularities);
// Output: [DAY, WEEK, MONTH, QUARTER, YEAR]

// Get label for a granularity
const label = periodOverPeriodGranularityLabels[TimeFrames.MONTH];
console.log(label); // Output: "Month"

// Validate granularity
const isValid = isSupportedPeriodOverPeriodGranularity(TimeFrames.HOUR);
console.log(isValid); // Output: false (HOUR not supported for PoP)

const isValidMonth = isSupportedPeriodOverPeriodGranularity(TimeFrames.MONTH);
console.log(isValidMonth); // Output: true

Field ID Management

Utilities for managing period-over-period field identifiers.

/**
 * Naming suffix for period-over-period comparison columns
 * This is the single source of truth for PoP column naming convention
 */
const POP_PREVIOUS_PERIOD_SUFFIX = '_previous';

/**
 * Generate the PoP field ID from a base metric field ID
 * @param baseFieldId - The field ID of the base metric (e.g., "orders_total_revenue")
 * @returns The PoP field ID (e.g., "orders_total_revenue_previous")
 */
function getPopFieldId(baseFieldId: string): string;

/**
 * Extract the base field ID from a PoP field ID
 * @param fieldId - The field ID to check
 * @returns The base field ID if this is a PoP field, null otherwise
 */
function getBaseFieldIdFromPop(fieldId: string): string | null;

Usage Example:

import {
  POP_PREVIOUS_PERIOD_SUFFIX,
  getPopFieldId,
  getBaseFieldIdFromPop
} from "@lightdash/common";

// Create PoP field ID
const baseMetric = "orders_total_revenue";
const popFieldId = getPopFieldId(baseMetric);
console.log(popFieldId); // Output: "orders_total_revenue_previous"

// Extract base field from PoP field
const popField = "sales_count_previous";
const baseField = getBaseFieldIdFromPop(popField);
console.log(baseField); // Output: "sales_count"

// Check if a field is NOT a PoP field
const regularField = "customer_name";
const notPopField = getBaseFieldIdFromPop(regularField);
console.log(notPopField); // Output: null

// Use the suffix constant
console.log(POP_PREVIOUS_PERIOD_SUFFIX); // Output: "_previous"

Integration with Metric Queries

Period-over-period comparisons work seamlessly with metric queries to enable comparative analysis:

import {
  type MetricQuery,
  type PeriodOverPeriodComparison,
  TimeFrames,
  FilterOperator,
  UnitOfTime
} from "@lightdash/common";

// Example query with period-over-period comparison
const query: MetricQuery = {
  exploreName: "sales",
  dimensions: ["sales_date"],
  metrics: ["sales_revenue"],
  filters: {
    dimensions: {
      id: "root",
      and: [{
        target: { fieldId: "sales_date" },
        operator: FilterOperator.IN_THE_PAST,
        values: [3],
        settings: { unitOfTime: UnitOfTime.months }
      }]
    }
  },
  sorts: [{ fieldId: "sales_date", descending: false }],
  limit: 1000,
  tableCalculations: []
};

// The PoP configuration would be applied by the backend
// to generate comparison columns like "sales_revenue_previous"

Common Patterns

Pattern 1: Month-over-Month Comparison

const monthOverMonth: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.MONTH,
  periodOffset: 1,
  field: {
    name: 'order_date',
    table: 'orders'
  }
};

Pattern 2: Year-over-Year Comparison

const yearOverYear: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.YEAR,
  periodOffset: 1,
  field: {
    name: 'transaction_date',
    table: 'transactions'
  }
};

Pattern 3: Week-over-Week with Multiple Period Offset

// Compare to 4 weeks ago
const fourWeekComparison: PeriodOverPeriodComparison = {
  type: 'previousPeriod',
  granularity: TimeFrames.WEEK,
  periodOffset: 4,
  field: {
    name: 'event_timestamp',
    table: 'events'
  }
};

Types Reference

import { TimeFrames } from "@lightdash/common";

// TimeFrames enum values for PoP
enum TimeFrames {
  DAY = 'DAY',
  WEEK = 'WEEK',
  MONTH = 'MONTH',
  QUARTER = 'QUARTER',
  YEAR = 'YEAR'
}