diff --git a/src/adapter/postgres.spec.ts b/src/adapter/postgres.spec.ts index ffa9b2ab72dba8cc6c0b12bb2af7652ab274f1fd..597692c0ec4cdd718bc148ac4808a30113b5f162 100644 --- a/src/adapter/postgres.spec.ts +++ b/src/adapter/postgres.spec.ts @@ -382,7 +382,7 @@ describe("postgres adapter", () => { adapter.getDataFromView(view, (err, result) => { expect(err).to.be.a("null"); expect(result).to.be.an("array"); - expect(result).to.have.length(8); + expect(result).to.have.length(4); expect(result[0]).to.be.an("object"); let keys: string[] = []; keys = keys.concat(view.metrics.map((item) => item.name)); diff --git a/src/core/clause.ts b/src/core/clause.ts index 8f61c5ddb2083a3399cb9cfc393cacf82a4b8511..1b7c47e6f47d07586f36f40b0e7b3a657bdc06ae 100644 --- a/src/core/clause.ts +++ b/src/core/clause.ts @@ -53,6 +53,6 @@ export class Clause { } public isCovered(coverage: (Metric|Dimension)[]): boolean { - return coverage.every((i) => this.targets.some((j) => i.name === j.name)); + return this.targets.every((i) => coverage.some((j) => i.name === j.name)); } } diff --git a/src/util/graph.spec.ts b/src/util/graph.spec.ts index aab53671b40c8425fdfa03a30f1435180b2db2ee..464dd1dc5ee244f77f9fc4c69b639db04f1c0e91 100644 --- a/src/util/graph.spec.ts +++ b/src/util/graph.spec.ts @@ -416,4 +416,243 @@ describe("graph class", () => { expect(children.every((item) => item.id !== view3.id)).to.be.true; }); + it("should cover the query, using filters of intervals", () => { + let dims = [ + new Dimension({name: "dim:0", dataType: "float"}), + new Dimension({name: "dim:1", dataType: "float"}), + new Dimension({name: "dim:2", dataType: "date"}), + ]; + + const filters = [ + new Filter({ + target: dims[0], + operator: FilterOperator.GREATEREQ, + value: "0" + }), + new Filter({ + target: dims[0], + operator: FilterOperator.LOWER, + value: "10" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.GREATER, + value: "0" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.LOWEREQ, + value: "10" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.GREATEREQ, + value: "0" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.LOWER, + value: "10" + }) + ]; + + const clauses = [ + new Clause({filters: [filters[0]]}), + new Clause({filters: [filters[1]]}), + new Clause({filters: [filters[2]]}), + new Clause({filters: [filters[3]]}), + new Clause({filters: [filters[4]]}), + new Clause({filters: [filters[5]]}), + ]; + + const filtersTest = [ + new Filter({ + target: dims[0], + operator: FilterOperator.GREATER, + value: "1" + }), + new Filter({ + target: dims[0], + operator: FilterOperator.LOWEREQ, + value: "9" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.GREATEREQ, + value: "1" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.LOWER, + value: "9" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.GREATER, + value: "1" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.LOWEREQ, + value: "9" + }) + ]; + + const testClauses = [ + new Clause({filters: [filtersTest[0]]}), + new Clause({filters: [filtersTest[1]]}), + new Clause({filters: [filtersTest[2]]}), + new Clause({filters: [filtersTest[3]]}), + new Clause({filters: [filtersTest[4]]}), + new Clause({filters: [filtersTest[5]]}), + ]; + + let g = new Graph(); + + for (let i = 0; i < 3; ++i) { + expect(g.addDimension(dims[i])).to.be.true; + } + + let view0 = new View({ + metrics: [], + dimensions: dims, + origin: false, + materialized: true, + clauses: clauses + }); + + expect(g.addView(view0)).to.be.true; + + const query: Query = { metrics: [], dimensions: dims, clauses: testClauses }; + let children = g.cover(query); + expect(children).to.have.length(1); + expect(children[0].id === view0.id).to.be.true; + }); + + it("should cover the query, not using filters of intervals", () => { + let dims = [ + new Dimension({name: "dim:0", dataType: "float"}), + new Dimension({name: "dim:1", dataType: "float"}), + new Dimension({name: "dim:2", dataType: "date"}), + ]; + + const filters = [ + new Filter({ + target: dims[0], + operator: FilterOperator.GREATEREQ, + value: "10" + }), + new Filter({ + target: dims[0], + operator: FilterOperator.LOWER, + value: "0" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.GREATER, + value: "10" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.LOWEREQ, + value: "0" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.GREATEREQ, + value: "10" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.LOWER, + value: "0" + }) + ]; + + const clauses = [ + new Clause({filters: [filters[0]]}), + new Clause({filters: [filters[1]]}), + new Clause({filters: [filters[2]]}), + new Clause({filters: [filters[3]]}), + new Clause({filters: [filters[4]]}), + new Clause({filters: [filters[5]]}), + ]; + + const filtersTest = [ + new Filter({ + target: dims[0], + operator: FilterOperator.GREATER, + value: "0" + }), + new Filter({ + target: dims[0], + operator: FilterOperator.LOWEREQ, + value: "10" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.GREATEREQ, + value: "0" + }), + new Filter({ + target: dims[1], + operator: FilterOperator.LOWER, + value: "11" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.GREATER, + value: "0" + }), + new Filter({ + target: dims[2], + operator: FilterOperator.LOWEREQ, + value: "10" + }) + ]; + + const testClauses = [ + new Clause({filters: [filtersTest[0]]}), + new Clause({filters: [filtersTest[1]]}), + new Clause({filters: [filtersTest[2]]}), + new Clause({filters: [filtersTest[3]]}), + new Clause({filters: [filtersTest[4]]}), + new Clause({filters: [filtersTest[5]]}), + ]; + + let g = new Graph(); + + for (let i = 0; i < 3; ++i) { + expect(g.addDimension(dims[i])).to.be.true; + } + + let views = [ + new View({ + metrics: [], + dimensions: dims, + origin: false, + materialized: true + }) + ]; + + for (let i = 0; i < testClauses.length; ++i) { + views.push(new View({ + metrics: [], + dimensions: dims, + origin: false, + materialized: true, + clauses: [clauses[i]] + })); + } + + for (let i = 0; i < views.length; ++i) { + expect(g.addView(views[i])).to.be.true; + } + + const query: Query = { metrics: [], dimensions: dims, clauses: testClauses }; + 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 9fe4d683929053966797bdbfd1a5656323275d65..2b020701c24e6e3f656ba02188c114080236657a 100644 --- a/src/util/graph.ts +++ b/src/util/graph.ts @@ -23,6 +23,7 @@ import { Metric } from "../core/metric"; import { Dimension } from "../core/dimension"; import { Query } from "../common/query"; import { Clause } from "../core/clause"; +import { FilterOperator } from "../core/filter"; enum State { UNVISITED, @@ -521,7 +522,7 @@ export class Graph { /* Check if a set of filter/clauses of a view suits for the query */ - private passConstraints(constraints: Clause[], target: Clause[]) { + private passConstraints(queryClauses: Clause[], viewClauses: Clause[]) { /* TODO: Enhance constraint check. @@ -533,7 +534,65 @@ export class Graph { only means that a more inneficient view must be choosen. */ - return target.every((item) => constraints.some((c) => c.id === item.id)); + return viewClauses.every((view) => queryClauses.some((query) => { + if (view.id === query.id) { + return true; + } + + else if (query.filters.length === 1 && view.filters.length === 1) { + let queryFilter = query.filters[0]; + let viewFilter = view.filters[0]; + if (queryFilter.target.name !== viewFilter.target.name) { + return false; + } + + let queryValue: number; + let viewValue: number; + if (queryFilter.target.dataType === "date") { + queryValue = new Date(queryFilter.value).getTime(); + viewValue = new Date(viewFilter.value).getTime(); + } + + else { + queryValue = parseFloat(queryFilter.value); + viewValue = parseFloat(viewFilter.value); + } + + if (viewFilter.operator === FilterOperator.GREATEREQ && + queryValue >= viewValue ) { + if (queryFilter.operator === FilterOperator.GREATER || + queryFilter.operator === FilterOperator.GREATEREQ) { + return true; + } + } + + else if (viewFilter.operator === FilterOperator.GREATER && + queryValue > viewValue ) { + if (queryFilter.operator === FilterOperator.GREATER || + queryFilter.operator === FilterOperator.GREATEREQ) { + return true; + } + } + + else if (viewFilter.operator === FilterOperator.LOWEREQ && + queryValue <= viewValue ) { + if (queryFilter.operator === FilterOperator.LOWER || + queryFilter.operator === FilterOperator.LOWEREQ) { + return true; + } + } + + else if (viewFilter.operator === FilterOperator.LOWER && + queryValue < viewValue ) { + if (queryFilter.operator === FilterOperator.LOWER || + queryFilter.operator === FilterOperator.LOWEREQ) { + return true; + } + } + } + + return false; + })); } @@ -565,5 +624,4 @@ export class Graph { } } - }