UNPKG

arquero

Version:

Query processing and transformation of array-backed data tables.

171 lines (143 loc) 3.96 kB
import { getAggregate } from '../../op/index.js'; import { concat } from '../../util/concat.js'; import { error } from '../../util/error.js'; import { isValid } from '../../util/is-valid.js'; import { unroll } from '../../util/unroll.js'; import { ValueList } from '../../util/value-list.js'; import { Reducer } from './reducer.js'; const update = (ops, args, fn) => unroll( args, '{' + concat(ops, (_, i) => `_${i}.${fn}(${args});`) + '}', ops ); export function fieldReducer(oplist, stream) { const { ops, output } = expand(oplist, stream); const fields = oplist[0].fields; const n = fields.length; const cls = n === 0 ? FieldReducer : n === 1 ? Field1Reducer : n === 2 ? Field2Reducer : error('Unsupported field count: ' + n); // @ts-ignore return new cls(fields, ops, output, stream); } function expand(oplist, stream) { const has = {}; const ops = []; function add(name, params = []) { // check key const key = name + ':' + params; if (has[key]) return has[key]; // get op instance const def = getAggregate(name); const op = def.create(...params); // add required dependencies if (stream < 0 && def.stream) { def.stream.forEach(name => add(name, [])); } if (def.req) { def.req.forEach(name => add(name, [])); } // update state has[key] = op; ops.push(op); return op; } const output = oplist.map(item => { const op = add(item.name, item.params); op.output = item.id; return op; }); return { ops, output }; } class FieldReducer extends Reducer { constructor(fields, ops, outputs, stream) { super(outputs); this._op = ops; this._fields = fields; this._stream = !!stream; } init() { const state = { count: 0, valid: 0, stream: this._stream }; this._op.forEach(op => op.init(state)); // value list requested if (state.values) { state.list = new ValueList(); } return state; } write(state, values, index) { const op = this._outputs; const n = op.length; for (let i = 0; i < n; ++i) { values[op[i].output][index] = op[i].value(state); } return 1; } _add() { } _rem() { } add(state) { ++state.count; } rem(state) { --state.count; } } class Field1Reducer extends FieldReducer { constructor(fields, ops, outputs, stream) { super(fields, ops, outputs, stream); // unroll op invocations for performance const args = ['state', 'v1', 'v2']; this._add = update(ops, args, 'add'); this._rem = update(ops, args, 'rem'); } add(state, row, data) { const value = this._fields[0](row, data); ++state.count; if (isValid(value)) { ++state.valid; if (state.list) state.list.add(value); this._add(state, value); } } rem(state, row, data) { const value = this._fields[0](row, data); --state.count; if (isValid(value)) { --state.valid; if (state.list) state.list.rem(); this._rem(state, value); } } } class Field2Reducer extends FieldReducer { constructor(fields, ops, outputs, stream) { super(fields, ops, outputs, stream); // unroll op invocations for performance const args = ['state', 'v1', 'v2']; this._add = update(ops, args, 'add'); this._rem = update(ops, args, 'rem'); } add(state, row, data) { const value1 = this._fields[0](row, data); const value2 = this._fields[1](row, data); ++state.count; if (isValid(value1) && isValid(value2)) { ++state.valid; if (state.list) state.list.add([value1, value2]); this._add(state, value1, value2); } } rem(state, row, data) { const value1 = this._fields[0](row, data); const value2 = this._fields[1](row, data); --state.count; if (isValid(value1) && isValid(value2)) { --state.valid; if (state.list) state.list.rem(); this._rem(state, value1, value2); } } }