diff --git a/config/ci_test.yaml.example b/config/ci_test.yaml.example index 4ec663f5afb3120d8e12a4e5d0d790bf68d59862..4a6d58584c241d0fdb69859df7a45cad4939bcc8 100644 --- a/config/ci_test.yaml.example +++ b/config/ci_test.yaml.example @@ -17,6 +17,7 @@ schema: - alias: "View 0" data: "test/postgres/fixtures/view0.json" + origin: true dimensions: - "dim:0" - "dim:7" @@ -27,6 +28,7 @@ schema: - alias: "View 1" data: "test/postgres/fixtures/view1.json" + origin: true dimensions: - "dim:1" - "dim:8" @@ -36,6 +38,7 @@ schema: - alias: "View 2" data: "test/postgres/fixtures/view2.json" + origin: true dimensions: - "dim:2" metrics: @@ -44,6 +47,7 @@ schema: - alias: "View 3" data: "test/postgres/fixtures/view3.json" + origin: true dimensions: - "dim:2" - "dim:3" @@ -51,6 +55,7 @@ schema: - alias: "View 4" data: "test/postgres/fixtures/view4.json" + origin: true dimensions: - "dim:2" - "dim:7" @@ -58,6 +63,7 @@ schema: - alias: "View 5" data: "test/postgres/fixtures/view5.json" + origin: true dimensions: - "dim:3" metrics: @@ -65,6 +71,7 @@ schema: - alias: "View 6" data: "test/postgres/fixtures/view6.json" + origin: true dimensions: - "dim:4" metrics: @@ -72,6 +79,7 @@ schema: - alias: "View 7" data: "test/postgres/fixtures/view7.json" + origin: true dimensions: - "dim:4" - "dim:5" @@ -79,6 +87,7 @@ schema: - alias: "View 8" data: "test/postgres/fixtures/view8.json" + origin: true dimensions: - "dim:5" - "dim:6" diff --git a/src/adapter/postgres.spec.ts b/src/adapter/postgres.spec.ts index 370c7bdfdf28da1837ec8d8cf232212093069bee..cae97a84b9676aef05865d63c8a43ab783a279cf 100644 --- a/src/adapter/postgres.spec.ts +++ b/src/adapter/postgres.spec.ts @@ -148,4 +148,25 @@ describe("postgres adapter", () => { done(); }); }); + it("should get data from view with all types of agreggation", (done) => { + let view = adapterScenario.aggrView; + adapter.getDataFromView(view, (err, result) => { + expect(err).to.be.a("null"); + expect(result).to.be.an("array"); + expect(result).to.have.length(1); + expect(result[0]).to.be.an("object"); + let keys: string[] = []; + keys = keys.concat(view.metrics.map((item) => item.name)); + keys = keys.concat(view.dimensions.map((item) => item.name)); + result.forEach((row) => { + expect(row).to.be.an("object"); + expect(row).to.have.all.keys(keys); + }); + + expect(parseInt(result[0]["met:0"], 10)).to.be.equal(15); + expect(parseInt(result[0]["met:1"], 10)).to.be.equal(3); + expect(parseInt(result[0]["met:6"], 10)).to.be.equal(5); + done(); + }); + }); }); diff --git a/src/adapter/postgres.ts b/src/adapter/postgres.ts index c7353aa27f698e2129a212ea131eabaceb87e31b..685963c48d1c85d59647f35c9289ea065584446a 100644 --- a/src/adapter/postgres.ts +++ b/src/adapter/postgres.ts @@ -66,7 +66,7 @@ export class PostgresAdapter extends Adapter { view. So is possible only get useful data in the sub-querys. */ let strMetrics = metrics.map((metric) => { - let func = this.getAggregateFunction(metric.aggregation); + let func = this.getAggregateFunction(metric.aggregation, view.origin); let quotedName = "\"" + metric.name + "\""; let extMetric = func + "(" + quotedName + ") AS " + quotedName; return extMetric; @@ -167,7 +167,8 @@ export class PostgresAdapter extends Adapter { }); child.metrics.forEach((metric: Metric) => { - let func = this.getAggregateFunction(metric.aggregation); + // Only materialized views can have origin as true + let func = this.getAggregateFunction(metric.aggregation, false); let quotedName = "\"" + metric.name + "\""; let extMetric = func + "(" + child.alias + "." + quotedName + ") AS " + quotedName; elements.push(extMetric); @@ -189,14 +190,14 @@ export class PostgresAdapter extends Adapter { } } - private getAggregateFunction(aggrType: AggregationType): string { + private getAggregateFunction(aggrType: AggregationType, origin: boolean): string { switch (aggrType) { case AggregationType.SUM: return "SUM"; case AggregationType.AVG: return "AVG"; case AggregationType.COUNT: - return "COUNT"; + return (origin) ? "COUNT" : "SUM"; default: return ""; } diff --git a/src/core/engine.ts b/src/core/engine.ts index 39fbfbb523830f7e3abab9b2f41c256b94bc901d..f96a5b782c2341a72351c4d60b6760f91c76086b 100644 --- a/src/core/engine.ts +++ b/src/core/engine.ts @@ -112,6 +112,7 @@ export class Engine { metrics: q.metrics, dimensions: q.dimensions, materialized: false, + origin: false, // Never a dynamic generated view will be origin childViews: optimalViews }; diff --git a/src/core/view.ts b/src/core/view.ts index 6de687e4cf7bf098e6568cf4b253cacd3cd9b8a5..dba8c040398b31fe0ed4ee061863beccdd2f8b3a 100644 --- a/src/core/view.ts +++ b/src/core/view.ts @@ -36,6 +36,7 @@ export interface ChildView { export interface ViewOptions { metrics: Metric[]; dimensions: Dimension[]; + origin: boolean; materialized?: boolean; childViews?: ChildView[]; } @@ -45,12 +46,14 @@ export class View { public readonly metrics: Metric[]; public readonly dimensions: Dimension[]; public readonly materialized: boolean; + public readonly origin: boolean; public childViews: ChildView[]; constructor (options: ViewOptions) { this.metrics = options.metrics.sort(); this.dimensions = options.dimensions.sort(); this.materialized = options.materialized || false; + this.origin = options.origin || false; this.childViews = (options.childViews) ? options.childViews : []; // calculate the id of the view based on it's metrics and dimensions diff --git a/src/util/configParser.ts b/src/util/configParser.ts index 1aaf8c66aa401c61a33f1987e704b531d499ea02..3064e7b13f27ac14380020f276efe65e4376b739 100644 --- a/src/util/configParser.ts +++ b/src/util/configParser.ts @@ -29,6 +29,7 @@ import * as yaml from "js-yaml"; export interface ViewParsingOptions { alias: string; data: string; + origin?: boolean; dimensions: string[]; metrics: string[]; } @@ -127,6 +128,7 @@ export class ConfigParser { metrics: [], dimensions: [], materialized: true, + origin: opts.origin, childViews: [], }; diff --git a/src/util/graph.spec.ts b/src/util/graph.spec.ts index 17de06d000fc78b8fe96b739a70d486700193f55..6b0cc095ba6838d50b5f40ee3bc1efc304afcd54 100644 --- a/src/util/graph.spec.ts +++ b/src/util/graph.spec.ts @@ -93,16 +93,19 @@ describe("graph class", () => { new View({ metrics: [], dimensions: [dims[0], dims[1]], + origin: true, materialized: true }), new View({ metrics: [], dimensions: [dims[2], dims[3]], + origin: true, materialized: true }), new View({ metrics: [], dimensions: dims, + origin: true, materialized: true }) ]; @@ -127,6 +130,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: [dims[0], dims[1]], + origin: true, materialized: true }); @@ -148,6 +152,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: dims, + origin: true, materialized: true }); @@ -169,6 +174,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: [dim], + origin: true, materialized: true }); @@ -186,6 +192,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: [dim], + origin: true, materialized: true }); @@ -220,31 +227,37 @@ describe("graph class", () => { new View({ metrics: [mets[0]], dimensions: [dims[0]], + origin: true, materialized: true }), new View({ metrics: [mets[1]], dimensions: [dims[1]], + origin: true, materialized: true }), new View({ metrics: [mets[2]], dimensions: [dims[2]], + origin: true, materialized: true }), new View({ metrics: [], dimensions: dims, + origin: true, materialized: true }), new View({ metrics: mets, dimensions: dims, + origin: true, materialized: true }), new View({ metrics: [mets[0], mets[1]], dimensions: [dims[0], dims[1]], + origin: true, materialized: true }), ]; @@ -286,6 +299,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: [dims[0]], + origin: true, materialized: true }); @@ -324,6 +338,7 @@ describe("graph class", () => { let view = new View({ metrics: [], dimensions: [dims[0]], + origin: true, materialized: true }); diff --git a/test/scenario.ts b/test/scenario.ts index f5d778d8ffe752c10c240bf9b762d9ff3455f620..45a428c19f13a2e1a8bccf195897380281f846d6 100644 --- a/test/scenario.ts +++ b/test/scenario.ts @@ -41,6 +41,7 @@ interface AdapterScenario { subDimensionView: View; join4View: View; dateView: View; + aggrView: View; } interface DataCtrlScenario { @@ -128,6 +129,7 @@ const dateView = new View({ metrics: [], dimensions: dateSubDim, materialized: false, + origin: false, childViews: [{ view: views[0], metrics: [], @@ -135,10 +137,30 @@ const dateView = new View({ }] }); +const aggrView = new View({ + metrics: [mets[0], mets[1], mets[6]], + dimensions: [], + materialized: false, + origin: false, + childViews: [ + { + view: views[0], + metrics: [mets[0], mets[1]], + dimensions: [] + }, + { + view: views[2], + metrics: [mets[6]], + dimensions: [] + } + ] +}); + const subDimView = new View({ metrics: [mets[0]], dimensions: [subdims[0], subdims[1], dims[7], dims[8]], materialized: false, + origin: false, childViews: [ { view: views[0], @@ -162,6 +184,7 @@ const join4View = new View({ metrics: [mets[0], mets[1], mets[2], mets[3], mets[4], mets[5]], dimensions: [dims[2], dims[7], dims[8]], materialized: false, + origin: false, childViews: [ { view: views[0], @@ -190,6 +213,7 @@ const noSelView = new View({ metrics: [mets[0], mets[3]], dimensions: [], materialized: false, + origin: false, childViews: [ { view: views[0], @@ -208,6 +232,7 @@ const withSelView = new View({ metrics: [mets[0], mets[1]], dimensions: [dims[7], dims[8]], materialized: false, + origin: false, childViews: [ { view: views[0], @@ -237,7 +262,8 @@ export const adapterScenario: AdapterScenario = { withSelectionView: withSelView, subDimensionView: subDimView, join4View: join4View, - dateView: dateView + dateView: dateView, + aggrView: aggrView }; export const dataCtrlScenario: DataCtrlScenario = {