diff --git a/src/common/query.ts b/src/common/query.ts index 70a46d5b749af87c11468de0bc53e47cea137b67..e56ff85b9bbcb67cdd2abf0b89c710fbc6bf4b73 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 e59087d68e28acba644f3363a99d7615e274a0ee..2b9d24b6a0891c1705b34c2a77590d5d284b3f19 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 a7b3bf06f1dc1072e5fd90967af12b49665359aa..e810f570b64e1cf022961217d766c6d460dbad1e 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 };