diff --git a/src/core/query.spec.ts b/src/core/query.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f82e39910040ce456babda0701ab268688a0d390 --- /dev/null +++ b/src/core/query.spec.ts @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blendb. + * + * blendb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blendb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blendb. If not, see <http://www.gnu.org/licenses/>. + */ + +import { expect } from "chai"; + +import { sqlGenerator, IViewData } from "./query"; + +describe("SQL Generator", () => { + let sql = ""; + it("should be able to create a sql query", () => { + + const testViews: IViewData[] = [ + { + name: "view 0", + metrics : ["met 1", "met 2"], + dimensions: ["dim 1", "dim 2", "dim 3"] + }, + + { + name: "view 1", + metrics : ["met 2", "met 3"], + dimensions: ["dim 2", "dim 3", "dim 4"] + }, + + { + name: "view 2", + metrics : ["met 4", "met 5"], + dimensions: ["dim 1", "dim 4", "dim 5"] + }, + + { + name: "view 3", + metrics : ["met 1", "met 6"], + dimensions: ["dim 5"] + } + ]; + + const testQuery: IViewData = { + name: "Query 1", + metrics : ["met1", "met2", "met3", "met4", "met5"], + dimensions : ["dim1", "dim2", "dim3", "dim4", "dim5"] + }; + + sql = sqlGenerator(testQuery, testViews); + + expect(sql).to.be.an("string"); + + }); + + it(sql, () => { + expect(sql).to.be.an("string"); + }); +}); diff --git a/src/core/query.ts b/src/core/query.ts new file mode 100644 index 0000000000000000000000000000000000000000..eceaa5e0865725b48862ca97951d5f020d9d6eef --- /dev/null +++ b/src/core/query.ts @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blend. + * + * blend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blend. If not, see <http://www.gnu.org/licenses/>. + */ + +export interface IViewData { + name: string; + metrics: string[]; + dimensions: string[]; +}; + +export function sqlGenerator (query: IViewData, views: IViewData[]) { + let objective: string[] = query.dimensions; + let aliasPrefix: string = "v"; + let fromClause: IViewData[] = []; + let activeViews: IViewData[] = views; + let whereClause: Map<string, string[]> = new Map(); + objective.forEach((item: string) => whereClause.set(item, []) ); + + while (objective.length > 0) { + let actual: IViewData; + let actualDistance: number = objective.length + 1; + activeViews = activeViews.filter((view: IViewData) => { + let intersection: string[] = []; + let dims: string[] = view.dimensions; + intersection = dims.filter((item: string) => { + return objective.indexOf(item) !== -1; + }); + + if (intersection.length > 0) { + let distance: number = objective.length - intersection.length; + + if (distance < actualDistance) { + actual = view; + actualDistance = distance; + } + return true; + } + + return false; + }); + if (activeViews.length === objective.length + 1) { + // Erro as views fornecisdas não atemdem a query + return ""; + } + let viewAlias: number = fromClause.push(actual) - 1; + actual.dimensions.forEach((item: string) => { + whereClause.get(item).push(aliasPrefix + viewAlias); + }); + objective = objective.filter((item: string) => { + return actual.dimensions.indexOf(item) === -1; + }); + } + + let sql: string = ""; + // SELECT CLAUSE + sql += "FROM "; + let aliases = fromClause.map((item: IViewData, idx: number) => { + return item.name + " " + aliasPrefix + idx; + }); + sql += aliases.join(", ") + "\n"; + sql += "WHERE "; + whereClause.forEach((items: string[]) => { + let last: string = items.pop(); + sql += items.reduce((total: string, item: string) => { + return total + ", " + item + " = " + last; + }); + }); + // group by + + return sql; +}