ag-charts-community
Version:
Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue
163 lines (162 loc) • 8.19 kB
TypeScript
import { type DomainWithMetadata } from 'ag-charts-core';
import type { EventsHub } from '../../core/eventsHub';
import type { ChartMode } from '../chartMode';
import type { DataGroup, DataModelOptions, GroupDatumIteratorOutput, GroupedData, ProcessedData, ProcessedDataDef, PropertyDefinition, ScopeProvider, UngroupedData } from './dataModelTypes';
import type { DataChangeDescription, DataSet } from './dataSet';
import { type SortOrder } from './sortOrder';
export * from './dataModelTypes';
export { fixNumericExtent, getMissCount, datumKeys, getPathComponents, NULL_KEY_STRING, UNDEFINED_KEY_STRING, } from './data-model/utils/helpers';
/**
* Transforms raw chart data into a structured, renderable format for series visualization.
*
* The DataModel is responsible for processing data through a multi-stage pipeline:
* - Extracting and validating data from input datasets
* - Grouping data by keys (for categorical axes or stacked series)
* - Computing aggregations (sum, average, etc.) for grouped data
* - Calculating domains (value ranges) for axes and scales
* - Managing scoped data processing for multi-series charts
* - Supporting incremental updates when data changes
*
* The class orchestrates several specialized subsystems:
* - DataExtractor: Extracts and validates raw data
* - DataGrouper: Groups data by keys
* - Aggregator: Computes aggregations over grouped data
* - DomainManager: Calculates and maintains value domains
* - IncrementalProcessor: Handles efficient data updates
*
* Performance optimizations:
*
* 1. SHARED MEMORY OPTIMIZATION (groupsUnique=true):
* When each datum has unique keys, all groups share the same datumIndices array
* containing [0], since each datum's relative offset from its group is always 0.
*
* 2. BANDED DOMAIN PROCESSING:
* Large datasets are divided into bands for efficient domain calculation.
* Only dirty bands are recalculated during incremental updates.
*
* 3. BATCH MERGING:
* Column batches with identical characteristics (keys, invalidity) are merged
* to reduce processing overhead.
*
* 4. INCREMENTAL REPROCESSING:
* When supported, only changed data is reprocessed instead of full recalculation.
*/
export declare class DataModel<D extends object, K extends keyof D & string = keyof D & string, Grouped extends boolean | undefined = undefined> {
private readonly opts;
private readonly mode;
private readonly suppressFieldDotNotation;
private readonly eventsHub?;
private readonly debug;
private readonly scopeCache;
private readonly keys;
private readonly values;
private readonly resolvers;
private readonly scopeCacheManager;
private readonly domainInitializer;
private readonly domainManager;
private readonly reducerManager;
private readonly dataExtractor;
private readonly dataGrouper;
private readonly aggregator;
private readonly incrementalProcessor;
private readonly aggregates;
private readonly groupProcessors;
private readonly propertyProcessors;
private readonly reducers;
private readonly processors;
constructor(opts: DataModelOptions<K, Grouped, true>, mode?: ChartMode, suppressFieldDotNotation?: boolean, eventsHub?: EventsHub | undefined);
resolveProcessedDataDefById(scope: ScopeProvider, searchId: string): ProcessedDataDef | never;
resolveProcessedDataIndexById(scope: ScopeProvider, searchId: string): number;
resolveKeysById<T = string>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
hasColumnById(scope: ScopeProvider, searchId: string): boolean;
resolveColumnById<T = any>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
resolveColumnNeedsValueOf(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): boolean;
resolveMissingDataCount(scope: ScopeProvider): number;
/**
* Provides a convenience iterator to iterate over all of the extract datum values in a
* specific DataGroup.
*
* @param scope to which datums should belong
* @param group containing the datums
* @param processedData containing the group
* @param groupIndex index of the group in processedData.groups
*/
forEachDatum(scope: ScopeProvider, processedData: GroupedData<any>, group: DataGroup, groupIndex: number): Generator<any, void, unknown>;
private getUniqueDataSets;
/**
* Provides a convenience iterator to iterate over all of the extracted datum values in a
* GroupedData.
*
* @param scope to which datums should belong
* @param processedData to iterate through
*/
forEachGroupDatum(scope: ScopeProvider, processedData: GroupedData<any>): Generator<GroupDatumIteratorOutput, void, unknown>;
getDomain(scope: ScopeProvider, searchId: string, type: PropertyDefinition<any>['type'], processedData: ProcessedData<K>): DomainWithMetadata<any>;
getDomainBetweenRange(scope: ScopeProvider, searchIds: string[], [i0, i1]: [number, number], processedData: ProcessedData<K>): [number, number];
getKeySortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
getColumnSortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
/**
* Get sort metadata for a key column if available.
* Returns undefined if metadata is not available, is dirty, or data is unsorted.
*/
getKeySortMetadata(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): {
sortOrder: 1 | -1 | undefined;
isUnique?: boolean;
} | undefined;
processData(sources: Map<string, DataSet<unknown>>): (Grouped extends true ? GroupedData<D> : UngroupedData<D>) | undefined;
/**
* Determines if incremental reprocessing is supported for the given data.
*
* Reprocessing is supported when:
* - For ungrouped data: No aggregates, reducers, processors, or property processors
* - For grouped data: Additionally requires:
* - groupsUnique=true (each datum has unique keys)
* - Single data source (all scopes share same DataSet)
* - No invalid keys (to maintain groups.length === columns.length invariant)
* - All group processors support reprocessing
*
* When unsupported, falls back to full reprocessing automatically.
*
* @returns true if incremental reprocessing can be used, false otherwise
*/
isReprocessingSupported(processedData: ProcessedData<D>): boolean;
reprocessData(processedData: ProcessedData<D>, dataSets?: Map<DataSet<any>, DataChangeDescription | undefined>): ProcessedData<D>;
/**
* Recomputes domains from transformed arrays.
* Uses BandedDomain optimization for continuous domains to avoid full rescans.
*/
private recomputeDomains;
private warnDataMissingProperties;
private processScopeCache;
private valueGroupIdxLookup;
private valueIdxLookup;
private extractData;
/**
* Reprocesses group processors for incremental updates.
* Only processes newly inserted groups to avoid double-processing.
* This is safe only when all group processors support reprocessing.
* Deduplicates change descriptions to avoid processing the same groups multiple times
* when multiple scopes share the same DataSet.
*/
private reprocessGroupProcessors;
private postProcessProperties;
private reduceData;
private shouldUseReducerBanding;
private reduceWithBands;
private reduceStandard;
private postProcessData;
private initDataDomainProcessor;
/**
* Collects optimization metadata for debugging purposes.
* Only called when debug mode is enabled.
*/
private collectOptimizationMetadata;
/**
* Collects reducer banding metadata for debugging purposes.
* Tracks which reducers used banding and their performance stats.
*/
private collectReducerBandingMetadata;
buildAccessors(defs: Iterable<{
property: string;
}>): Map<string, (d: any) => any>;
}