import { LATEST_VERSION } from '@terascope/data-types' ; import { DataTypeFieldConfig , Maybe , DataTypeVersion , SortOrder , DataTypeFields , FieldType , ReadonlyDataTypeFields } from '@terascope/types' ; import { Builder } from '../builder' ; import { SerializeOptions , Vector } from '../vector' ; import { ColumnConfig , ColumnOptions } from './interfaces' ; import { runVectorAggregation , ValueAggregation } from './aggregations' ; import { getVectorId } from './utils' ; import { ReadableData , WritableData } from '../core' ; type NameType = ( number | string | symbol ) ; /**
 * A single column of values with the same data type.
 *
 * Changing the values is safe as long the length doesn't change.
 * When adding or removing values it is better to create a new Column.
*/ export class Column < T = unknown , N extends NameType = string > { /**
     * Create a Column from an array of values
    */ static fromJSON < R , F extends NameType = string > ( name , config , values ? , version ? , childConfig ? ) { const builder = Builder . make < R > ( new WritableData ( values ?. length ?? 0 ) , { childConfig , config , name : name as string , } ) ; values ?. forEach ( ( val ) => builder . append ( val ) ) ; return new Column < any , F > ( builder . toVector ( ) , { name , version } ) ; } /**
     * Create a Column from the custom serialized format
    */ static deserialize < R , F extends NameType = string > ( columnConfig ) { const config = JSON . parse ( columnConfig as string ) as ColumnConfig < R > ; const vector = vectorFromColumnJSON < R > ( config ) ; return new Column < any , F > ( vector , { name : config . name as F , version : config . version } ) ; } /**
     * The field name for the column
    */ name : N ; /**
     * The DataType version to use for the field definition
    */ readonly version : DataTypeVersion ; readonly vector : Vector < T > ; /**
     * Get the Data Type field configuration.
    */ readonly config : Readonly < DataTypeFieldConfig > ; constructor ( vector , options ) { this . vector = vector ; this . name = options . name ; this . version = options . version ?? LATEST_VERSION ; this . config = this . vector . config ; } /**
     * Iterate over each value, this is returned in the stored value format.
     * And may not be compatible with the JSON spec.
    */ * [ Symbol . iterator ] ( ) { yield * this . vector ; } /**
     * Returns the size of the column
    */ get size ( ) : number { return this . vector . size ; } /**
     * A Unique ID for the Column.
     * The ID should only change if the data vector changes.
    */ get id ( ) : string { return getVectorId ( this . vector ) ; } /**
     * Rename the column
    */ rename < K extends NameType > ( name ) { const col = this . fork ( this . vector . fork ( this . vector . data ) ) as unknown as Column < T , K > ; col . name = name ; col . vector . name = name as string ; return col ; } /**
     * Create a new Column with the same metadata but with different data
    */ fork ( vector ) { return new Column < T , N > ( vector , { name : this . name , version : this . version , } ) ; } /**
     * Sort the column
    */ sort ( direction ? ) { const sortedIndices = Vector . getSortedIndices ( [ { vector : this . vector , direction : direction || 'asc' } ] ) ; const len = sortedIndices . length ; const builder = Builder . make < T > ( new WritableData ( len ) , { childConfig : this . vector . childConfig , config : this . vector . config , name : this . name as string , } ) ; for ( let i = 0 ; i < len ; i ++ ) { const moveTo = sortedIndices [ i ] ; const val = this . vector . get ( i ) ; builder . set ( moveTo , val ) ; } return this . fork ( builder . toVector ( ) ) ; } example unique ( ) { const writable = new WritableData < T > ( this . size ) ; for ( const [ index , value ] of this . vector . unique ( ) ) { writable . set ( index , value ) ; } return this . fork ( this . vector . fork ( [ new ReadableData ( writable ) ] ) ) ; } /**
     * Average all of the values in the Column
    */ avg ( ) { return runVectorAggregation ( this . vector , ValueAggregation . avg ) ; } /**
     * Sum all of the values in the Column
    */ sum ( ) { return runVectorAggregation ( this . vector , ValueAggregation . sum ) ; } /**
     * Find the minimum value in the Column
    */ min ( ) { return runVectorAggregation ( this . vector , ValueAggregation . min ) ; } /**
     * Find the maximum value in the Column
    */ max ( ) { return runVectorAggregation ( this . vector , ValueAggregation . max ) ; } /**
     * Select the child fields with in a given vector.
     * This involves recreating the whole column in
     * most cases.
    */ selectSubFields ( fields ) { if ( ! this . vector . childConfig ) return this ; const childConfig = { } ; for ( const field of fields ) { if ( this . vector . childConfig [ field ] ) { childConfig [ field ] = this . vector . childConfig [ field ] ; } let parentField = '' ; for ( const part of field . split ( '.' ) ) { const dotPrefix = parentField ? '.' : '' ; parentField += `${ dotPrefix }${ part }` ; if ( this . vector . childConfig [ parentField ] ?. type === FieldType . Object ) { childConfig [ parentField ] = this . vector . childConfig [ parentField ] ; } } } const existingChildFields = Object . keys ( this . vector . childConfig ) . sort ( ) ; const selectedChildFields = Object . keys ( childConfig ) . sort ( ) ; if ( selectedChildFields . join ( ) === existingChildFields . join ( ) ) { return this ; } const builder = Builder . make < T > ( new WritableData ( this . size ) , { config : this . config , name : this . name as string , childConfig } ) ; for ( const value of this . vector ) { builder . append ( value ) ; } return this . fork ( builder . toVector ( ) ) ; } note toJSON ( options ? ) { return this . vector . toJSON ( options ) ; } serialize ( ) { const column = { name : `${ this . name }` , size : this . size , version : this . version , config : this . vector . config , childConfig : this . vector . childConfig , values : this . vector . toJSON ( ) , } ; return JSON . stringify ( column ) ; } /**
     * return an empty column with the same size and metadata as the previous one
    */ clearAll ( ) { return this . fork ( this . vector . fork ( [ new ReadableData ( new WritableData ( this . size ) ) ] ) ) ; } } function vectorFromColumnJSON < T > ( config ) { const builder = Builder . make < T > ( new WritableData ( config . size ) , { childConfig : config . childConfig , config : config . config , name : config . name , } ) ; for ( const value of config . values ) { builder . append ( value ) ; } return builder . toVector ( ) ; }