From 47edbd303f6e889b2ea887ef1c7cc63cb1b5f972 Mon Sep 17 00:00:00 2001
From: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
Date: Fri, 18 Aug 2017 11:30:12 -0300
Subject: [PATCH] Issue #39: Add MAX and MIN aggregations

Signed-off-by: Lucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
---
 config/ci_test.yaml.example       | 10 +++++++
 src/adapter/postgres.spec.ts      |  2 ++
 src/adapter/postgres.ts           |  4 +++
 src/common/types.ts               |  4 ++-
 src/core/engine.spec.ts           | 47 ++++++++++++++-----------------
 src/util/configParser.ts          |  4 +++
 test/postgres/fixtures/view8.json | 10 +++----
 test/scenario.ts                  |  6 ++--
 8 files changed, 52 insertions(+), 35 deletions(-)

diff --git a/config/ci_test.yaml.example b/config/ci_test.yaml.example
index 8bab1978..f9f7ef30 100644
--- a/config/ci_test.yaml.example
+++ b/config/ci_test.yaml.example
@@ -93,6 +93,8 @@ schema:
                 - "dim:6"
             metrics:
                 - "met:9"
+                - "met:10"
+                - "met:11"
         -
             alias: "view 9"
             data: "test/postgres/fixtures/view9.json"
@@ -146,6 +148,14 @@ schema:
             name: "met:9"
             dataType: "integer"
             aggregation: "count"
+        -
+            name: "met:10"
+            dataType: "integer"
+            aggregation: "max"
+        -
+            name: "met:11"
+            dataType: "integer"
+            aggregation: "min"
     dimensions:
         -
             name: "dim:0"
diff --git a/src/adapter/postgres.spec.ts b/src/adapter/postgres.spec.ts
index e002335d..4db63388 100644
--- a/src/adapter/postgres.spec.ts
+++ b/src/adapter/postgres.spec.ts
@@ -166,6 +166,8 @@ describe("postgres adapter", () => {
             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);
+            expect(parseInt(result[0]["met:10"], 10)).to.be.equal(5);
+            expect(parseInt(result[0]["met:11"], 10)).to.be.equal(1);
             done();
         });
     });
diff --git a/src/adapter/postgres.ts b/src/adapter/postgres.ts
index 0c326896..9035c374 100644
--- a/src/adapter/postgres.ts
+++ b/src/adapter/postgres.ts
@@ -213,6 +213,10 @@ export class PostgresAdapter extends Adapter {
                 return "AVG";
             case AggregationType.COUNT:
                 return (origin) ? "COUNT" : "SUM";
+            case AggregationType.MAX:
+                return "MAX";
+            case AggregationType.MIN:
+                return "MIN";
             default:
                 return  "";
         }
diff --git a/src/common/types.ts b/src/common/types.ts
index 9757e7af..661fce35 100644
--- a/src/common/types.ts
+++ b/src/common/types.ts
@@ -22,7 +22,9 @@ export enum AggregationType {
    NONE,
    SUM,
    AVG,
-   COUNT
+   COUNT,
+   MAX,
+   MIN
 };
 
 export enum RelationType {
diff --git a/src/core/engine.spec.ts b/src/core/engine.spec.ts
index 4498640c..95812055 100644
--- a/src/core/engine.spec.ts
+++ b/src/core/engine.spec.ts
@@ -22,7 +22,6 @@ import { expect } from "chai";
 
 import { Engine } from "./engine";
 import { Metric } from "./metric";
-import { Dimension } from "./dimension";
 import { Filter, FilterOperator } from "./filter";
 import { Clause } from "./clause";
 import { View } from "./view";
@@ -31,25 +30,33 @@ import { engineScenario } from "../../test/scenario";
 describe("engine class", () => {
     const engine = new Engine();
 
-    const met = engineScenario.metrics;
-    const dim = engineScenario.dimensions;
-    const subdim = engineScenario.subDimensions;
+    const met = engineScenario.metrics.sort((a, b) => {
+        const aValue = parseInt(a.name.split(":")[1], 10);
+        const bValue = parseInt(b.name.split(":")[1], 10);
+        return aValue - bValue;
+    });
+    const dim = engineScenario.dimensions.sort((a, b) => {
+        const aValue = parseInt(a.name.split(":")[1], 10);
+        const bValue = parseInt(b.name.split(":")[1], 10);
+        return aValue - bValue;
+    });
+    const subdim = engineScenario.subDimensions.sort((a, b) => {
+        const aValue = parseInt(a.name.split(":")[1], 10);
+        const bValue = parseInt(b.name.split(":")[1], 10);
+        return aValue - bValue;
+    });
     const views = engineScenario.views;
 
-    for (let i = 0; i < 10; ++i) {
-        engine.addMetric(met[i]);
-        engine.addDimension(dim[i]);
-        if (i < 5) {
-            engine.addDimension(subdim[i]);
-        }
-    }
+    met.forEach((item) => engine.addMetric(item));
+    dim.forEach((item) => engine.addDimension(item));
+    subdim.forEach((item) => engine.addDimension(item));
 
     views.forEach((view) => engine.addView(view));
 
     it("should be create a fill that cover all metrics and dimensions", () => {
         let query = {
-            metrics : met
-            , dimensions : dim
+            metrics : met.slice(0)
+            , dimensions : dim.slice(0)
         };
         let optimalView = engine.query(query);
         expect(optimalView).to.be.an("object");
@@ -59,20 +66,8 @@ describe("engine class", () => {
         expect(optimalView.metrics).to.be.an("array");
         expect(optimalView.dimensions).to.be.an("array");
         expect(optimalView.childViews).to.be.an("array");
-        expect(optimalView.metrics).to.have.length(10);
+        expect(optimalView.metrics).to.have.length(12);
         expect(optimalView.dimensions).to.have.length(9);
-        let metAux: number[] = optimalView.metrics.sort().map((item: Metric) => {
-            return Number(item.name.split(":")[1]);
-        });
-        let dimAux: number[] = optimalView.dimensions.sort().map((item: Dimension) => {
-            return Number(item.name.split(":")[1]);
-        });
-
-        for (let i: number = 0; i < 9; ++i) {
-            expect(dimAux[i]).to.be.equal(i);
-            expect(metAux[i]).to.be.equal(i);
-        }
-        expect(metAux[9]).to.be.equal(9);
     });
     it("should throw an exception, query with non-existent metric", () => {
         let error: boolean = false;
diff --git a/src/util/configParser.ts b/src/util/configParser.ts
index 83105036..e2d10ec9 100644
--- a/src/util/configParser.ts
+++ b/src/util/configParser.ts
@@ -271,6 +271,10 @@ export class ConfigParser {
                 return AggregationType.AVG;
             case "count":
                 return AggregationType.COUNT;
+            case "min":
+                return AggregationType.MIN;
+            case "max":
+                return AggregationType.MAX;
             default:
                 return AggregationType.NONE;
         }
diff --git a/test/postgres/fixtures/view8.json b/test/postgres/fixtures/view8.json
index 40725b32..6d4641c9 100644
--- a/test/postgres/fixtures/view8.json
+++ b/test/postgres/fixtures/view8.json
@@ -1,7 +1,7 @@
 [
-{"dim:5":"t","dim:6":"1","met:9":"1"},
-{"dim:5":"t","dim:6":"2","met:9":"2"},
-{"dim:5":"t","dim:6":"3","met:9":"3"},
-{"dim:5":"f","dim:6":"4","met:9":"4"},
-{"dim:5":"f","dim:6":"5","met:9":"5"}
+{"dim:5":"t","dim:6":"1","met:9":"1","met:10":"1","met:11":"1"},
+{"dim:5":"t","dim:6":"2","met:9":"2","met:10":"2","met:11":"2"},
+{"dim:5":"t","dim:6":"3","met:9":"3","met:10":"3","met:11":"3"},
+{"dim:5":"f","dim:6":"4","met:9":"4","met:10":"4","met:11":"4"},
+{"dim:5":"f","dim:6":"5","met:9":"5","met:10":"5","met:11":"5"}
 ]
diff --git a/test/scenario.ts b/test/scenario.ts
index 0d1be6fb..0a48d94e 100644
--- a/test/scenario.ts
+++ b/test/scenario.ts
@@ -127,7 +127,7 @@ const clauses: { [key: string]: Clause }  = {
 };
 
 const wrongMet = new Metric({
-    name: "met:11",
+    name: "met:-1",
     aggregation: AggregationType.COUNT,
     dataType: "integer"
 });
@@ -204,11 +204,11 @@ const dateView = new View({
 });
 
 const aggrView = new View({
-    metrics: [mets[0], mets[1], mets[6]],
+    metrics: [mets[0], mets[1], mets[6], mets[10], mets[11]],
     dimensions: [],
     materialized: false,
     origin: false,
-    childViews: [views[0], views[2], views[4]]
+    childViews: [views[0], views[2], views[3], views[4], views[7], views[8]]
 });
 
 const clauseView = new View({
-- 
GitLab