Commit e7a6f836 authored by Lucas Fernandes de Oliveira's avatar Lucas Fernandes de Oliveira
Browse files

Issue #46: Add clause propagation


Signed-off-by: Lucas Fernandes de Oliveira's avatarLucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
parent 4d5adf56
Pipeline #12194 passed with stage
in 50 seconds
...@@ -357,4 +357,23 @@ describe("postgres adapter", () => { ...@@ -357,4 +357,23 @@ describe("postgres adapter", () => {
done(); done();
}); });
}); });
it("should get data from view when joins can be propagated", (done) => {
let view = adapterScenario.propagatedClauseView;
adapter.getDataFromView(view, (err, result) => {
expect(err).to.be.a("null");
expect(result).to.be.an("array");
expect(result).to.have.length(2);
expect(result[0]).to.be.an("object");
let keys: string[] = [];
keys = keys.concat(view.metrics.map((item) => item.name));
keys = keys.concat(view.dimensions.map((item) => item.name));
result.forEach((row) => {
expect(row).to.be.an("object");
expect(row).to.have.all.keys(keys);
});
done();
});
});
}); });
...@@ -112,6 +112,7 @@ export class PostgresAdapter extends Adapter { ...@@ -112,6 +112,7 @@ export class PostgresAdapter extends Adapter {
Partial Join represents how many sources still exists, Partial Join represents how many sources still exists,
every join reduces this number. every join reduces this number.
*/ */
let clausesToCover = view.clauses.map((i) => i);
while (partialJoin.length > 1) { while (partialJoin.length > 1) {
/* /*
Variable map finds what dimenensions are still needed to Variable map finds what dimenensions are still needed to
...@@ -158,6 +159,17 @@ export class PostgresAdapter extends Adapter { ...@@ -158,6 +159,17 @@ export class PostgresAdapter extends Adapter {
} }
} }
/*
Also mark scores for dimensions inside clauses
*/
for (let i = 0; i < clausesToCover.length; ++i) {
for (let j = 0; j < clausesToCover[i].targets.length; ++j) {
if (map[clausesToCover[i].targets[j].name]) {
++map[clausesToCover[i].targets[j].name];
}
}
}
for (let i = 0; i < partialJoin.length; ++i) { for (let i = 0; i < partialJoin.length; ++i) {
const dims = partialJoin[i].dimensions.filter((item) => { const dims = partialJoin[i].dimensions.filter((item) => {
return map[item.name] > 1; return map[item.name] > 1;
...@@ -171,13 +183,32 @@ export class PostgresAdapter extends Adapter { ...@@ -171,13 +183,32 @@ export class PostgresAdapter extends Adapter {
again, with less dimensions, removing this dimension again, with less dimensions, removing this dimension
from the view. from the view.
*/ */
if (dims.length < partialJoin[i].dimensions.length) {
let coveredClauses: Clause[] = [];
let notCoveredClauses: Clause[] = [];
/*
If all dimensions in a clause are a sub set of the
dimensions of a view, this clause is apllied now,
propagating the clause to this point.
Then this clause is removed from the set of clauses
*/
for (let j = 0; j < clausesToCover.length; ++j) {
if (clausesToCover[j].isCovered(partialJoin[i].dimensions)) {
coveredClauses.push(clausesToCover[j]);
}
else {
notCoveredClauses.push(clausesToCover[j]);
}
}
clausesToCover = notCoveredClauses;
if (dims.length < partialJoin[i].dimensions.length || coveredClauses.length > 0) {
const partial = new View({ const partial = new View({
metrics: partialJoin[i].metrics, metrics: partialJoin[i].metrics,
dimensions: dims, dimensions: dims,
keys: keys, keys: keys,
origin: false, origin: false,
clauses: partialJoin[i].clauses, clauses: coveredClauses.concat(partialJoin[i].clauses),
materialized: false materialized: false
}); });
const from = "(" + const from = "(" +
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
import { Filter } from "./filter"; import { Filter } from "./filter";
import { Hash } from "../util/hash"; import { Hash } from "../util/hash";
import { Dimension } from "./dimension";
import { Metric } from "./metric";
export interface ClauseOptions { export interface ClauseOptions {
filters: Filter[]; filters: Filter[];
...@@ -28,11 +30,29 @@ export interface ClauseOptions { ...@@ -28,11 +30,29 @@ export interface ClauseOptions {
export class Clause { export class Clause {
public readonly id: string; public readonly id: string;
public readonly filters: Filter[]; public readonly filters: Filter[];
public readonly targets: (Metric|Dimension)[];
constructor (options: ClauseOptions) { constructor (options: ClauseOptions) {
this.filters = options.filters; this.filters = options.filters.map((i) => i);
const t = this.filters.map((i) => i.target).sort((a, b) => {
return (a.name < b.name) ? -1 : 1;
});
if (t.length > 0) {
this.targets = [t[0]];
for (let i = 1; i < t.length; ++i) {
if (t[i - 1] !== t[i]) {
this.targets.push(t[i]);
}
}
}
else {
this.targets = [];
}
const filtersIds = this.filters.map((item) => item.id); const filtersIds = this.filters.map((item) => item.id);
this.id = Hash.sha1(filtersIds.sort()); this.id = Hash.sha1(filtersIds.sort());
} }
public isCovered(coverage: (Metric|Dimension)[]): boolean {
return coverage.every((i) => this.targets.some((j) => i.name === j.name));
}
} }
...@@ -54,6 +54,7 @@ interface AdapterScenario { ...@@ -54,6 +54,7 @@ interface AdapterScenario {
notOriginCount: View; notOriginCount: View;
unMaterializebleView: View; unMaterializebleView: View;
partialJoinView: View; partialJoinView: View;
propagatedClauseView: View;
} }
interface DataCtrlScenario { interface DataCtrlScenario {
...@@ -117,6 +118,11 @@ const filters: { [key: string]: Filter } = { ...@@ -117,6 +118,11 @@ const filters: { [key: string]: Filter } = {
operator: FilterOperator.NOTEQUAL, operator: FilterOperator.NOTEQUAL,
value: "1" value: "1"
}), }),
"dim:4" : new Filter({
target: dims[4],
operator: FilterOperator.NOTEQUAL,
value: "dim:4:1"
}),
"dim:5" : new Filter({ "dim:5" : new Filter({
target: dims[5], target: dims[5],
operator: FilterOperator.NOTEQUAL, operator: FilterOperator.NOTEQUAL,
...@@ -137,6 +143,7 @@ const clauses: { [key: string]: Clause } = { ...@@ -137,6 +143,7 @@ const clauses: { [key: string]: Clause } = {
"view0le": new Clause({filters: [filters["dim:0:le"]]}), "view0le": new Clause({filters: [filters["dim:0:le"]]}),
"view0dim0": new Clause({filters: [filters["dim:0:0"], filters["dim:0:1"]]}), "view0dim0": new Clause({filters: [filters["dim:0:0"], filters["dim:0:1"]]}),
"view9dim2": new Clause({filters: [filters["dim:2"]]}), "view9dim2": new Clause({filters: [filters["dim:2"]]}),
"view6dim4": new Clause({filters: [filters["dim:4"]]}),
"view7dim5": new Clause({filters: [filters["dim:5"]]}) "view7dim5": new Clause({filters: [filters["dim:5"]]})
}; };
...@@ -348,6 +355,15 @@ const partialJoinView = new View({ ...@@ -348,6 +355,15 @@ const partialJoinView = new View({
childViews: [views[3], views[5], views[6]] childViews: [views[3], views[5], views[6]]
}); });
const propagatedClauseView = new View({
metrics: [mets[8]],
dimensions: [dims[4]],
materialized: false,
origin: false,
childViews: [views[6], views[7]],
clauses: [clauses.view7dim5, clauses.view6dim4]
});
export const engineScenario: EngineScenario = { export const engineScenario: EngineScenario = {
metrics: mets, metrics: mets,
dimensions: dims, dimensions: dims,
...@@ -374,7 +390,8 @@ export const adapterScenario: AdapterScenario = { ...@@ -374,7 +390,8 @@ export const adapterScenario: AdapterScenario = {
notMatchFilterView: notMatchFilterView, notMatchFilterView: notMatchFilterView,
notOriginCount: notOriginCount, notOriginCount: notOriginCount,
unMaterializebleView: unMaterializebleView, unMaterializebleView: unMaterializebleView,
partialJoinView: partialJoinView partialJoinView: partialJoinView,
propagatedClauseView: propagatedClauseView
}; };
export const dataCtrlScenario: DataCtrlScenario = { export const dataCtrlScenario: DataCtrlScenario = {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment