Issue #22: Change cover algorithm to use graph

Signed-off-by: Lucas Fernandes de Oliveira's avatarLucas Fernandes de Oliveira <lfo14@inf.ufpr.br>
parent 439c0e4c
Pipeline #10771 passed with stage
in 42 seconds
......@@ -140,7 +140,7 @@ describe("engine class", () => {
return optView.childViews[0].dimensions.some((item) => item.name === subdim[0].name);
});
expect(optimalView).satisfy((optView: View) => {
return optView.childViews[0].dimensions.some((item) => item.name === subdim[0].name);
return optView.childViews[0].dimensions.some((item) => item.name === subdim[1].name);
});
});
......@@ -177,7 +177,7 @@ describe("engine class", () => {
}
catch (e){
error = true;
expect(e.message).to.be.equal("Engine sub-dimention " + subdim[3].name + " with no parent");
expect(e.message).to.be.equal("Engine views cannot cover the query");
}
expect(error).to.be.true;
......
......@@ -20,29 +20,43 @@
import { Dimension } from "./dimension";
import { Metric } from "./metric";
import { View, ChildView } from "./view";
import { View } from "./view";
import { Query } from "../common/query";
import { RelationType } from "../common/types";
import { Graph } from "../util/graph";
export class Engine {
private views: View[] = [];
private metrics: Metric[] = [];
private dimensions: Dimension[] = [];
private graph: Graph;
constructor () { }
constructor () {
this.views = [];
this.metrics = [];
this.dimensions = [];
this.graph = new Graph();
}
public getViews() {
return this.views;
}
public addView(view: View) {
this.views.push(view);
return view;
if (this.graph.addView(view)) {
this.views.push(view);
return view;
}
return null;
}
public addMetric(metric: Metric) {
this.metrics.push(metric);
return metric;
if (this.graph.addMetric(metric)) {
this.metrics.push(metric);
return metric;
}
return null;
}
public getMetricByName(name: string) {
......@@ -56,8 +70,12 @@ export class Engine {
}
public addDimension(dimension: Dimension) {
this.dimensions.push(dimension);
return dimension;
if (this.graph.addDimension(dimension)) {
this.dimensions.push(dimension);
return dimension;
}
return null;
}
public getDimensionByName(name: string) {
......@@ -75,92 +93,9 @@ export class Engine {
}
private selectOptimalView (q: Query) {
let metUncovered = q.metrics.map((met) => met);
let dimUncovered = q.dimensions.map((dim) => dim);
let optimalViews: ChildView[] = [];
let activeViews = this.getViews();
// run this block until all metrics and dimmensions are covered
while (metUncovered.length > 0 || dimUncovered.length > 0) {
let bestView: ChildView;
let coverLength = metUncovered.length + dimUncovered.length;
let shortestDistance = coverLength + 1;
// remove views from the activeViews if they don't intersect
// with the objective
activeViews = activeViews.filter((view: View) => {
let metIntersection = metUncovered.filter((met: Metric) => {
return view.metrics.some((item) => item.name === met.name);
});
let dimIntersection = dimUncovered.filter((dim: Dimension) => {
let r: boolean = view.dimensions.some((item) => item.name === dim.name);
while (!r && dim.relation !== RelationType.NONE) {
if (dim.parent === null) {
throw new Error("Engine sub-dimention " + dim.name + " with no parent");
}
r = view.dimensions.some((item) => item.name === dim.parent.name);
dim = dim.parent;
}
return r;
});
let intersection = metIntersection.length + dimIntersection.length;
if (intersection > 0) {
let distance = coverLength - intersection;
if (distance < shortestDistance) {
bestView = {
view: view,
metrics: metIntersection,
dimensions: dimIntersection
};
shortestDistance = distance;
}
else if (distance === shortestDistance &&
view.dimensions.length < bestView.dimensions.length) {
// priorizes views with less dimensions
bestView = {
view: view,
metrics: metIntersection,
dimensions: dimIntersection
};
}
return true;
}
/*If the intersection is empty,
remove this element from future searches*/
return false;
});
if (shortestDistance === coverLength + 1) {
throw new Error("Engine views cannot cover the query");
}
optimalViews.push(bestView);
// remove metrics and dimensions corvered by the bestView from the
// objective (i.e. the object is already met for those metrics/dimensions)
metUncovered = metUncovered.filter((met: Metric) => {
return !bestView.metrics.some((item) => item.name === met.name);
});
dimUncovered = dimUncovered.filter((dim: Dimension) => {
let r: boolean = bestView.dimensions.some((item) => item.name === dim.name);
while (!r && dim.relation !== RelationType.NONE) {
if (dim.parent === null) {
throw new Error("Engine sub-dimention " + dim.name + " with no parent");
}
r = bestView.dimensions.some((item) => item.name === dim.parent.name);
dim = dim.parent;
}
return !r;
});
let optimalViews = this.graph.cover(q.metrics, q.dimensions);
if (optimalViews.length === 0) {
throw new Error ("Engine views cannot cover the query");
}
// If all the metrics and dimensions are the same and only exist one child view
......
This diff is collapsed.
This diff is collapsed.
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