From a1bac963cd0601f9acf990fc81ed19ac65c711ce Mon Sep 17 00:00:00 2001
From: rafaelatc3sl <rpd17@c3sl>
Date: Mon, 5 Nov 2018 11:03:49 -0200
Subject: [PATCH] Issue #90: Add class Query

Signed-off-by: rafaelatc3sl <rpd17@c3sl>
---
 src/common/query.ts     |  23 +++++++-
 src/core/engine.ts      |  10 ++--
 src/util/graph.spec.ts  |  20 ++++---
 src/util/graph.ts       |   4 +-
 src/util/viewHandler.ts |  11 ++--
 test/scenario.ts        | 119 ++++++++++++++++++++++++++++------------
 6 files changed, 131 insertions(+), 56 deletions(-)

diff --git a/src/common/query.ts b/src/common/query.ts
index 4cb0a9e9..85f158d8 100644
--- a/src/common/query.ts
+++ b/src/common/query.ts
@@ -25,7 +25,7 @@ import { Clause } from "../core/clause";
 /**
  * Internal representation of a query in BlenDB perspective.
  */
-export interface Query {
+export interface queryOpts {
     /** Set of metrics of the query. */
     metrics: Metric[];
     /** Set of dimensions of the query. */
@@ -35,3 +35,24 @@ export interface Query {
     /** List of metrics and dimensions to sort the query. */
     sort?: (Metric|Dimension)[];
 }
+
+export class Query {
+    /** Set of metrics of the query. */
+    public metrics: Metric[];
+    /** Set of dimensions of the query. */
+    public dimensions: Dimension[];
+    /** Set of clauses of the query. */
+    public clauses: Clause[];
+    /** List of metrics and dimensions to sort the query. */
+    public sort: (Metric|Dimension)[];
+    /**
+     * Create Query
+     * @param opts - Parameters required to create a query.
+     */
+    constructor(opts: queryOpts){
+        this.metrics = opts.metrics;
+        this.dimensions = opts.dimensions;
+        this.clauses = (opts.clauses) ? opts.clauses : [];
+        this.sort = (opts.sort) ? opts.sort : [];
+    }
+}
diff --git a/src/core/engine.ts b/src/core/engine.ts
index 712d7220..fd7ae68a 100644
--- a/src/core/engine.ts
+++ b/src/core/engine.ts
@@ -283,8 +283,8 @@ export class Engine {
                 queries.push({
                     metrics: [q.metrics[i]],
                     dimensions: q.dimensions,
-                    clauses: (q.clauses) ? q.clauses : [],
-                    sort: (q.sort) ? q.sort : []
+                    clauses: q.clauses,
+                    sort: q.sort,
                 });
             }
             const views = queries.map((query) => {
@@ -295,11 +295,11 @@ export class Engine {
         }
 
         else {
-            let query = {
+            let query: Query = {
                 metrics: q.metrics,
                 dimensions: q.dimensions,
-                clauses: (q.clauses) ? q.clauses : [],
-                sort: (q.sort) ? q.sort : []
+                clauses: q.clauses,
+                sort: q.sort
             };
             return ViewHandler.growView(query, this.getCover(query));
         }
diff --git a/src/util/graph.spec.ts b/src/util/graph.spec.ts
index 8dc8ea98..b6621fed 100644
--- a/src/util/graph.spec.ts
+++ b/src/util/graph.spec.ts
@@ -193,7 +193,7 @@ describe("graph class", () => {
         });
 
         expect(g.addView(view)).to.be.true;
-        const query: Query = { metrics: [], dimensions: [dim] };
+        const query: Query = { metrics: [], dimensions: [dim], sort: [], clauses: []};
         let children = g.cover(query);
         expect(children).to.be.an("array");
         expect(children).to.have.length(1);
@@ -257,7 +257,8 @@ describe("graph class", () => {
             expect(g.addView(views[i])).to.be.true;
         }
 
-        const query: Query = { metrics: [mets[0], mets[1]], dimensions: [dims[0], dims[1]] };
+        const query: Query = { metrics: [mets[0], mets[1]], dimensions: [dims[0], dims[1]],
+            sort: [], clauses: [] };
         let children = g.cover(query);
         expect(children).to.be.an("array");
         expect(children).to.have.length(1);
@@ -296,7 +297,8 @@ describe("graph class", () => {
 
         expect(g.addView(view)).to.be.true;
 
-        const query: Query = { metrics: [], dimensions: [dims[1], dims[2]] };
+        const query: Query = { metrics: [], dimensions: [dims[1], dims[2]],
+            sort: [], clauses: [] };
         let children = g.cover(query);
         expect(children).to.be.an("array");
         expect(children).to.have.length(1);
@@ -335,7 +337,8 @@ describe("graph class", () => {
 
         expect(g.addView(view)).to.be.true;
 
-        const query: Query = { metrics: [], dimensions: [] };
+        const query: Query = { metrics: [], dimensions: [],
+            sort: [], clauses: [] };
         let children = g.cover(query);
         expect(children).to.be.an("array");
         expect(children).to.be.empty;
@@ -390,7 +393,8 @@ describe("graph class", () => {
         expect(g.addView(view2)).to.be.true;
         expect(g.addView(view3)).to.be.true;
 
-        const query: Query = { metrics: [], dimensions: dims, clauses: [clause2] };
+        const query: Query = { metrics: [], dimensions: dims, clauses: [clause2],
+            sort: []};
         let children = g.cover(query);
         expect(children).to.be.an("array");
         expect(children).to.have.length(2);
@@ -503,7 +507,8 @@ describe("graph class", () => {
 
         expect(g.addView(view0)).to.be.true;
 
-        const query: Query = { metrics: [], dimensions: dims, clauses: testClauses };
+        const query: Query = { metrics: [], dimensions: dims, clauses: testClauses,
+            sort: []};
         let children = g.cover(query);
         expect(children).to.have.length(1);
         expect(children[0].id === view0.id).to.be.true;
@@ -627,7 +632,8 @@ describe("graph class", () => {
             expect(g.addView(views[i])).to.be.true;
         }
 
-        const query: Query = { metrics: [], dimensions: dims, clauses: testClauses };
+        const query: Query = { metrics: [], dimensions: dims, clauses: testClauses,
+            sort: []};
         let children = g.cover(query);
         expect(children).to.have.length(1);
         expect(children[0].id === views[0].id).to.be.true;
diff --git a/src/util/graph.ts b/src/util/graph.ts
index 36a5ea10..3eb989b7 100644
--- a/src/util/graph.ts
+++ b/src/util/graph.ts
@@ -321,7 +321,7 @@ export class Graph {
     public cover(q: Query): View[] {
         const metrics = q.metrics;
         const dimensions = q.dimensions;
-        const clauses = (q.clauses) ? q.clauses : [];
+        const clauses = q.clauses
         let output: View[] = [];
         let verticesIds = this.verticesInQuery(q);
 
@@ -621,7 +621,7 @@ export class Graph {
     private verticesInQuery(q: Query): string[] {
         const metrics = q.metrics;
         const dimensions = q.dimensions;
-        const clauses = (q.clauses) ? q.clauses : [];
+        const clauses = q.clauses;
         let verticesIds = metrics.map((met) => met.name);
         verticesIds = verticesIds.concat(dimensions.map((dim) => dim.name));
         for (let i = 0; i < clauses.length; ++i) {
diff --git a/src/util/viewHandler.ts b/src/util/viewHandler.ts
index 6ecc43f0..72914f8e 100644
--- a/src/util/viewHandler.ts
+++ b/src/util/viewHandler.ts
@@ -60,8 +60,8 @@ export class ViewHandler {
                 metrics: q.metrics,
                 dimensions: q.dimensions,
                 origin: false,
-                clauses: (q.clauses) ? q.clauses : [],
-                sort: (q.sort) ? q.sort : [],
+                clauses: q.clauses,
+                sort: q.sort,
                 operation: {
                     opcode: Opcode.JOIN,
                     values: views.map((i) => i)
@@ -85,8 +85,8 @@ export class ViewHandler {
             metrics: q.metrics,
             dimensions: q.dimensions,
             origin: false,
-            clauses: (q.clauses) ? q.clauses : [],
-            sort: (q.sort) ? q.sort : [],
+            clauses: q.clauses,
+            sort: q.sort,
             operation: {
                 opcode: Opcode.REDUCE,
                 values: [view]
@@ -358,7 +358,8 @@ export class ViewHandler {
         const partialQuery: Query = {
             metrics: mets,
             dimensions: dims,
-            clauses: clauses
+            clauses: clauses,
+            sort: []
         };
 
         const partial = ViewHandler.queryJoin(partialQuery, [partial0, partial1]);
diff --git a/test/scenario.ts b/test/scenario.ts
index 570d6ef8..6f72da33 100644
--- a/test/scenario.ts
+++ b/test/scenario.ts
@@ -27,6 +27,7 @@ import { Clause } from "../src/core/clause";
 import { AggregationType, RelationType , DataType} from "../src/common/types";
 import { ViewHandler } from "../src/util/viewHandler";
 import { EngineScenario, AdapterScenario, DataCtrlScenario } from "../src/util/scenarioHandler";
+import { Query } from "../src/common/query";
 
 const configPath =  process.env.BLENDB_SCHEMA_FILE;
 const config = ConfigParser.parse(configPath);
@@ -50,8 +51,15 @@ for (let i in config.buildViews){
     views[view.alias] = view.view;
 }
 
+let emptyQuery: Query = {
+    metrics: [],
+    dimensions: [],
+    sort: [],
+    clauses: []
+};
+
 /**
- * Create new filters to use in clause and test 
+ * Create new filters to use in clause and test
  * the clauses
  */
 const filters: { [key: string]: Filter } = {
@@ -133,7 +141,7 @@ const subdims : {[key:string]: Dimension} = {
 
 // Clauses
 
-const clauses: { [key: string]: Clause }  = {    
+const clauses: { [key: string]: Clause }  = {
     "lastDay":
     new Clause({filters: [filters["equal"]]}),
     "undefined":
@@ -162,19 +170,22 @@ const clauses: { [key: string]: Clause }  = {
 const JoinWithAncestors = ViewHandler.growView({
     metrics: [mets["met:sell:count:quantity"]],
     dimensions: [dims["dim:seller:id"],dims["dim:provider:id"]],
-    clauses: []
+    clauses: [],
+    sort: []
 },[views["Sell"]]);
 
 const joinWithNoMetrics = ViewHandler.growView({
-    metrics: [],
+    metrics: emptyQuery.metrics,
     dimensions: [dims["dim:product:name"],dims["dim:seller:name"]],
-    clauses: []
+    clauses: [],
+    sort: []
 }, [views["Product"],views["Sell"],views["Seller"]]);
 
 const growOneView = ViewHandler.growView({
     metrics: [mets["met:seller:min:age"]],
     dimensions: [dims["dim:seller:name"],dims["dim:seller:sex"]],
-    clauses: []
+    clauses: [],
+    sort: []
 }, [views["Seller"]]);
 
 const multipleClause = ViewHandler.growView({
@@ -182,44 +193,56 @@ const multipleClause = ViewHandler.growView({
     dimensions: [dims["dim:sell:datein"],dims["dim:seller:name"],
     dims["dim:client:name"]],
     // and between filters => (A) and (B)
-    clauses: [clauses["expired"],clauses["averageBought"]]
+    clauses: [clauses["expired"],clauses["averageBought"]],
+    sort: []
 }, [views["Sell"],views["Seller"],views["Client"],views["Product"]]);
 
 const singleClause = ViewHandler.growView({
     metrics: [mets["met:sell:avg:quantity"]],
     dimensions: [dims["dim:sell:datein"],dims["dim:seller:name"],
     dims["dim:client:name"]],
-    clauses: [clauses["expiredAndAverage"]]
+    clauses: [clauses["expiredAndAverage"]],
+    sort: []
 }, [views["Sell"],views["Seller"],views["Client"],views["Product"]]);
 
 const equalfilter = ViewHandler.queryJoin({
-    metrics: [],
+    metrics: emptyQuery.metrics,
     dimensions: [dims["dim:client:name"],dims["dim:product:validity"]],
-    clauses: [clauses["lastDay"]]
+    clauses: [clauses["lastDay"]],
+    sort: []
 },[views["Sell"],views["Client"],views["Product"]]);
 
 const withSortView = ViewHandler.queryJoin({
     metrics: [mets["met:sell:sum:quantity"]],
     dimensions: [dims["dim:client:name"]],
+    clauses: [],
     sort: [mets["met:sell:sum:quantity"]]
     },[ViewHandler.queryReduce({
         metrics: [mets["met:sell:sum:quantity"]],
-        dimensions: [dims["dim:client:id"]] 
+        dimensions: [dims["dim:client:id"]],
+        clauses: [],
+        sort: [],
     },views["Sell"]),views["Client"]]);
 
 const subDimView = ViewHandler.queryJoin({
-    metrics : [],
+    metrics: emptyQuery.metrics,
     dimensions : [dims["dim:sell:datein"],subdims["subdims_day"],
-    subdims["subdims_month"],subdims["subdims_year"]]},
+    subdims["subdims_month"],subdims["subdims_year"]],
+    clauses: [],
+    sort: []},
 [ViewHandler.queryReduce({
-    metrics: [],
-    dimensions: [dims["dim:sell:datein"]]
+    metrics: emptyQuery.metrics,
+    dimensions: [dims["dim:sell:datein"]],
+    clauses: [],
+    sort: []
 },views["Sell"]),views["Sell"]]);
 
 
 const joinOneView = ViewHandler.queryJoin({
     metrics: [mets["met:product:avg:pricein"]],
-    dimensions: []
+    dimensions: emptyQuery.dimensions,
+    clauses: [],
+    sort: []
 },[views["Product"]]);
 
 const reduceAsView = ViewHandler.queryReduce({
@@ -227,16 +250,20 @@ const reduceAsView = ViewHandler.queryReduce({
 mets["met:sell:count:quantity"]],
     dimensions: [dims["dim:sell:registered"], dims["dim:product:id"],
 dims["dim:seller:id"], dims["dim:client:id"],dims["dim:sell:datein"]],
+clauses: [],
 sort: [mets["met:sell:sum:quantity"]]
 },views["Sell"]);
 
 const clientAverageBought = ViewHandler.queryReduce({
     metrics: [mets["met:sell:avg:quantity"]],
     dimensions: [dims["dim:client:name"]],
-    clauses: [clauses["averageBought"]]
+    clauses: [clauses["averageBought"]],
+    sort: []
 },ViewHandler.queryJoin({
     metrics: [mets["met:sell:avg:quantity"]],
-    dimensions: [dims["dim:client:name"], dims["dim:seller:id"]]
+    dimensions: [dims["dim:client:name"], dims["dim:seller:id"]],
+    clauses: [],
+    sort: []
 },[views["Sell"],views["Client"]]));
 
 
@@ -257,44 +284,56 @@ const wrongDim = new Dimension({ name: "dim:this:is:just:a:test", dataType: Data
 // Queries
 
 const queryNoParent = {
-    metrics: [mets["met:sell:count:quantity"]], 
-    dimensions: [subdims["subdims_none"]]
+    metrics: [mets["met:sell:count:quantity"]],
+    dimensions: [subdims["subdims_none"]],
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 }
 
 const queryMetsDims = {
     metrics : Object.keys(mets).map((key) => mets[key]),
-    dimensions : Object.keys(dims).map((key) => dims[key])
+    dimensions : Object.keys(dims).map((key) => dims[key]),
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 };
 
 const queryNoMets = {
-    metrics: [wrongMet], 
-    dimensions: [dims["dim:product:name"]]
+    metrics: [wrongMet],
+    dimensions: [dims["dim:product:name"]],
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 };
 
-const queryNoDims = { 
+const queryNoDims = {
     metrics: [mets["met:buyout:min:quantity"]],
-    dimensions: [wrongDim]
+    dimensions: [wrongDim],
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 }
 
 const queryProduct = {
     metrics: [mets["met:product:avg:pricein"], mets["met:product:max:pricein"], mets["met:product:min:pricein"],
     mets["met:product:avg:priceout"],mets["met:product:max:priceout"],mets["met:product:min:priceout"]],
-    dimensions: [dims["dim:product:name"], dims["dim:product:validity"],dims["dim:product:id"]]
+    dimensions: [dims["dim:product:name"], dims["dim:product:validity"],dims["dim:product:id"]],
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 };
 
 
 const queryActive = {
     metrics: [mets["met:seller:max:age"]],
     dimensions: [dims["dim:seller:name"],dims["dim:seller:status"]],
-    clauses: [clauses["equalClauseView"]]
+    clauses: [clauses["equalClauseView"]],
+    sort: emptyQuery.sort
 }
 
 // Metrics
 
-let emptyMetrics: Metric[] = [];
-const querySubDim = {      
-    metrics : emptyMetrics
-    , dimensions : [subdims["subdims_day"],subdims["subdims_month"]]
+const querySubDim = {
+    metrics : emptyQuery.metrics
+    , dimensions : [subdims["subdims_day"],subdims["subdims_month"]],
+    clauses: emptyQuery.clauses,
+    sort: emptyQuery.sort
 }
 
 // Exports
@@ -335,18 +374,26 @@ export const adapterScenario: AdapterScenario = {
 export const dataCtrlScenario: DataCtrlScenario = {
     wrongMet: {
         metrics: [wrongMet],
-        dimensions: [dims["dim:product:id"]]
+        dimensions: [dims["dim:product:id"]],
+        clauses: [],
+        sort: []
     },
     wrongDim: {
         metrics: [mets["met:sell:avg:quantity"]],
-        dimensions: [wrongDim] },
+        dimensions: [wrongDim],
+        clauses: [],
+        sort: [] },
     correct: {
         metrics: [mets["met:buyout:avg:quantity"]],
-        dimensions: [dims["dim:provider:id"]] 
+        dimensions: [dims["dim:provider:id"]],
+        clauses: [],
+        sort: []
     },
     clausal: {
         metrics: [mets["met:product:avg:pricein"]],
         dimensions: [dims["dim:product:name"],
-        dims["dim:product:id"]]
-    }   
+        dims["dim:product:id"]],
+        clauses: [],
+        sort: []
+    }
 };
-- 
GitLab