import { AnyIterable, HoistBase, PlainObject, Some } from '@xh/hoist/core'; import { Store, StoreConfig, StoreRecordIdSpec, StoreTransaction } from '../Store'; import { StoreRecord } from '../StoreRecord'; import { BucketSpec } from './BucketSpec'; import { CubeField, CubeFieldSpec } from './CubeField'; import { QueryConfig } from './Query'; import { BaseRow } from './row/BaseRow'; import { AggregateRow, BucketRow } from './row/ParentRow'; import { View } from './View'; import { ViewRowData } from './ViewRowData'; /** * Configuration for a {@link Cube}. Provide `fields` (including at least one dimension * and one or more measures with aggregators) and load data via `data` or * `Cube.loadDataAsync()`. * * See the Cube package README (`data/cube/README.md`) for aggregator options and usage patterns. * * @see Cube * @see CubeFieldSpec */ export interface CubeConfig { fields: CubeField[] | CubeFieldSpec[]; /** Default configs applied to all `CubeField`s constructed internally by this Cube. */ fieldDefaults?: Partial; /** Array of initial raw data. */ data?: PlainObject[]; /** See {@link StoreConfig.idSpec} */ idSpec?: StoreRecordIdSpec; /** See {@link StoreConfig.processRawData} */ processRawData?: (data: PlainObject) => PlainObject; /** * Additional configs for the internal {@link Store} of leaf-level records maintained by this * Cube - i.e. tuning of how that Store holds the data described by `fields` / `idSpec` above. * * Note `digestSpec` is recommended whenever the source can supply a cheap per-row digest - it * preserves record identity for unchanged rows across loads and updates, allowing connected * Views to reuse their generated rows and connected stores their records. */ store?: CubeStoreConfig; /** Convenience bucket for app-specific metadata associated with the loaded dataset. */ info?: PlainObject; /** * Optional function to be called for each aggregate node to determine if it should be "locked", * preventing drill-down into its children. */ lockFn?: LockFn; /** * Optional function to be called for each dimension during row generation to determine if the * children of that dimension should be bucketed into additional dynamic dimensions. */ bucketSpecFn?: BucketSpecFn; /** * Optional function to be called on all single child rows during view processing. * Return true to omit the row. */ omitFn?: OmitFn; } /** * Configs available for a {@link Cube}'s internal {@link Store}, via {@link CubeConfig.store}. * * Excludes configs specified on {@link CubeConfig} itself (`fields`, `data`, `idSpec`, * `processRawData`) or required by the Cube's internal representation (`freezeData`, * `idEncodesTreePath`), along with configs that do not apply to a flat store of leaf-level facts: * * - Tree loading (`loadTreeData`, `loadTreeDataFrom`, `loadRootAsSummary`) - hierarchy is produced * by `View`s from the Cube's dimensions, not loaded into its source store. * - Filtering (`filter`, `filterIncludesChildren`) - filter via `QueryConfig.filter` instead, so * that aggregations remain consistent with the facts loaded into the Cube. * - `projectionOnly` - incompatible with `Cube.modifyRecordsAsync()`, which requires a store that * supports local record modification. */ export type CubeStoreConfig = Omit; /** * Function to be called for each node to aggregate to determine if it should be "locked", * preventing drilldown into its children. If true returned for a node, no drilldown will be * allowed, and the row will be marked with a boolean "locked" property. */ export type LockFn = (row: AggregateRow | BucketRow) => boolean; /** * Function to be called for each node during row generation to determine if it should be * skipped in tree output. Useful for removing aggregates that are degenerate due to context. * Note that skipping in this way has no effect on aggregations -- all children of this node are * simply promoted to their parent node. */ export type OmitFn = (row: AggregateRow | BucketRow) => boolean; /** * Function to be called for rows making up an aggregated dimension to determine if the children of * that dimension should be dynamically bucketed into additional sub-groupings. * * An example use case would be a grouped collection of portfolio positions, where any closed * positions are identified as such by this function and bucketed into a "Closed" sub-grouping, * without having to add something like an "openClosed" dimension that would apply to all * aggregations and create an unwanted "Open" grouping. * * @param rows - the rows being checked for bucketing * @returns {@link BucketSpec} for dynamic sub-aggregations, or null to perform no bucketing. */ export type BucketSpecFn = (rows: BaseRow[]) => BucketSpec; /** * Client-side OLAP-style data structure for multi-dimensional grouping and aggregation. * * A Cube wraps a flat {@link Store} of leaf-level records and supports creating {@link View}s * via structured {@link Query} objects. Each View filters, groups, and aggregates the source * data into a hierarchical result for use in tree grids, treemaps, and other visualizations. * * Fields are defined as {@link CubeField}s - each marked as either a dimension (groupable) * or a measure with an {@link Aggregator} (e.g. SUM, AVG, MIN, MAX). Views can be transient * (run a query once) or connected for efficient, auto-updating results as source data changes. * * See the Cube package README (`data/cube/README.md`) for full documentation including * aggregator options, querying patterns, and View integration with Store/GridModel. * * @see CubeConfig * @see CubeField * @see View * @see Query * * @mcpHint multi-dimensional data store with aggregation and views */ export declare class Cube extends HoistBase { static RECORD_ID_DELIMITER: string; static isCube(obj: unknown): obj is Cube; _created: number; store: Store; lockFn: LockFn; bucketSpecFn: BucketSpecFn; omitFn: OmitFn; info: any; _connectedViews: Set; constructor({ fields, fieldDefaults, data, idSpec, processRawData, store, info, lockFn, bucketSpecFn, omitFn }: CubeConfig); /** Fields configured for this Cube. */ get fields(): CubeField[]; /** Dimension Fields configured for this Cube. */ get dimensions(): CubeField[]; /** Records loaded in to this Cube. */ get records(): StoreRecord[]; /** True if this Cube contains no data / records. */ get empty(): boolean; /** Timestamp (ms) of when the Cube data was last updated */ get lastUpdated(): number; /** Count of currently connected, auto-updating Views. */ get connectedViewCount(): number; getField(name: string): CubeField; /** * Query the cube. * * This method will return a snapshot of javascript objects representing the filtered * and aggregated data in the query. In addition to the fields specified in Query, nodes will * each contain a 'cubeLabel' and a 'cubeDimension' property. * * @param query - Config for query defining the shape of the view. * @returns data containing the results of the query as a hierarchical set of rows. */ executeQuery(query: QueryConfig): ViewRowData[]; /** * Create a dynamic {@link View} of the cube data based on a query. Unlike the static snapshot * returned by {@link Cube.executeQuery}, a View created with this method can be configured * with `connect:true` to automatically update as the underlying data in the Cube changes. * * Provide one or more `stores` to automatically populate them with the aggregated data returned * by the query, or read the returned {@link View.result} directly. * * When the returned View is no longer needed, call {@link View.destroy} (or save a reference * via an `@managed` model property) to avoid unnecessary processing. * * @param query - query to be used to construct this view. * @param stores - Stores to be automatically loaded/reloaded with View results. * @param connect - true to update View automatically when data in the underlying Cube changes. */ createView({ query, stores, connect }: { query: QueryConfig; stores?: Store[] | Store; connect?: boolean; }): View; /** True if the provided view is connected to this Cube for live updates. */ viewIsConnected(view: View): boolean; /** Cease pushing further updates to this Cube's data into a previously connected View. */ disconnectView(view: View): void; /** Connect a View to this Cube for live updates. */ connectView(view: View): void; /** * Populate this cube with a new dataset. * This method largely delegates to {@link Store.loadData} - see that method for more info. * * May also be passed a streaming source - a sync or async iterable yielding raw records - * loaded via {@link Store.loadDataAsync}, e.g. * `cube.loadDataAsync(XH.fetchNdjson({url}).lines)`. * * Note that this method will update its views asynchronously in order to avoid locking up the * browser when attached to multiple expensive views. * * @param rawData - flat array of lowest/leaf level data rows, or a streaming source of same. * @param info - optional metadata to associate with this cube/dataset. */ loadDataAsync(rawData: PlainObject[] | AnyIterable, info?: PlainObject): Promise; /** * Update this cube with incremental data set changes and/or info. * This method largely delegates to {@link Store.updateData} - see that method for more info. * * Note that this method will update its views asynchronously in order to avoid locking * up the browser when attached to multiple expensive views. * * @param rawData - data changes to process. If provided as an array, rawData will be processed * into adds and updates, with updates determined by matching existing records by ID. * @param infoUpdates - new key-value pairs to be applied to existing info on this cube. */ updateDataAsync(rawData: PlainObject[] | StoreTransaction, infoUpdates?: PlainObject): Promise; /** * Similar to `updateDataAsync`, but intended for modifying individual field values in a local * uncommitted state - i.e. when updating via an inline grid editor or similar control. Like * `updateDataAsync`, this method will update its views asynchronously. * * This method largely delegates to {@link Store.modifyRecords} - see that method for more info. * * @param modifications - field-level modifications to apply to existing * Records in this Cube. Each object in the list must have an `id` property identifying * the StoreRecord to modify, plus any other properties with updated field values to apply, * e.g. `{id: 4, quantity: 100}, {id: 5, quantity: 99, customer: 'bob'}`. */ modifyRecordsAsync(modifications: Some): Promise; /** Clear any/all data and info from this Cube. */ clearAsync(): Promise; /** * Populate the metadata associated with this cube. * @param infoUpdates - new key-value pairs to be applied to existing info on this cube. */ updateInfo(infoUpdates?: PlainObject): void; private setInfo; private parseFields; destroy(): void; }