From 412a51a783de70354e45f0fff2072f67765be4b6 Mon Sep 17 00:00:00 2001 From: "Eduardo L. Buratti" <elburatti@inf.ufpr.br> Date: Wed, 9 Nov 2016 10:20:26 -0200 Subject: [PATCH 1/3] Update typescript config --- tsconfig.json | 4 +++- tslint.json | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 37c0501b..0ef91a28 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,16 +4,18 @@ "target": "es6", "module": "commonjs", "noImplicitAny": true, + "experimentalDecorators": true, "outDir": "dist/" }, "exclude": [ "node_modules" ], - "compileOnSave": false, + "compileOnSave": true, "typeRoots": [ "node_modules/@types" ], "types": [ + "mocha", "d3-force" ] } diff --git a/tslint.json b/tslint.json index 0309ae07..27a0066a 100644 --- a/tslint.json +++ b/tslint.json @@ -4,6 +4,7 @@ "no-var-requires": false, "object-literal-sort-keys": false, "one-line": false, - "trailing-comma": false + "trailing-comma": false, + "interface-name": false } } -- GitLab From 9239886b3e8ab04c622b861db3d52849127f08c6 Mon Sep 17 00:00:00 2001 From: "Eduardo L. Buratti" <elburatti@inf.ufpr.br> Date: Wed, 9 Nov 2016 12:13:08 -0200 Subject: [PATCH 2/3] WIP: Refactor engine and views --- src/adapter/postgres.ts | 4 +- src/common/{aggregationType.ts => types.ts} | 0 src/core/dimension.ts | 31 ++++++ src/core/engine.spec.ts | 112 +++++++++++--------- src/core/engine.ts | 77 +++++++++++--- src/core/metric.ts | 36 +++++++ src/core/view.ts | 35 +++--- src/util/hash.ts | 2 +- 8 files changed, 208 insertions(+), 89 deletions(-) rename src/common/{aggregationType.ts => types.ts} (100%) create mode 100644 src/core/dimension.ts create mode 100644 src/core/metric.ts diff --git a/src/adapter/postgres.ts b/src/adapter/postgres.ts index 3cb982a3..b796d142 100644 --- a/src/adapter/postgres.ts +++ b/src/adapter/postgres.ts @@ -18,9 +18,9 @@ * along with blend. If not, see <http://www.gnu.org/licenses/>. */ -import { View } from "../core/view"; import { Adapter } from "../core/adapter"; -import { AggregationType } from "../common/aggregationType"; +import { AggregationType } from "../common/types"; +import { View } from "../core/view"; interface ParsedView { query: string; diff --git a/src/common/aggregationType.ts b/src/common/types.ts similarity index 100% rename from src/common/aggregationType.ts rename to src/common/types.ts diff --git a/src/core/dimension.ts b/src/core/dimension.ts new file mode 100644 index 00000000..f0b27898 --- /dev/null +++ b/src/core/dimension.ts @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blend. + * + * blend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blend. If not, see <http://www.gnu.org/licenses/>. + */ + +export interface DimensionOptions { + name: string; +} + +export class Dimension { + public readonly name: string; + + constructor(options: DimensionOptions) { + this.name = options.name; + } +} diff --git a/src/core/engine.spec.ts b/src/core/engine.spec.ts index 94c6456d..e59087d6 100644 --- a/src/core/engine.spec.ts +++ b/src/core/engine.spec.ts @@ -21,78 +21,88 @@ import { expect } from "chai"; import { Engine } from "./engine"; +import { Metric } from "./metric"; +import { Dimension } from "./dimension"; import { View } from "./view"; import { ViewOptions } from "./view"; import { Query } from "../common/query"; -import { AggregationType } from "../common/aggregationType"; +import { AggregationType } from "../common/types"; describe("engine class", () => { - let query: Query = { - metrics: [], - dimensions: [] - }; + const engine = new Engine(); - let views: View[] = []; + const met1 = new Metric({ name: "met:1", aggregation: AggregationType.SUM }); + const met2 = new Metric({ name: "met:2", aggregation: AggregationType.AVG }); + const met3 = new Metric({ name: "met:3", aggregation: AggregationType.AVG }); + const met4 = new Metric({ name: "met:4", aggregation: AggregationType.SUM }); + const met5 = new Metric({ name: "met:5", aggregation: AggregationType.SUM }); + const met6 = new Metric({ name: "met:6", aggregation: AggregationType.AVG }); + const met7 = new Metric({ name: "met:7", aggregation: AggregationType.COUNT }); + const met8 = new Metric({ name: "met:8", aggregation: AggregationType.COUNT }); + const met9 = new Metric({ name: "met:9", aggregation: AggregationType.SUM }); + const met10 = new Metric({ name: "met:10", aggregation: AggregationType.COUNT }); - let viewBuilder: Query[] = [ - { metrics: ["met:1", "met:2", "met:3"], dimensions: ["dim:1", "dim:2"]}, - { metrics: ["met:1", "met:3", "met:5"], dimensions: ["dim:1", "dim:2"]}, - { metrics: ["met:3", "met:4", "met:7"], dimensions: ["dim:4", "dim:5"]}, - { metrics: ["met:6", "met:7"], dimensions: ["dim:3", "dim:4", "dim:5", "dim:6"]}, - { metrics: ["met:8", "met:2", "met:3"], dimensions: ["dim:1", "dim:2", "dim:7"]}, - { metrics: ["met:1", "met:2", "met:3"], dimensions: ["dim:1", "dim:2"]}, - { metrics: ["met:2", "met:4"], dimensions: ["dim:1", "dim:2"]}, - { metrics: ["met:8"], dimensions: ["dim:8", "dim:9", "dim:10"]}, - { metrics: ["met:9"], dimensions: ["dim:8", "dim:9", "dim:10"]}, - { metrics: ["met:10"], dimensions: ["dim:8", "dim:9", "dim:10"]} - ]; + const dim1 = new Dimension({ name: "dim:0" }); + const dim2 = new Dimension({ name: "dim:1" }); + const dim3 = new Dimension({ name: "dim:2" }); + const dim4 = new Dimension({ name: "dim:3" }); + const dim5 = new Dimension({ name: "dim:4" }); + const dim6 = new Dimension({ name: "dim:5" }); + const dim7 = new Dimension({ name: "dim:6" }); + const dim8 = new Dimension({ name: "dim:7" }); + const dim9 = new Dimension({ name: "dim:8" }); + const dim10 = new Dimension({ name: "dim:9" }); - let iterable: [string, AggregationType][] = [ - ["met:1", AggregationType.SUM], - ["met:2", AggregationType.AVG], - ["met:3", AggregationType.AVG], - ["met:4", AggregationType.SUM], - ["met:5", AggregationType.SUM], - ["met:6", AggregationType.AVG], - ["met:7", AggregationType.COUNT], - ["met:8", AggregationType.COUNT], - ["met:9", AggregationType.SUM], - ["met:10", AggregationType.COUNT], - ]; + engine.addMetric(met1); + engine.addMetric(met2); + engine.addMetric(met3); + engine.addMetric(met4); + engine.addMetric(met5); + engine.addMetric(met6); + engine.addMetric(met7); + engine.addMetric(met8); + engine.addMetric(met9); + engine.addMetric(met10); - let map: Map<string, AggregationType> = new Map(iterable); + engine.addDimension(dim1); + engine.addDimension(dim2); + engine.addDimension(dim3); + engine.addDimension(dim4); + engine.addDimension(dim5); + engine.addDimension(dim6); + engine.addDimension(dim7); + engine.addDimension(dim8); + engine.addDimension(dim9); + engine.addDimension(dim10); - for (let i: number = 0; i < 10; ++ i) { - query.metrics.push("met:" + (i + 1)); - query.dimensions.push("dim:" + (i + 1)); - let options: ViewOptions = { - metrics: viewBuilder[i].metrics, - dimensions: viewBuilder[i].dimensions, - materialized: true, - aggregationMap: map, - childViews: [] - }; - views.push(new View (options)); - } + let views: View[] = [ + new View({ metrics: [met1, met2, met3], dimensions: [dim1, dim2]}), + new View({ metrics: [met1, met3, met5], dimensions: [dim1, dim2]}), + new View({ metrics: [met3, met4, met7], dimensions: [dim4, dim5]}), + new View({ metrics: [met6, met7], dimensions: [dim3, dim4, dim5, dim6]}), + new View({ metrics: [met8, met2, met3], dimensions: [dim1, dim2, dim7]}), + new View({ metrics: [met1, met2, met3], dimensions: [dim1, dim2]}), + new View({ metrics: [met2, met4], dimensions: [dim1, dim2]}), + new View({ metrics: [met8], dimensions: [dim8, dim9, dim10]}), + new View({ metrics: [met9], dimensions: [dim8, dim9, dim10]}), + new View({ metrics: [met10], dimensions: [dim8, dim9, dim10]}) + ]; views.push(new View({ - metrics: ["met:1", "met:2", "met:3", "met:4", "met:5"], - dimensions: ["dim:1", "dim:2"], + metrics: [met1, met2, met3, met4, met5], + dimensions: [dim1, dim2], materialized: false, - aggregationMap: map, childViews: [views[0], views[6]] })); views.push(new View({ - metrics: ["met:8", "met:9", "met:10"], - dimensions: ["dim:8", "dim:9", "dim:10"], + metrics: [met8, met9, met10], + dimensions: [dim8, dim9, dim10], materialized: false, - aggregationMap: map, childViews: [views[7], views[8], views[9]] })); it("should be create a fill that cover the query and has 4 children", () => { - let engine: Engine = new Engine (views); let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); @@ -116,7 +126,6 @@ describe("engine class", () => { } }); it("should throw an exception, query with non-existent metric", () => { - let engine: Engine = new Engine (views); let error: boolean = false; try { engine.query({metrics: ["met:11"], dimensions: ["dim:1"]}); @@ -130,7 +139,6 @@ describe("engine class", () => { }); it("should throw an exception, query with non-existent dimension", () => { - let engine: Engine = new Engine (views); let error: boolean = false; try { engine.query({metrics: ["met:1"], dimensions: ["dim:11"]}); diff --git a/src/core/engine.ts b/src/core/engine.ts index a4e4a5fd..a7b3bf06 100644 --- a/src/core/engine.ts +++ b/src/core/engine.ts @@ -18,28 +18,74 @@ * along with blend. If not, see <http://www.gnu.org/licenses/>. */ -import { Query } from "../common/query"; +import { Dimension } from "./dimension"; +import { Metric } from "./metric"; import { View } from "./view"; -import { AggregationType } from "../common/aggregationType"; +import { AggregationType } from "../common/types"; +import { Query } from "../common/query"; export class Engine { - private views: View[]; + private views: View[] = []; + private metrics: Metric[] = []; + private dimensions: Dimension[] = []; + + constructor () { } + + public getViews() { + return this.views; + } + + public addView(view: View) { + this.views.push(view); + return view; + } + + public addMetric(metric: Metric) { + this.metrics.push(metric); + return metric; + } + + public getMetricByName(name: string) { + let result = this.metrics.find(metric => metric.name === name); + + if (!result) { + throw new Error('The metric named ' + name + ' was not found'); + } - constructor (v: View[]) { - this.views = v; + return result; + } + + public addDimension(dimension: Dimension) { + this.dimensions.push(dimension); + return dimension; + } + + public getDimensionByName(name: string) { + let result = this.dimensions.find(dimension => dimension.name === name); + + if (!result) { + throw new Error('The dimension named ' + name + ' was not found'); + } + + return result; + } + + public query (q: Query) { + return this.selectOptimalView(q); } - public query (q: Query) { return this.selectOptimalView(q); } - public insert (data: any) { /*to Implement*/ } private selectOptimalView (q: Query) { let objective = q.metrics.concat(q.dimensions); let optimalViews: View[] = []; - let activeViews = this.views; + let activeViews = this.getViews(); - // Run this block until all metrics and dimmensions are covered + // run this block until all metrics and dimmensions are covered while (objective.length > 0) { let bestView: View; let shortestDistance = objective.length + 1; + + // remove views from the activeViews if they don't intersect + // with the objective activeViews = activeViews.filter((view: View) => { let cover = view.metrics.concat(view.dimensions); let intersection = cover.filter((item: string) => { @@ -62,23 +108,28 @@ export class Engine { }); if (shortestDistance === objective.length + 1) { - throw new Error ("Engine views cannot cover the query"); + throw new Error("Engine views cannot cover the query"); } optimalViews.push(bestView); - //remove metrics and dimensions corevered by the bestView + // remove metrics and dimensions corvered by the bestView from the + // objective (i.e. the object is already met for those metrics/dimensions) objective = objective.filter((item: string) => { let cover = bestView.dimensions.concat(bestView.metrics); return cover.indexOf(item) === -1; }); } + if (optimalViews.length === 1) { + // if there is a single view that covers the query, we just return it return optimalViews.pop(); } - else { + // if more than one view is necessary to cover the query, + // we need to compose them into a new singular virtual view + let map = new Map(); optimalViews.forEach((view: View) => { view.aggregationMap.forEach((value: AggregationType, key: string) => { @@ -95,7 +146,7 @@ export class Engine { }; let view = new View(options); - this.views.push(view); + this.addView(view); return view; } } diff --git a/src/core/metric.ts b/src/core/metric.ts new file mode 100644 index 00000000..adbabeaf --- /dev/null +++ b/src/core/metric.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blend. + * + * blend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blend. If not, see <http://www.gnu.org/licenses/>. + */ + +import { AggregationType } from "../common/types"; + +export interface MetricOptions { + name: string; + aggregation: AggregationType; +} + +export class Metric { + public readonly name: string; + public readonly aggregation: AggregationType; + + constructor(options: MetricOptions) { + this.name = options.name; + this.aggregation = options.aggregation; + } +} diff --git a/src/core/view.ts b/src/core/view.ts index 34716d22..b1f5fb60 100644 --- a/src/core/view.ts +++ b/src/core/view.ts @@ -18,40 +18,33 @@ * along with blend. If not, see <http://www.gnu.org/licenses/>. */ +import { Dimension } from "./dimension"; +import { Metric } from "./metric"; import { Hash } from "../util/hash"; -import { AggregationType } from "../common/aggregationType"; export interface ViewOptions { - metrics: string[]; - dimensions: string[]; - materialized: boolean; - aggregationMap: Map <string, AggregationType>; - childViews: View[]; + metrics: Metric[]; + dimensions: Dimension[]; + materialized?: boolean; + childViews?: View[]; } export class View { public readonly id: string; - public readonly metrics: string[]; - public readonly dimensions: string[]; + public readonly metrics: Metric[]; + public readonly dimensions: Dimension[]; public readonly materialized: boolean; - public readonly aggregationMap: Map<string, AggregationType>; - public childViews: View[]; constructor (options: ViewOptions) { this.metrics = options.metrics; this.dimensions = options.dimensions; - this.materialized = options.materialized; - this.childViews = options.childViews; - this.aggregationMap = options.aggregationMap; - this.id = Hash.sha1(options.metrics.sort(), options.dimensions.sort()); - } - - public getAggregatationType(metric: string): AggregationType { - if (this.aggregationMap.has(metric)) { - return AggregationType.NONE; - } + this.materialized = options.materialized || true; + this.childViews = options.childViews || []; - return this.aggregationMap.get(metric); + // calculate the id of the view based on it's metrics and dimensions + let metricsNames = this.metrics.map(metric => metric.name); + let dimensionsNames = this.dimensions.map(dimension => dimension.name); + this.id = Hash.sha1(metricsNames.concat(dimensionsNames)); } } diff --git a/src/util/hash.ts b/src/util/hash.ts index bfe77767..0ca3c606 100644 --- a/src/util/hash.ts +++ b/src/util/hash.ts @@ -39,7 +39,7 @@ export class Hash { } }) .sort() - .map((objStr) => { + .forEach((objStr) => { hash.update(objStr); }); -- GitLab From d0e10959ed9edd7928ea72b0dbfb6fc083a0afd1 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br> Date: Fri, 18 Nov 2016 09:06:45 -0200 Subject: [PATCH 3/3] Fix engine to use name instead of reference Signed-off-by: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br> --- src/common/query.ts | 7 +++++-- src/core/engine.spec.ts | 42 +++++++++++++++++++++++------------------ src/core/engine.ts | 26 +++++++++++-------------- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/common/query.ts b/src/common/query.ts index 70a46d5b..e56ff85b 100644 --- a/src/common/query.ts +++ b/src/common/query.ts @@ -18,8 +18,11 @@ * along with blend. If not, see <http://www.gnu.org/licenses/>. */ +import { Metric } from "../core/metric"; +import { Dimension } from "../core/dimension"; + export interface Query { - public metrics: string[]; - public dimensions: string[]; + public metrics: Metric[]; + public dimensions: Dimension[]; } diff --git a/src/core/engine.spec.ts b/src/core/engine.spec.ts index e59087d6..2b9d24b6 100644 --- a/src/core/engine.spec.ts +++ b/src/core/engine.spec.ts @@ -24,8 +24,6 @@ import { Engine } from "./engine"; import { Metric } from "./metric"; import { Dimension } from "./dimension"; import { View } from "./view"; -import { ViewOptions } from "./view"; -import { Query } from "../common/query"; import { AggregationType } from "../common/types"; describe("engine class", () => { @@ -41,17 +39,19 @@ describe("engine class", () => { const met8 = new Metric({ name: "met:8", aggregation: AggregationType.COUNT }); const met9 = new Metric({ name: "met:9", aggregation: AggregationType.SUM }); const met10 = new Metric({ name: "met:10", aggregation: AggregationType.COUNT }); + const met11 = new Metric({ name: "met:11", aggregation: AggregationType.COUNT }); - const dim1 = new Dimension({ name: "dim:0" }); - const dim2 = new Dimension({ name: "dim:1" }); - const dim3 = new Dimension({ name: "dim:2" }); - const dim4 = new Dimension({ name: "dim:3" }); - const dim5 = new Dimension({ name: "dim:4" }); - const dim6 = new Dimension({ name: "dim:5" }); - const dim7 = new Dimension({ name: "dim:6" }); - const dim8 = new Dimension({ name: "dim:7" }); - const dim9 = new Dimension({ name: "dim:8" }); - const dim10 = new Dimension({ name: "dim:9" }); + const dim1 = new Dimension({ name: "dim:1" }); + const dim2 = new Dimension({ name: "dim:2" }); + const dim3 = new Dimension({ name: "dim:3" }); + const dim4 = new Dimension({ name: "dim:4" }); + const dim5 = new Dimension({ name: "dim:5" }); + const dim6 = new Dimension({ name: "dim:6" }); + const dim7 = new Dimension({ name: "dim:7" }); + const dim8 = new Dimension({ name: "dim:8" }); + const dim9 = new Dimension({ name: "dim:9" }); + const dim10 = new Dimension({ name: "dim:10" }); + const dim11 = new Dimension({ name: "dim:11" }); engine.addMetric(met1); engine.addMetric(met2); @@ -102,7 +102,13 @@ describe("engine class", () => { childViews: [views[7], views[8], views[9]] })); + views.forEach((view) => engine.addView(view)); + it("should be create a fill that cover the query and has 4 children", () => { + let query = { + metrics : [met1, met2, met3, met4, met5, met6, met7, met8, met9, met10] + , dimensions : [dim1, dim2, dim3, dim4, dim5, dim6, dim7, dim8, dim9, dim10] + }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); @@ -114,11 +120,11 @@ describe("engine class", () => { expect(optimalView.metrics.length === 10); expect(optimalView.dimensions.length === 10); expect(optimalView.childViews.length === 4); - let metAux: number[] = optimalView.metrics.sort().map((item: string) => { - return Number(item.split(":")[1]); + let metAux: number[] = optimalView.metrics.sort().map((met: Metric) => { + return Number(met.name.split(":")[1]); }); - let dimAux: number[] = optimalView.dimensions.sort().map((item: string) => { - return Number(item.split(":")[1]); + let dimAux: number[] = optimalView.dimensions.sort().map((dim: Dimension) => { + return Number(dim.name.split(":")[1]); }); for (let i: number = 1; i <= 10; ++i) { expect(dimAux[i] === i); @@ -128,7 +134,7 @@ describe("engine class", () => { it("should throw an exception, query with non-existent metric", () => { let error: boolean = false; try { - engine.query({metrics: ["met:11"], dimensions: ["dim:1"]}); + engine.query({metrics: [met11], dimensions: [dim1]}); } catch (e){ error = true; @@ -141,7 +147,7 @@ describe("engine class", () => { it("should throw an exception, query with non-existent dimension", () => { let error: boolean = false; try { - engine.query({metrics: ["met:1"], dimensions: ["dim:11"]}); + engine.query({metrics: [met1], dimensions: [dim11]}); } catch (e){ error = true; diff --git a/src/core/engine.ts b/src/core/engine.ts index a7b3bf06..e810f570 100644 --- a/src/core/engine.ts +++ b/src/core/engine.ts @@ -21,7 +21,6 @@ import { Dimension } from "./dimension"; import { Metric } from "./metric"; import { View } from "./view"; -import { AggregationType } from "../common/types"; import { Query } from "../common/query"; export class Engine { @@ -49,7 +48,7 @@ export class Engine { let result = this.metrics.find(metric => metric.name === name); if (!result) { - throw new Error('The metric named ' + name + ' was not found'); + throw new Error("The metric named " + name + " was not found"); } return result; @@ -64,7 +63,7 @@ export class Engine { let result = this.dimensions.find(dimension => dimension.name === name); if (!result) { - throw new Error('The dimension named ' + name + ' was not found'); + throw new Error("The dimension named " + name + " was not found"); } return result; @@ -75,7 +74,9 @@ export class Engine { } private selectOptimalView (q: Query) { - let objective = q.metrics.concat(q.dimensions); + let metricsName = q.metrics.map((met) => met.name); + let dimensionsName = q.dimensions.map((dim) => dim.name); + let objective = metricsName.concat(dimensionsName); let optimalViews: View[] = []; let activeViews = this.getViews(); @@ -87,7 +88,9 @@ export class Engine { // remove views from the activeViews if they don't intersect // with the objective activeViews = activeViews.filter((view: View) => { - let cover = view.metrics.concat(view.dimensions); + metricsName = view.metrics.map((met) => met.name); + dimensionsName = view.dimensions.map((dim) => dim.name); + let cover = metricsName.concat(dimensionsName); let intersection = cover.filter((item: string) => { return objective.indexOf(item) !== -1; }); @@ -116,12 +119,13 @@ export class Engine { // remove metrics and dimensions corvered by the bestView from the // objective (i.e. the object is already met for those metrics/dimensions) objective = objective.filter((item: string) => { - let cover = bestView.dimensions.concat(bestView.metrics); + metricsName = bestView.metrics.map((met) => met.name); + dimensionsName = bestView.dimensions.map((dim) => dim.name); + let cover = dimensionsName.concat(metricsName); return cover.indexOf(item) === -1; }); } - if (optimalViews.length === 1) { // if there is a single view that covers the query, we just return it return optimalViews.pop(); @@ -130,18 +134,10 @@ export class Engine { // if more than one view is necessary to cover the query, // we need to compose them into a new singular virtual view - let map = new Map(); - optimalViews.forEach((view: View) => { - view.aggregationMap.forEach((value: AggregationType, key: string) => { - map.set(key, value); - }); - }); - let options = { metrics: q.metrics, dimensions: q.dimensions, materialized: false, - aggregationMap: map, childViews: optimalViews }; -- GitLab