Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// Copyright 2020 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import meta::pure::metamodel::path::*;
import meta::pure::tds::extensions::*;
Class meta::pure::tds::TabularDataSet
{
columns : TDSColumn[*];
columnByName(s:String[1]){$this.columns->filter(c|$c.name == $s)->first()}:TDSColumn[0..1];
rows : TDSRow[*];
}
Class meta::pure::tds::TDSColumn
{
<>
offset : Integer[0..1];
<>
name : String[1];
<>
type : DataType[0..1];
<>
enumMappingId : String[0..1];
<>
enumMapping: meta::pure::mapping::EnumerationMapping[0..1];
<>
documentation : String[0..1];
<>
sourceDataType: Any[0..1];
toString(){
'meta::pure::tds::TDSColumn('
+ 'name=' + $this.name
+ if($this.type->isNotEmpty(),
| ', ' + 'type=' + $this.type->makeString(),
| ''
)
+ if($this.offset->isNotEmpty(),
| ', ' + 'offset=' + $this.offset->makeString(),
| ''
)
+ if($this.documentation->isNotEmpty(),
| ', ' + 'documentation=' + $this.documentation->makeString(),
| ''
)
+ if($this.enumMappingId->isNotEmpty(),
| ', ' + 'enumMappingId=' + $this.enumMappingId->makeString(),
| ''
)
+ if($this.sourceDataType->isNotEmpty(),
| ', ' + 'sourceDataType=' + $this.sourceDataType->makeString(),
| ''
)
+ ')';
}:String[1];
}
Class meta::pure::tds::TDSRow
{
parent : TabularDataSet[0..1];
values : Any[*];
getString(colName:String[1]){$this.get($colName)->cast(@String)}:String[1];
getNullableString(colName:String[1]){ if($this.isNull($colName), | [] , | $this.getString($colName))}:String[0..1];
getNumber(colName:String[1]){$this.get($colName)->cast(@Number)}:Number[1];
getInteger(colName:String[1]){$this.get($colName)->cast(@Integer)}:Integer[1];
getFloat(colName:String[1]){$this.get($colName)->cast(@Number)->toFloat()}:Float[1];
getDecimal(colName:String[1]){$this.get($colName)->cast(@Number)->toDecimal()}:Decimal[1];
getDate(colName:String[1]){$this.get($colName)->cast(@Date)}:Date[1];
getDateTime(colName:String[1]){$this.get($colName)->cast(@DateTime)}:DateTime[1];
getStrictDate(colName:String[1]){$this.get($colName)->cast(@StrictDate)}:StrictDate[1];
getBoolean(colName:String[1]){$this.get($colName)->cast(@Boolean)}:Boolean[1];
getEnum(colName:String[1]){$this.get($colName)->cast(@Enum)}:Enum[1];
isNotNull(colName:String[1]){!$this.isNull($colName)}:Boolean[1];
get(colName:String[1])
{
let parent = $this.parent;
assertNotEmpty($parent, {| 'Unable to find index for the column:' +$colName+'. The TDS row has no parent, make sure the row has a parent.'});
let col = $this.parent->toOne().columnByName($colName);
assertNotEmpty($col, {| $colName+' is unknown (columns=['+$parent.columns.name->makeString(',')+'])!'});
$this.get($col->toOne());
}:Any[1];
isNull(colName:String[1]){
let parent = $this.parent;
assertNotEmpty($parent, {| 'Unable to find index for the column:' +$colName+'. The TDS row has no parent, make sure the row has a parent.'});
let col = $this.parent->toOne().columnByName($colName);
assertNotEmpty($col, {| $colName+' is unknown (columns=['+$parent.columns.name->makeString(',')+'])!'});
$this.isNull($col->toOne());
}:Boolean[1];
getString(col:TDSColumn[1]){$this.get($col)->cast(@String)}:String[1];
getNullableString(col:TDSColumn[1]){ if($this.isNull($col), | [] , | $this.getString($col))}:String[0..1];
getNumber(col:TDSColumn[1]){$this.get($col)->cast(@Number)}:Number[1];
getInteger(col:TDSColumn[1]){$this.get($col)->cast(@Integer)}:Integer[1];
getFloat(col:TDSColumn[1]){$this.get($col)->cast(@Float)}:Float[1];
isNull(col:TDSColumn[1]){$this.get($col) == ^TDSNull()}: Boolean[1];
isNotNull(col:TDSColumn[1]){!$this.isNull($col)}: Boolean[1];
get(col: TDSColumn[1]){
assertNotEmpty($col.offset, {| 'No index for the column:' +$col.name+'.'});
$this.values->at($col.offset->toOne())->toOne();
} : Any[1];
}
Class meta::pure::tds::TDSNull
{
//Used to make sure that two TDSNull instances are equal
<> key:Nil[0];
toString(){
'TDSNull'
}:String[1];
}
Class meta::pure::tds::AggregateValue
{
name : String[1];
mapFn : FunctionDefinition<{TDSRow[1]->T[0..1]}>[1];
aggregateFn : FunctionDefinition<{T[*]->U[0..1]}>[1];
}
Class meta::pure::tds::ColumnSpecification
{
name : String[1];
documentation : String[0..1];
}
Class {doc.doc = 'Normal Project column, also used for extending a TDS'}
meta::pure::tds::BasicColumnSpecification extends meta::pure::tds::ColumnSpecification
{
func : Function<{T[1]->Any[*]}>[1];
}
Enum meta::pure::tds::SortDirection
{
ASC, DESC
}
Class meta::pure::tds::SortInformation
{
column : String[1];
direction : SortDirection[1];
}
Class meta::pure::tds::ColumnSort
{
column:Function<{T[1]->Any[1]}>[1];
direction:SortDirection[1];
}
Class meta::pure::tds::Window
{
partition:Function<{T[1]->Any[1]}>[*];
}
Class meta::pure::tds::OlapOperation
{
//superclass for all operations
}
Class meta::pure::tds::OlapAggregation extends OlapOperation
{
mapFn : FunctionDefinition<{T[1]->Number[1]}>[1];
aggregateFn : FunctionDefinition<{Number[*]->Number[0..1]}>[1];
}
Class meta::pure::tds::OlapRank extends OlapOperation
{
rankFn: FunctionDefinition<{T[*]->Map[1]}>[1];
}
Class meta::pure::tds::WindowColumnSpecification extends ColumnSpecification
{
window:Window[0..1];
sortInfo: ColumnSort[0..1];
func: meta::pure::tds::OlapOperation[1];
}
function
{doc.doc = 'Helper function to create / define an aggreagation value (combination of column name and aggregation function)'}
meta::pure::tds::agg(name:String[1], mapFn:FunctionDefinition<{TDSRow[1]->T[0..1]}>[1], aggregateFn:FunctionDefinition<{T[*]->U[0..1]}>[1]):meta::pure::tds::AggregateValue[1]
{
^meta::pure::tds::AggregateValue(name = $name, mapFn=$mapFn, aggregateFn=$aggregateFn);
}
function meta::pure::tds::col(window: Window[1], func:FunctionDefinition<{T[*]->Map[1]}>[1], name: String[1]):WindowColumnSpecification[1]
{
let rank = ^OlapRank(rankFn = $func);
^WindowColumnSpecification
(
window = $window,
name = $name,
func = $rank
);
}
function meta::pure::tds::col(window: Window[1], sortInfo:ColumnSort[1], func:FunctionDefinition<{T[*]->Map[1]}>[1], name: String[1]):WindowColumnSpecification[1]
{
let rank = ^OlapRank(rankFn = $func);
^WindowColumnSpecification
(
window = $window,
sortInfo = $sortInfo,
name = $name,
func = $rank
);
}
function meta::pure::tds::col(window: Window[1], func: meta::pure::tds::OlapAggregation[1], name: String[1]):WindowColumnSpecification[1]
{
^WindowColumnSpecification
(
window = $window,
name = $name,
func = $func
)
}
function meta::pure::tds::col(window: Window[1],sortInfo:ColumnSort[1], func: meta::pure::tds::OlapAggregation[1], name: String[1]):WindowColumnSpecification[1]
{
^WindowColumnSpecification
(
window = $window,
sortInfo = $sortInfo,
name = $name,
func = $func
)
}
function meta::pure::tds::col(sortInfo:ColumnSort[1], func: meta::pure::tds::OlapAggregation[1], name: String[1]):WindowColumnSpecification[1]
{
^WindowColumnSpecification
(
sortInfo = $sortInfo,
name = $name,
func = $func
);
}
function meta::pure::tds::col(sortInfo:ColumnSort[1], func:FunctionDefinition<{T[*]->Map[1]}>[1], name: String[1]):WindowColumnSpecification[1]
{
let rank = ^OlapRank(rankFn = $func);
^WindowColumnSpecification
(
sortInfo = $sortInfo,
name = $name,
func = $rank
);
}
function
{doc.doc = 'Helper function to create a column specificantion based on the specified value function and column name'}
meta::pure::tds::col(func : Function<{T[1]->Any[*]}>[1], name : String[1]):meta::pure::tds::BasicColumnSpecification[1]
{
^BasicColumnSpecification
(
func = $func,
name = $name
)
}
function
{doc.doc = 'Helper function to create a column specificantion based on the specified value function, column name and documentation for the column'}
meta::pure::tds::col(func : Function<{T[1]->Any[*]}>[1], name : String[1], documentation : String[1]):meta::pure::tds::BasicColumnSpecification[1]
{
^BasicColumnSpecification
(
func = $func,
name = $name,
documentation = $documentation
)
}
function meta::pure::tds::tdsRows(tds:TabularDataSet[1]):TDSRow[*]
{
$tds.rows
}
function meta::pure::tds::projectWithColumnSubset(set:T[*], columnSpecifications:ColumnSpecification[*], columnList:String[*]):TabularDataSet[1]
{
$columnList->map( i| assert($columnSpecifications.name->contains($i) , 'SubSetColumnList should be in project List ' ) );
$set->project($columnSpecifications->filter(c|$c.name->in($columnList)));
}
function meta::pure::tds::projectWithColumnSubset(set:T[*], functions:Function<{T[1]->Any[*]}>[*], ids:String[*], columnList:String[*]):TabularDataSet[1]
{
$columnList->map( i| assert($ids->contains($i) , 'SubSetColumnList should be in project List ' ) );
$set->project( $functions->zip($ids)->filter(p:PairAny[*]}>,String>[1] |$p.second->in($columnList))->map(p|^BasicColumnSpecification(func = $p.first->cast(@Function<{Nil[1]->Any[*]}>), name = $p.second))->cast(@BasicColumnSpecification));
}
function
{doc.doc = 'Project the values from the columns specifications provided to create a new TDS'}
meta::pure::tds::project(set:T[*], columnSpecifications:ColumnSpecification[*]):TabularDataSet[1]
{
let simpleCols = $columnSpecifications->filter(c|$c->instanceOf(BasicColumnSpecification))->cast(@BasicColumnSpecification);
let functions = $simpleCols.func;
let res = ^TabularDataSet(
columns = $simpleCols->map(cs|let offset = $columnSpecifications->indexOf($cs);
^TDSColumn(offset = $offset,
name = $cs.name,
type = $cs.func->functionReturnType().rawType->toOne()->cast(@DataType),
documentation = $cs.documentation);
));
let newRows = $set->map(value|
$functions->fold(
{
path, list | let res = $path->eval($value);
let updated = if ($res->size() == 0,|^TDSNull(),|$res);
^List(values = $updated->map(navValue|$list.values->map(row|^$row(values+=$navValue))));
},
^List(values = ^TDSRow(values=[], parent=$res))
).values;
);
$res->mutateAdd('rows',$newRows);
$res;
}
function
{doc.doc = 'Project the values from the lambda functions specified to create a new TDS'}
meta::pure::tds::project(set:K[*], functions:Function<{K[1]->Any[*]}>[*], ids:String[*]):TabularDataSet[1]
{
$set->project($functions->zip($ids)->map(p|^BasicColumnSpecification(func = $p.first->cast(@Function<{Nil[1]->Any[*]}>), name = $p.second))->cast(@BasicColumnSpecification));
}
function
{doc.doc = 'Project the values from the property paths specified to create a new TDS'}
meta::pure::tds::project(set:T[*], paths:Path[*]):TabularDataSet[1]
{
$paths->validatePaths();
project($set, $paths, $paths->map(p|$p->buildColumnNameOutOfPath()));
}
function
{doc.doc = 'Sort the provided TDS based on the specified information (sorting is based on order of the sort information)'}
meta::pure::tds::sort(tds:TabularDataSet[1], sortInfo:SortInformation[*]):TabularDataSet[1]
{
^$tds(rows = $tds.rows->sort({a,b|$a->multipleColumnComp($b, $sortInfo)}));
}
function
{doc.doc = 'Sort the provided TDS based on the specified column & direction'}
meta::pure::tds::sort(tds:TabularDataSet[1], column:String[1], direction:SortDirection[1]):TabularDataSet[1]
{
$tds->sort(^SortInformation(column=$column, direction=$direction));
}
function
{doc.doc = 'Sort the provided TDS based on the specified column (with ascending direction)'}
meta::pure::tds::sort(tds:TabularDataSet[1], columns:String[*]):TabularDataSet[1]
{
$tds->sort($columns->map(c | ^SortInformation(column=$c, direction=SortDirection.ASC)));
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS, taking the first set of rows based on the specified size'}
meta::pure::tds::limit(tds:TabularDataSet[1], size:Integer[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->limit($size)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS if a size is specified (otherwise returns original rows), taking the first set of rows based on the specified size'}
meta::pure::tds::limit(tds:TabularDataSet[1], size:Integer[0..1]):TabularDataSet[1]
{
if($size->isEmpty(),
| $tds,
| $tds->limit($size->toOne())
)
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS, taking the first set of rows based on the specified size'}
meta::pure::tds::take(tds:TabularDataSet[1], size:Integer[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->take($size)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function
{doc.doc = 'Returns the n-th (n = pageNumber) page TDS after paginating as per pageSize'}
meta::pure::tds::paginated(tds:TabularDataSet[1], pageNumber:Integer[1], pageSize: Integer[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->paginated($pageNumber, $pageSize)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS, dropping the first set of rows based on the specified size'}
meta::pure::tds::drop(tds:TabularDataSet[1], size:Integer[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->drop($size)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS, selecting the set of rows in the specified range between start and stop'}
meta::pure::tds::slice(tds:TabularDataSet[1], start:Integer[1], stop:Integer[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->slice($start, $stop)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function
{doc.doc = 'Reduce the number of rows in the provided TDS, selecting the set of rows that match the provided filter function'}
meta::pure::tds::filter(tds:TabularDataSet[1], f:Function<{TDSRow[1]->Boolean[1]}>[1]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(columns=$tds.columns);
$newTds->mutateAdd('rows', $tds.rows->filter($f)->map(r|^TDSRow(parent=$newTds,values=$r.values)));
}
function <> meta::pure::tds::multipleColumnComp(row:TDSRow[1], other:TDSRow[1], sortInfo:SortInformation[*]):Integer[1]
{
if ($sortInfo->isEmpty(),
| 0,
| let s = $sortInfo->head();
let rowVal = if($row.isNull($s.column->toOne()), |[], |$row.get($s.column->toOne()));
let otherVal = if($other.isNull($s.column->toOne()), |[], |$other.get($s.column->toOne()));
let res = $rowVal->match([
none: Any[0]|
$otherVal->match([
none: Any[0] | 0,
otherVal: Any[1] | 1
]),
val: Any[1]|
$otherVal->match([
none: Any[0] | -1,
otherVal: Any[1] | $val->compare($otherVal)
])
]);
if ($res == 0, |$row->multipleColumnComp($other, $sortInfo->tail()), |if ($s.direction == SortDirection.ASC, |$res,|-$res));
);
}
function
{doc.doc = 'Remove duplicate rows from the priovded TDS'}
meta::pure::tds::distinct(tds:TabularDataSet[1]):TabularDataSet[1]
{
let newTds = ^$tds(rows = []);
let distinctRows = $tds.rows->removeDuplicates({l, r | $l.values == $r.values})->map(r | ^$r(parent = $newTds));
$newTds->mutateAdd('rows', $distinctRows);
}
function
{doc.doc = 'Append the rows from the additional TDSs to the rows in the first TDS. N.B. The column must be the same for the TDSs'}
meta::pure::tds::concatenate(tds1:TabularDataSet[1], tdss:TabularDataSet[*]):TabularDataSet[1]
{
$tdss->forAll(tds2|
assert($tds1.columns->size() == $tds2.columns->size(), | 'Number of columns must match to concatenate, got ' + $tds1.columns.name->joinStrings('[', ',', ']') + ' and ' + $tds2.columns.name->joinStrings('[', ',', ']') );
assertEquals($tds1.columns.name, $tds2.columns.name, | 'Columns names must match to concatenate, got ' + $tds1.columns.name->joinStrings('[', ',', ']') + ' and ' + $tds2.columns.name->joinStrings('[', ',', ']') );
assert(zip($tds1.columns, $tds2.columns)->forAll(pair |
assertEquals($pair.first.name, $pair.second.name);
assertEquals($pair.first.type, $pair.second.type, | 'Column types must match to concatenate, mismatched for column ' + $pair.first.name + ' (' + $pair.first.type->makeString() + ' vs ' + $pair.second.type->makeString() + ')');
));
);
let tds = ^TabularDataSet(columns=$tds1.columns->map(col|^TDSColumn(offset=$col.offset, name=$col.name, type=$col.type)));
let newRows = $tds1.rows->concatenate($tdss.rows)->map(r|^TDSRow(parent=$tds,values=$r.values));
$tds->mutateAdd('rows', $newRows);
$tds;
}
function
{doc.doc = 'Append the rows from the second TDS to the rows in the first TDS. N.B. The column must be the same for the two input TDSs'}
meta::pure::tds::concatenate(tds1:TabularDataSet[1], tds2:TabularDataSet[1]):TabularDataSet[1]
{
$tds1->concatenate($tds2->toOneMany());
}
function meta::pure::tds::validatePaths(paths:Path[*]):Boolean[1]
{
let names = $paths->map(p|
let lastElement = $p.path->last()->cast(@PropertyPathElement)->toOne();
assert($lastElement.property->functionReturnType().rawType->toOne()->instanceOf(DataType), 'All the paths provided to project need to end with a DataType');
if($p.name == '',|$lastElement.property.name;,|$p.name);
);
assertEquals($names->size(), $names->removeDuplicates()->size(), 'The list of Paths provided to project can\'t contain duplicate names. Please use ! to provide an alias. Example:#/Person/lastName!newAlias#');
}
function
{doc.doc = 'Select the value for the specified column (based on its name) from each of the rows in the TDS provided'}
meta::pure::tds::columnValues(tds:TabularDataSet[1], pathAsString:String[1]):Any[*]
{
columnValues($tds, $tds.columns->map(c | $c.name)->indexOf($pathAsString));
}
function
{doc.doc = 'Select the value for the specified column (based on its index) from each of the rows in the TDS provided'}
meta::pure::tds::columnValues(tds:TabularDataSet[1], columnIndex:Integer[1]):Any[*]
{
$tds.rows->map(row | $row.values->at($columnIndex));
}
function
{doc.doc = 'Rename the specified column in the provided TDS to a new name'}
meta::pure::tds::renameColumn(tds:TabularDataSet[1], oldColumnName:String[1], newColumnName:String[1]):TabularDataSet[1]
{
$tds->renameColumns(pair($oldColumnName, $newColumnName));
}
function
{doc.doc = 'Rename the specified columns in the provided TDS to a new name'}
meta::pure::tds::renameColumns(tds:TabularDataSet[1], columnMappings : Pair[*]):TabularDataSet[1]
{
let columnMappingsMap = $columnMappings->newMap();
let newTds = ^TabularDataSet(columns=$tds.columns->map(c |
let newName = $columnMappingsMap->get($c.name);
if($newName->isNotEmpty(), | ^$c(name=$newName->toOne()), | $c);
));
$newTds->mutateAdd('rows', $tds.rows->map(r | ^TDSRow(parent=$newTds,values=$r.values)));
$newTds;
}
function
{doc.doc = 'Add additional new calculated columns to the provided TDS'}
meta::pure::tds::extend(tds:TabularDataSet[1], newColumnFunctions:BasicColumnSpecification[*]):TabularDataSet[1]
{
let newTds = ^TabularDataSet(
columns=$tds.columns->concatenate($newColumnFunctions->size()->range()->map(index |
let col = $newColumnFunctions->at($index);
^TDSColumn(offset=$tds.columns->size() + $index, name=$col.name, type=$col.func->functionReturnType().rawType->toOne()->cast(@DataType));
)));
//todo: remove this by making parent an association
$newTds->mutateAdd('rows', $tds.rows->map(r | ^TDSRow(parent=$newTds,
values=$r.values->concatenate(
$newColumnFunctions.func->map(f |
let value = $f->evaluate(^List(values=[$r]));
if($value->isEmpty(), |^TDSNull(), |$value);
)
)
)
)
);
$newTds;
}
function
{doc.doc = 'Project the specified calculated columns from the provided TDS. This is similar to extend, but rather than adding the columns it replaces all of the existing ones '}
meta::pure::tds::project(tds:TabularDataSet[1], columnFunctions:ColumnSpecification[*]):TabularDataSet[1]
{
let newColumnFunctions = $columnFunctions->filter(c|$c->instanceOf(BasicColumnSpecification))->cast(@BasicColumnSpecification);
let newTds = ^TabularDataSet(
columns=$newColumnFunctions->size()->range()->map(index |
let col = $newColumnFunctions->at($index);
^TDSColumn(offset=$index, name=$col.name, type=$col.func->functionReturnType().rawType->toOne()->cast(@DataType));
));
//todo: remove this by making parent an association
$newTds->mutateAdd('rows', $tds.rows->map(r | ^TDSRow(parent=$newTds,
values=$newColumnFunctions.func->map(f |
let value = $f->evaluate(^List(values=[$r]));
if($value->isEmpty(), |^TDSNull(), |$value);
)
)
)
);
$newTds;
}
function
{doc.doc = 'Restrict / reduce the columns from within the provided TDS'}
meta::pure::tds::restrict(tds:TabularDataSet[1], columnNames : String[*]):TabularDataSet[1]
{
assertEmpty($columnNames->removeAll($tds.columns.name), | 'Columns not found in input TDS: ' + $columnNames->removeAll($tds.columns.name)->joinStrings('[', ',', ']'));
let origCols = $columnNames->map(columnName|$tds.columns->filter(c|$c.name == $columnName)->toOne());
let newCols = $origCols->size()->range()->map(index |let origCol = $origCols->at($index); ^$origCol(offset=$index););
let newTds = ^TabularDataSet(
columns=$newCols
);
let restrictedRows = $tds.rows->map(r | ^TDSRow(values = $origCols.offset->map(offset|$r.values->at($offset)), parent=$newTds));
//todo: remove this by making parent an association
$newTds->mutateAdd('rows',$restrictedRows);
$newTds;
}
function
{doc.doc = 'Restrict / reduce columns in provided TDS and return TDS with distinct rows'}
meta::pure::tds::restrictDistinct(tds : TabularDataSet[1], columnNames : String[*]):TabularDataSet[1]
{
$tds->restrict($columnNames)->distinct()
}
function
{doc.doc = 'Aggregate the data within the provided TDS using the specified groupings and aggregation functions'}
meta::pure::tds::groupBy(tds:TabularDataSet[1], columns:String[*], aggValues:meta::pure::tds::AggregateValue[*]):TabularDataSet[1]
{
let groupByOutputCols = range($columns->size())->map(index|
let name = $columns->at($index);
let colType = $tds.columnByName($name)->toOne().type;
^TDSColumn(offset = $index,
type = $colType,
name = $name
);
);
let aggOutputCols = range($aggValues->size())->map(index|
let name = $aggValues->at($index).name;
let colType = $aggValues->at($index).aggregateFn->functionReturnType().rawType->toOne()->cast(@DataType);
^TDSColumn(offset = $index + $columns->size(),
type = $colType,
name = $name
);
);
let outputCols = $groupByOutputCols->concatenate($aggOutputCols);
let newTds = ^TabularDataSet(
columns = $outputCols);
let outputRows = $tds.rows->aggRows($columns,
$columns->map(name|agg($name, row|$row.get($name), values|$values->at(0)))->concatenate($aggValues),
$newTds
);
$newTds->mutateAdd('rows', $outputRows);
$newTds;
}
function <> meta::pure::tds::aggRows(rows : TDSRow[*], columnNames : String[*], aggValues:meta::pure::tds::AggregateValue[*], parent:TabularDataSet[1]) : TDSRow[*]
{
if ($columnNames->isEmpty(), {|
let values = $aggValues->map(aggValue|
let rowMapValues = $rows->map(row|$aggValue.mapFn->eval($row));
let value = $aggValue.aggregateFn->eval($rowMapValues);
if($value->isEmpty(), |^TDSNull(), |$value);
);
^TDSRow(values = $values, parent=$parent);
}, {|
let col = $columnNames->at(0);
let distinctColValues = $rows->map(row|$row.get($col))->distinct();
$distinctColValues->map(colValue|
$rows->filter(row|$row.get($col) == $colValue)->aggRows($columnNames->tail(), $aggValues, $parent)
);
});
}
function
{doc.doc = 'Helper function to create a default column name for the specified path'}
meta::pure::tds::buildColumnNameOutOfPath(p:Path[1]):String[1]
{
if($p.name=='',|$p.path->last()->cast(@PropertyPathElement).property.name->toOne(),|$p.name)->toOne()
}
function
{doc.doc = 'Helper function to create sort information with the specified column in descending order'}
meta::pure::tds::desc(column:String[1]):SortInformation[1]
{
^SortInformation(column=$column, direction=SortDirection.DESC)
}
function
{doc.doc = 'Helper function to create sort information with the specified column in ascending order'}
meta::pure::tds::asc(column:String[1]):SortInformation[1]
{
^SortInformation(column=$column, direction=SortDirection.ASC)
}
function meta::pure::tds::sortAsc(column:Function<{T[1]->Any[1]}>[1]): ColumnSort[1]
{
^ColumnSort
(
column=$column,
direction= SortDirection.ASC
)
}
function meta::pure::tds::window(functions:Function<{T[1]->Any[1]}>[*]):Window[1]
{
^Window
(
partition=$functions
)
}
function meta::pure::tds::sortDesc(column:Function<{T[1]->Any[1]}>[1]): ColumnSort[1]
{
^ColumnSort
(
column=$column,
direction= SortDirection.DESC
)
}
function meta::pure::tds::func(map:FunctionDefinition<{T[1]->Any[1]}>[1], agg:FunctionDefinition<{Number[*]->Number[0..1]}>[1]):OlapAggregation[1]
{
^OlapAggregation
(
mapFn= $map,
aggregateFn= $agg
)
}
function meta::pure::tds::extractOperation(o:OlapOperation[1]): FunctionDefinition[1]
{
$o->match([
a:OlapAggregation[1]| $a.aggregateFn,
r: OlapRank[1]| $r.rankFn,
t: TdsOlapAggregation[1]| $t.func,
tr: TdsOlapRank[1]| $tr.func
]
)
}
/*OLAP TDS protocol*/
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], columns:String[*], sortBy:SortInformation[0..1],operation:meta::pure::tds::OlapOperation[1],columnName:String[1]):TabularDataSet[1]
{
fail('This Function is not meant for use outside the context of an execute()');
$tds;
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], columns:String[*], operation:meta::pure::tds::OlapOperation[1],columnName:String[1]):TabularDataSet[1]
{
fail('This Function is not meant for use outside the context of an execute()');
$tds;
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], operation:meta::pure::tds::OlapOperation[1],columnName:String[1]):TabularDataSet[1]
{
fail('This Function is not meant for use outside the context of an execute()');
$tds;
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], sort:SortInformation[0..1], operation:meta::pure::tds::OlapOperation[1],columnName:String[1]):TabularDataSet[1]
{
fail('This Function is not meant for use outside the context of an execute()');
$tds;
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], columns:String[*], sortBy:SortInformation[0..1],operation:FunctionDefinition<{TDSRow[*]->Map[1]}>[1],columnName:String[1]):TabularDataSet[1]
{
olapGroupBy($tds,$columns,$sortBy,func($operation),$columnName);
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], columns:String[*], operation:FunctionDefinition<{TDSRow[*]->Map[1]}>[1],columnName:String[1]):TabularDataSet[1]
{
olapGroupBy($tds,$columns,func($operation),$columnName);
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], operation:FunctionDefinition<{TDSRow[*]->Map[1]}>[1],columnName:String[1]):TabularDataSet[1]
{
olapGroupBy($tds,func($operation),$columnName);
}
function meta::pure::tds::olapGroupBy(tds:TabularDataSet[1], sortBy:SortInformation[0..1], operation:FunctionDefinition<{TDSRow[*]->Map[1]}>[1],columnName:String[1]):TabularDataSet[1]
{
olapGroupBy($tds,$sortBy,func($operation),$columnName);
}
function meta::pure::tds::func(col:String[1], f: FunctionDefinition<{Number[*]->Number[0..1]}>[1]): TdsOlapAggregation[1]
{
^TdsOlapAggregation(colName = $col, func = $f);
}
function meta::pure::tds::func(f: FunctionDefinition<{TDSRow[*]->Map[1]}>[1]): TdsOlapRank[1]
{
^TdsOlapRank(func = $f);
}
Class meta::pure::tds::TdsOlapAggregation extends meta::pure::tds::OlapOperation
{
colName : String[1] ;
func: FunctionDefinition<{Number[*]->Number[0..1]}>[1];
}
Class meta::pure::tds::TdsOlapRank extends meta::pure::tds::OlapOperation
{
func:FunctionDefinition<{T[*]->Map[1]}>[1];
}
// Multi In
function meta::pure::tds::tdsContains(object: T[1], functions: Function<{T[1]->Any[0..1]}>[*], tds: TabularDataSet[1]):Boolean[1]
{
assert($functions->isNotEmpty(), 'Functions cannot be empty');
assert($tds.columns->size() == $functions->size(), | 'Number of functions doesn\'t match TDS columns, Functions number: ' + $functions->size()->toString() + ', TDSColumns number: ' + $tds.columns->size()->toString());
$tds.rows->exists(r | $r.values == $functions->map(k | $k->eval($object)));
}
function meta::pure::tds::tdsContains(object: T[1], functions: Function<{T[1]->Any[0..1]}>[*], ids:String[*], tds: TabularDataSet[1], crossOperation: Function<{TDSRow[1],TDSRow[1]->Boolean[1]}>[1]):Boolean[1]
{
assert($functions->isNotEmpty(), 'Functions cannot be empty');
assert($tds.columns->size() == $functions->size(), | 'Number of functions doesn\'t match TDS columns, Functions number: ' + $functions->size()->toString() + ', TDSColumns number: ' + $tds.columns->size()->toString());
let projected = $object->project($functions, $ids);
($projected.rows->size() != 0) && $tds.rows->exists(r | $crossOperation->eval($projected.rows->at(0), $r));
}
function meta::pure::tds::groupBy(set:K[*], functions:meta::pure::metamodel::function::Function<{K[1]->Any[*]}>[*], aggValues:meta::pure::functions::collection::AggregateValue[*], ids:String[*]):TabularDataSet[1]
{
let grouped = $set->fold({value,map|let key = ^List(values=$functions->map(f | let val = $f->eval($value);
if($val->isEmpty(), | ^TDSNull(), | $val);
));
let currentList = $map->get($key);
$map->put($key, ^List(values=if($currentList->isEmpty(), |$value, |$currentList->toOne().values->concatenate($value))));
},^Map,List>());
let columnInfo = $ids->zip($functions->concatenate($aggValues.aggregateFn));
let columns = $columnInfo->map(info | ^TDSColumn(
name = $info.first,
offset = $columnInfo->indexOf($info),
type = $info.second->functionReturnType().rawType->toOne()->cast(@DataType)));
let tds = ^TabularDataSet(columns=$columns);
let rows = $grouped->keys()->map({key | let listObjs = $grouped->get($key);
let aggResults = $aggValues->map(a | let vals = $listObjs.values->map(o | $a.mapFn->eval($o));
if ($vals->isEmpty(), | ^TDSNull(), | $a.aggregateFn->eval($vals)->cast(@Any));
);
^TDSRow(values=$key.values->concatenate($aggResults), parent=$tds);
});
$tds->mutateAdd('rows', $rows);
$tds;
}
function meta::pure::tds::groupByWithWindowSubset(set:K[*], functions:meta::pure::metamodel::function::Function<{K[1]->Any[*]}>[*], aggValues:meta::pure::functions::collection::AggregateValue[*], ids:String[*] , subSelectIds:String[*] , subAggIds:String[*] ):TabularDataSet[1]
{
let newAggValues = $aggValues->filter( g| $subAggIds->map(i |$ids->indexOf($i))->contains($aggValues->indexOf($g) + $functions->size() ))->sort({a1, a2 | compare($subAggIds->indexOf($ids->at($aggValues->indexOf($a1) + $functions->size())) , $subAggIds->indexOf($ids->at($aggValues->indexOf($a2) + $functions->size()))) });
let newIds = $subSelectIds->concatenate($subAggIds);
let subSetfunctions = $functions->filter(f | $subSelectIds->map(i | $ids->indexOf($i))->contains($functions->indexOf($f)) )->sort({a1, a2 | compare($ids->indexOf($ids->at($functions->indexOf($a1))) , $ids->indexOf($ids->at($functions->indexOf($a2)))) });
meta::pure::tds::groupBy( $set, $subSetfunctions ,$newAggValues , $newIds );
}