From 44480c5ea986673b098fddd1ca74c23bc2b7e5e1 Mon Sep 17 00:00:00 2001
From: Lucas Fernandes de Oliveira <lfoliveira@inf.ufpr.br>
Date: Wed, 12 Dec 2018 09:23:49 -0200
Subject: [PATCH] Issue #100: Topological sort the query partial views in SQL
 adapter

Signed-off-by: Lucas Fernandes de Oliveira <lfoliveira@inf.ufpr.br>
---
 src/adapter/sql.ts | 31 ++++++++++++++++++--------
 src/util/tsort.ts  | 55 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 9 deletions(-)

diff --git a/src/adapter/sql.ts b/src/adapter/sql.ts
index 6bf00505..93c3bb96 100644
--- a/src/adapter/sql.ts
+++ b/src/adapter/sql.ts
@@ -27,6 +27,7 @@ import { Filter, FilterOperator } from "../core/filter";
 import { AggregationType, RelationType, DataType } from "../common/types";
 import { Operation, Opcode } from "../common/expression";
 import { View } from "../core/view";
+import { Tsort } from "../util/tsort";
 
 /**
  * Information required to make a join clause.
@@ -62,6 +63,11 @@ interface QueryAndName {
     name: string;
 }
 
+/** Dictonary indexed by view name, that returns the QueryAndName object. */
+interface QNMap {
+    [key: string]: QueryAndName;
+}
+
 /**
  * Two Dictionaries, both indexed with a dimension name.
  * Used to get the views where a dimension is.
@@ -90,7 +96,11 @@ export abstract class SQLAdapter extends Adapter {
      * @param view - View to be translated.
      */
     public getQueryFromView(view: View): string {
-        const partials = this.buildPartials(view).filter((i) => {
+        const map = this.buildPartials(view);
+        const topSort = Tsort.view(view);
+        const partials = topSort.map((i) => {
+            return (map[i]) ? map[i] : {query : "", name: i};
+        }).filter((i) => {
             return i.query !== "";
         }).map((i) => {
             return i.name + " AS (" + i.query + ")";
@@ -121,13 +131,14 @@ export abstract class SQLAdapter extends Adapter {
      * partials can only have one metric.
      * @param view - View which the partial will be built.
      */
-    private buildPartials(view: View): QueryAndName[] {
+    private buildPartials(view: View): QNMap {
         let op = view.operation;
         let queue: View[] = op.values.map((i) => i);
-        const output: QueryAndName[] = [{
+        const output: QNMap = {};
+        output[view.name] = {
             query: this.operation(op, view),
-            name: view.name,
-        }];
+            name: view.name
+        };
 
         const map: {[key: string]: boolean } = {};
 
@@ -137,10 +148,12 @@ export abstract class SQLAdapter extends Adapter {
                 const query = this.operation(partial.operation, partial);
                 if (query !== "") {
                     map[partial.id] = true;
-                    output.unshift({
-                        query: query,
-                        name: partial.name
-                    });
+                    if (!output[partial.name]) {
+                        output[partial.name] = {
+                            query: query,
+                            name: partial.name
+                        };
+                    }
                     queue = queue.concat(partial.operation.values);
                 }
             }
diff --git a/src/util/tsort.ts b/src/util/tsort.ts
index a97f7f4b..2bff0690 100644
--- a/src/util/tsort.ts
+++ b/src/util/tsort.ts
@@ -22,6 +22,9 @@
  * A representation of a dependency relation.
  * Equivalent to a edge in a dependency graph.
  */
+
+import { View } from "../core/view";
+
 export interface TsortDep {
     /** The value (vertex). */
     value: string;
@@ -60,6 +63,7 @@ export class Tsort {
      * dependency graph).
      */
     public static dependencies(deps: TsortDep[]): string[]{
+        // Graph Creation
         let graph: Graph = {};
         let mark: VertexMark = {};
         for (let i = 0; i < deps.length; ++i) {
@@ -82,6 +86,57 @@ export class Tsort {
 
         }
 
+        // Topological Sort
+        let output: string[] = [];
+        for (let vertex of Object.keys(graph)) {
+            if (!mark[vertex]) {
+                Tsort.markVertex(vertex, graph, mark, output);
+            }
+        }
+        return output;
+    }
+
+    /**
+     * Topologicaly sort view descendents names.
+     * @param view - View to construct dependency tree(graph) and sort it
+     */
+    public static view(view: View): string[] {
+        // Graph creation
+        const graph: Graph = {};
+        const mark: VertexMark = {};
+
+        let queue = [view];
+
+        while (queue.length > 0) {
+            const partial = queue.shift();
+
+            // If the dependent vertex does not exist, create it
+            // with a empty dependent list
+            if (!graph[partial.name]) {
+                graph[partial.name] = [];
+                mark[partial.name] = false;
+            }
+            for (let i = 0; i < partial.operation.values.length; ++i) {
+                const dep = partial.operation.values[i];
+
+                // If the required vertex does not exists create it
+                // and put the dependent vertex in the dependent list
+                if (!graph[dep.name]) {
+                    graph[dep.name] = [partial.name];
+                    mark[dep.name] = false;
+                }
+
+                // If the vertex already exists, add it in the dependent list
+                else {
+                    graph[dep.name].push(partial.name);
+                }
+
+                // Add the other view. Could create replicated edges.
+                queue = queue.concat(partial.operation.values);
+            }
+        }
+
+        // Topological Sort
         let output: string[] = [];
         for (let vertex of Object.keys(graph)) {
             if (!mark[vertex]) {
-- 
GitLab