From f26a44196bf51e10445e9bc5a2eca1395b1185cb Mon Sep 17 00:00:00 2001
From: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
Date: Thu, 3 Aug 2017 11:55:16 -0300
Subject: [PATCH] Issue #27: Fix count aggregation

Signed-off-by: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
---
 config/ci_test.yaml.example  |  9 +++++++++
 src/adapter/postgres.spec.ts | 21 +++++++++++++++++++++
 src/adapter/postgres.ts      |  9 +++++----
 src/core/engine.ts           |  1 +
 src/core/view.ts             |  3 +++
 src/util/configParser.ts     |  2 ++
 src/util/graph.spec.ts       | 15 +++++++++++++++
 test/scenario.ts             | 28 +++++++++++++++++++++++++++-
 8 files changed, 83 insertions(+), 5 deletions(-)

diff --git a/config/ci_test.yaml.example b/config/ci_test.yaml.example
index 4ec663f5..4a6d5858 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 370c7bdf..cae97a84 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 c7353aa2..685963c4 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 39fbfbb5..f96a5b78 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 6de687e4..dba8c040 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 1aaf8c66..3064e7b1 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 17de06d0..6b0cc095 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 f5d778d8..45a428c1 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 = {
-- 
GitLab