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

Merge branch 'issue/90' into 'develop'

Issue #90: Add class Query

See merge request !78
parents 10dabda1 b427c5c8
Pipeline #18626 passed with stages
in 1 minute
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
import * as express from "express"; import * as express from "express";
import { Request } from "../types"; import { Request } from "../types";
import { Query } from "../../common/query"; import { Query, QueryOpts } from "../../common/query";
/** /**
* Constroller responsable for data part from the API. In other * Constroller responsable for data part from the API. In other
...@@ -48,7 +48,8 @@ export class DataCtrl { ...@@ -48,7 +48,8 @@ export class DataCtrl {
let view; let view;
try { try {
let query: Query = { metrics: [], dimensions: [], clauses: [], sort: [] }; const qOpt: QueryOpts = { metrics: [], dimensions: []};
let query = new Query(qOpt);
for (let i = 0; i < metrics.length; ++i) { for (let i = 0; i < metrics.length; ++i) {
query.metrics.push(req.engine.getMetricByName(metrics[i])); query.metrics.push(req.engine.getMetricByName(metrics[i]));
} }
......
...@@ -25,7 +25,7 @@ import { Clause } from "../core/clause"; ...@@ -25,7 +25,7 @@ import { Clause } from "../core/clause";
/** /**
* Internal representation of a query in BlenDB perspective. * Internal representation of a query in BlenDB perspective.
*/ */
export interface Query { export interface QueryOpts {
/** Set of metrics of the query. */ /** Set of metrics of the query. */
metrics: Metric[]; metrics: Metric[];
/** Set of dimensions of the query. */ /** Set of dimensions of the query. */
...@@ -35,3 +35,24 @@ export interface Query { ...@@ -35,3 +35,24 @@ export interface Query {
/** List of metrics and dimensions to sort the query. */ /** List of metrics and dimensions to sort the query. */
sort?: (Metric|Dimension)[]; sort?: (Metric|Dimension)[];
} }
export class Query {
/** Set of metrics of the query. */
public readonly metrics: Metric[];
/** Set of dimensions of the query. */
public readonly dimensions: Dimension[];
/** Set of clauses of the query. */
public readonly clauses: Clause[];
/** List of metrics and dimensions to sort the query. */
public readonly sort: (Metric|Dimension)[];
/**
* Create Query
* @param opts - Parameters required to create a query.
*/
constructor(opts: QueryOpts){
this.metrics = opts.metrics.map((i) => i);
this.dimensions = opts.dimensions.map((i) => i);
this.clauses = (opts.clauses) ? opts.clauses.map((i) => i) : [];
this.sort = (opts.sort) ? opts.sort.map((i) => i) : [];
}
}
...@@ -23,7 +23,7 @@ import { Metric, MetricStrOptions } from "./metric"; ...@@ -23,7 +23,7 @@ import { Metric, MetricStrOptions } from "./metric";
import { Clause } from "./clause"; import { Clause } from "./clause";
import { Filter } from "./filter"; import { Filter } from "./filter";
import { View } from "./view"; import { View } from "./view";
import { Query } from "../common/query"; import { Query, QueryOpts } from "../common/query";
import { Graph } from "../util/graph"; import { Graph } from "../util/graph";
import { EnumType, EnumTypeOptions} from "./enumType"; import { EnumType, EnumTypeOptions} from "./enumType";
import { Source , SourceStrOptions } from "./source"; import { Source , SourceStrOptions } from "./source";
...@@ -278,14 +278,14 @@ export class Engine { ...@@ -278,14 +278,14 @@ export class Engine {
*/ */
private selectOptimalView (q: Query): View { private selectOptimalView (q: Query): View {
let queries: Query[] = []; let queries: Query[] = [];
let qOpt: QueryOpts;
if (q.metrics.length > 0) { if (q.metrics.length > 0) {
for (let i = 0; i < q.metrics.length; ++i) { for (let i = 0; i < q.metrics.length; ++i) {
queries.push({ qOpt = { metrics: [q.metrics[i]],
metrics: [q.metrics[i]],
dimensions: q.dimensions, dimensions: q.dimensions,
clauses: (q.clauses) ? q.clauses : [], clauses: q.clauses,
sort: (q.sort) ? q.sort : [] sort: q.sort };
}); queries.push(new Query(qOpt));
} }
const views = queries.map((query) => { const views = queries.map((query) => {
return ViewHandler.growView(query, this.getCover(query)); return ViewHandler.growView(query, this.getCover(query));
...@@ -295,12 +295,7 @@ export class Engine { ...@@ -295,12 +295,7 @@ export class Engine {
} }
else { else {
let query = { let query = new Query(q);
metrics: q.metrics,
dimensions: q.dimensions,
clauses: (q.clauses) ? q.clauses : [],
sort: (q.sort) ? q.sort : []
};
return ViewHandler.growView(query, this.getCover(query)); return ViewHandler.growView(query, this.getCover(query));
} }
......
...@@ -27,7 +27,7 @@ import { Filter, FilterOperator } from "../core/filter"; ...@@ -27,7 +27,7 @@ import { Filter, FilterOperator } from "../core/filter";
import { Clause } from "../core/clause"; import { Clause } from "../core/clause";
import { Graph } from "./graph"; import { Graph } from "./graph";
import { AggregationType, RelationType, DataType } from "../common/types"; import { AggregationType, RelationType, DataType } from "../common/types";
import { Query } from "../common/query"; import { Query, QueryOpts } from "../common/query";
describe("graph class", () => { describe("graph class", () => {
...@@ -193,7 +193,8 @@ describe("graph class", () => { ...@@ -193,7 +193,8 @@ describe("graph class", () => {
}); });
expect(g.addView(view)).to.be.true; expect(g.addView(view)).to.be.true;
const query: Query = { metrics: [], dimensions: [dim] }; const qOpts: QueryOpts = {metrics: [], dimensions: [dim]};
const query = new Query (qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.be.an("array"); expect(children).to.be.an("array");
expect(children).to.have.length(1); expect(children).to.have.length(1);
...@@ -256,8 +257,8 @@ describe("graph class", () => { ...@@ -256,8 +257,8 @@ describe("graph class", () => {
for (let i = 0; i < views.length; ++i) { for (let i = 0; i < views.length; ++i) {
expect(g.addView(views[i])).to.be.true; expect(g.addView(views[i])).to.be.true;
} }
const qOpts: QueryOpts = { metrics: [mets[0], mets[1]], dimensions: [dims[0], dims[1]]};
const query: Query = { metrics: [mets[0], mets[1]], dimensions: [dims[0], dims[1]] }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.be.an("array"); expect(children).to.be.an("array");
expect(children).to.have.length(1); expect(children).to.have.length(1);
...@@ -295,8 +296,8 @@ describe("graph class", () => { ...@@ -295,8 +296,8 @@ describe("graph class", () => {
}); });
expect(g.addView(view)).to.be.true; expect(g.addView(view)).to.be.true;
const qOpts: QueryOpts = { metrics: [], dimensions: [dims[1], dims[2]]};
const query: Query = { metrics: [], dimensions: [dims[1], dims[2]] }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.be.an("array"); expect(children).to.be.an("array");
expect(children).to.have.length(1); expect(children).to.have.length(1);
...@@ -334,8 +335,8 @@ describe("graph class", () => { ...@@ -334,8 +335,8 @@ describe("graph class", () => {
}); });
expect(g.addView(view)).to.be.true; expect(g.addView(view)).to.be.true;
const qOpts: QueryOpts = { metrics: [], dimensions: []};
const query: Query = { metrics: [], dimensions: [] }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.be.an("array"); expect(children).to.be.an("array");
expect(children).to.be.empty; expect(children).to.be.empty;
...@@ -389,8 +390,8 @@ describe("graph class", () => { ...@@ -389,8 +390,8 @@ describe("graph class", () => {
expect(g.addView(view1)).to.be.true; expect(g.addView(view1)).to.be.true;
expect(g.addView(view2)).to.be.true; expect(g.addView(view2)).to.be.true;
expect(g.addView(view3)).to.be.true; expect(g.addView(view3)).to.be.true;
const qOpts: QueryOpts = { metrics: [], dimensions: dims, clauses: [clause2]};
const query: Query = { metrics: [], dimensions: dims, clauses: [clause2] }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.be.an("array"); expect(children).to.be.an("array");
expect(children).to.have.length(2); expect(children).to.have.length(2);
...@@ -502,8 +503,8 @@ describe("graph class", () => { ...@@ -502,8 +503,8 @@ describe("graph class", () => {
}); });
expect(g.addView(view0)).to.be.true; expect(g.addView(view0)).to.be.true;
const qOpts: QueryOpts = { metrics: [], dimensions: dims, clauses: testClauses};
const query: Query = { metrics: [], dimensions: dims, clauses: testClauses }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.have.length(1); expect(children).to.have.length(1);
expect(children[0].id === view0.id).to.be.true; expect(children[0].id === view0.id).to.be.true;
...@@ -626,8 +627,8 @@ describe("graph class", () => { ...@@ -626,8 +627,8 @@ describe("graph class", () => {
for (let i = 0; i < views.length; ++i) { for (let i = 0; i < views.length; ++i) {
expect(g.addView(views[i])).to.be.true; expect(g.addView(views[i])).to.be.true;
} }
const qOpts: QueryOpts = { metrics: [], dimensions: dims, clauses: testClauses};
const query: Query = { metrics: [], dimensions: dims, clauses: testClauses }; const query = new Query(qOpts);
let children = g.cover(query); let children = g.cover(query);
expect(children).to.have.length(1); expect(children).to.have.length(1);
expect(children[0].id === views[0].id).to.be.true; expect(children[0].id === views[0].id).to.be.true;
......
...@@ -321,7 +321,7 @@ export class Graph { ...@@ -321,7 +321,7 @@ export class Graph {
public cover(q: Query): View[] { public cover(q: Query): View[] {
const metrics = q.metrics; const metrics = q.metrics;
const dimensions = q.dimensions; const dimensions = q.dimensions;
const clauses = (q.clauses) ? q.clauses : []; const clauses = q.clauses;
let output: View[] = []; let output: View[] = [];
let verticesIds = this.verticesInQuery(q); let verticesIds = this.verticesInQuery(q);
...@@ -621,7 +621,7 @@ export class Graph { ...@@ -621,7 +621,7 @@ export class Graph {
private verticesInQuery(q: Query): string[] { private verticesInQuery(q: Query): string[] {
const metrics = q.metrics; const metrics = q.metrics;
const dimensions = q.dimensions; const dimensions = q.dimensions;
const clauses = (q.clauses) ? q.clauses : []; const clauses = q.clauses;
let verticesIds = metrics.map((met) => met.name); let verticesIds = metrics.map((met) => met.name);
verticesIds = verticesIds.concat(dimensions.map((dim) => dim.name)); verticesIds = verticesIds.concat(dimensions.map((dim) => dim.name));
for (let i = 0; i < clauses.length; ++i) { for (let i = 0; i < clauses.length; ++i) {
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
import { Opcode } from "../common/expression"; import { Opcode } from "../common/expression";
import { View } from "../core/view"; import { View } from "../core/view";
import { Clause } from "../core/clause"; import { Clause } from "../core/clause";
import { Query } from "../common/query"; import { Query, QueryOpts } from "../common/query";
import { Dimension } from "../core/dimension"; import { Dimension } from "../core/dimension";
import { Metric } from "../core/metric"; import { Metric } from "../core/metric";
...@@ -60,8 +60,8 @@ export class ViewHandler { ...@@ -60,8 +60,8 @@ export class ViewHandler {
metrics: q.metrics, metrics: q.metrics,
dimensions: q.dimensions, dimensions: q.dimensions,
origin: false, origin: false,
clauses: (q.clauses) ? q.clauses : [], clauses: q.clauses,
sort: (q.sort) ? q.sort : [], sort: q.sort,
operation: { operation: {
opcode: Opcode.JOIN, opcode: Opcode.JOIN,
values: views.map((i) => i) values: views.map((i) => i)
...@@ -85,8 +85,8 @@ export class ViewHandler { ...@@ -85,8 +85,8 @@ export class ViewHandler {
metrics: q.metrics, metrics: q.metrics,
dimensions: q.dimensions, dimensions: q.dimensions,
origin: false, origin: false,
clauses: (q.clauses) ? q.clauses : [], clauses: q.clauses,
sort: (q.sort) ? q.sort : [], sort: q.sort,
operation: { operation: {
opcode: Opcode.REDUCE, opcode: Opcode.REDUCE,
values: [view] values: [view]
...@@ -109,17 +109,16 @@ export class ViewHandler { ...@@ -109,17 +109,16 @@ export class ViewHandler {
let metView: View = null; let metView: View = null;
let partialJoin: View[] = null; let partialJoin: View[] = null;
let reduced: ViewsAndClauses = null; let reduced: ViewsAndClauses = null;
let partialQuery: Query = { let qOpt: QueryOpts;
metrics: q.metrics,
dimensions: q.dimensions, let partialQuery = new Query(q);
clauses: q.clauses,
sort: q.sort
};
partialJoin = views.map((i) => (i)); partialJoin = views.map((i) => (i));
if (q.metrics.length === 0) { // ignore metView if there are 0 metrics if (q.metrics.length === 0) { // ignore metView if there are 0 metrics
while (partialJoin.length > 1) { while (partialJoin.length > 1) {
partialQuery.clauses = clausesToCover; qOpt = {metrics: partialQuery.metrics, dimensions: partialQuery.dimensions, clauses: clausesToCover};
partialQuery = new Query(qOpt);
reduced = ViewHandler.applyReduce(partialJoin, partialQuery); reduced = ViewHandler.applyReduce(partialJoin, partialQuery);
clausesToCover = reduced.clauses; clausesToCover = reduced.clauses;
partialJoin = ViewHandler.applyJoin(reduced.views); partialJoin = ViewHandler.applyJoin(reduced.views);
...@@ -137,7 +136,8 @@ export class ViewHandler { ...@@ -137,7 +136,8 @@ export class ViewHandler {
partialJoin.splice(metViewId, 1); partialJoin.splice(metViewId, 1);
while (partialJoin.length > 1) { while (partialJoin.length > 1) {
partialJoin.push(metView); // MetView is now the last view partialJoin.push(metView); // MetView is now the last view
partialQuery.clauses = clausesToCover; qOpt = {metrics: partialQuery.metrics, dimensions: partialQuery.dimensions, clauses: clausesToCover};
partialQuery = new Query(qOpt);
reduced = ViewHandler.applyReduce(partialJoin, partialQuery); reduced = ViewHandler.applyReduce(partialJoin, partialQuery);
clausesToCover = reduced.clauses; clausesToCover = reduced.clauses;
metView = reduced.views.pop(); metView = reduced.views.pop();
...@@ -153,7 +153,8 @@ export class ViewHandler { ...@@ -153,7 +153,8 @@ export class ViewHandler {
// In this case, there are exactly 2 views // In this case, there are exactly 2 views
reduced = ViewHandler.applyReduce(partialJoin, partialQuery); reduced = ViewHandler.applyReduce(partialJoin, partialQuery);
partialJoin = ViewHandler.applyJoin(reduced.views); partialJoin = ViewHandler.applyJoin(reduced.views);
partialQuery.clauses = reduced.clauses; qOpt = {metrics: partialQuery.metrics, dimensions: partialQuery.dimensions, clauses: reduced.clauses};
partialQuery = new Query(qOpt);
return ViewHandler.applyReduce(partialJoin, partialQuery).views[0]; return ViewHandler.applyReduce(partialJoin, partialQuery).views[0];
} }
} }
...@@ -355,11 +356,9 @@ export class ViewHandler { ...@@ -355,11 +356,9 @@ export class ViewHandler {
let clauses = partial0.clauses.concat(partial1.clauses); let clauses = partial0.clauses.concat(partial1.clauses);
clauses = ViewHandler.removeDuplicatedClauses(clauses); clauses = ViewHandler.removeDuplicatedClauses(clauses);
const partialQuery: Query = { const qOpts: QueryOpts = {metrics: mets, dimensions: dims, clauses: clauses};
metrics: mets,
dimensions: dims, const partialQuery = new Query(qOpts);
clauses: clauses
};
const partial = ViewHandler.queryJoin(partialQuery, [partial0, partial1]); const partial = ViewHandler.queryJoin(partialQuery, [partial0, partial1]);
views.push(partial); views.push(partial);
......
...@@ -27,11 +27,11 @@ import { Clause } from "../src/core/clause"; ...@@ -27,11 +27,11 @@ import { Clause } from "../src/core/clause";
import { AggregationType, RelationType , DataType} from "../src/common/types"; import { AggregationType, RelationType , DataType} from "../src/common/types";
import { ViewHandler } from "../src/util/viewHandler"; import { ViewHandler } from "../src/util/viewHandler";
import { EngineScenario, AdapterScenario, DataCtrlScenario } from "../src/util/scenarioHandler"; import { EngineScenario, AdapterScenario, DataCtrlScenario } from "../src/util/scenarioHandler";
import { Query, QueryOpts } from "../src/common/query";
const configPath = process.env.BLENDB_SCHEMA_FILE; const configPath = process.env.BLENDB_SCHEMA_FILE;
const config = ConfigParser.parse(configPath); const config = ConfigParser.parse(configPath);
const mets : {[key: string]: Metric} = {}; const mets : {[key: string]: Metric} = {};
for (let i in config.metrics) { for (let i in config.metrics) {
let met = config.metrics[i]; let met = config.metrics[i];
...@@ -50,8 +50,16 @@ for (let i in config.buildViews){ ...@@ -50,8 +50,16 @@ for (let i in config.buildViews){
views[view.alias] = view.view; views[view.alias] = view.view;
} }
const wrongMet = new Metric({
name: "met:this:is:just:a:test",
aggregation: AggregationType.COUNT,
dataType: DataType.INTEGER
});
const wrongDim = new Dimension({ name: "dim:this:is:just:a:test", dataType: DataType.INTEGER });
/** /**
* Create new filters to use in clause and test * Create new filters to use in clause and test
* the clauses * the clauses
*/ */
const filters: { [key: string]: Filter } = { const filters: { [key: string]: Filter } = {
...@@ -102,8 +110,6 @@ const filters: { [key: string]: Filter } = { ...@@ -102,8 +110,6 @@ const filters: { [key: string]: Filter } = {
}) })
} }
// Subdimensions
const subdims : {[key:string]: Dimension} = { const subdims : {[key:string]: Dimension} = {
"subdims_day" : new Dimension({ "subdims_day" : new Dimension({
name: "subdims_day", name: "subdims_day",
...@@ -131,9 +137,7 @@ const subdims : {[key:string]: Dimension} = { ...@@ -131,9 +137,7 @@ const subdims : {[key:string]: Dimension} = {
}) })
} }
// Clauses const clauses: { [key: string]: Clause } = {
const clauses: { [key: string]: Clause } = {
"lastDay": "lastDay":
new Clause({filters: [filters["equal"]]}), new Clause({filters: [filters["equal"]]}),
"undefined": "undefined":
...@@ -157,146 +161,207 @@ const clauses: { [key: string]: Clause } = { ...@@ -157,146 +161,207 @@ const clauses: { [key: string]: Clause } = {
new Clause ({filters: [filters["equalFilterView"]]}) new Clause ({filters: [filters["equalFilterView"]]})
} }
// Views
const JoinWithAncestors = ViewHandler.growView({ let qOpts : {[key: string]: QueryOpts} = {
metrics: [mets["met:sell:count:quantity"]], "JoinWithAncestors":
dimensions: [dims["dim:seller:id"],dims["dim:provider:id"]], {
clauses: [] metrics: [mets["met:sell:count:quantity"]],
},[views["view:Sell"]]); dimensions: [dims["dim:seller:id"],dims["dim:provider:id"]]
},
"joinWithNoMetrics":
{
metrics: [],
dimensions: [dims["dim:product:name"],dims["dim:seller:name"]]
},
"growOneView":
{
metrics: [mets["met:seller:min:age"]],
dimensions: [dims["dim:seller:name"],dims["dim:seller:sex"]]
},
"multipleClause":
{
metrics: [mets["met:sell:avg:quantity"]],
dimensions: [dims["dim:sell:datein"],dims["dim:seller:name"],
dims["dim:client:name"]],
// and between filters => (A) and (B)
clauses: [clauses["expired"],clauses["averageBought"]],
},
"singleClause":
{
metrics: [mets["met:sell:avg:quantity"]],
dimensions: [dims["dim:sell:datein"],dims["dim:seller:name"],
dims["dim:client:name"]],
clauses: [clauses["expiredAndAverage"]],
},
"equalfilter":
{
metrics: [],
dimensions: [dims["dim:client:name"],dims["dim:product:validity"]],
clauses: [clauses["lastDay"]],
},
"withSortView:0":
{
metrics: [mets["met:sell:sum:quantity"]],
dimensions: [dims["dim:client:name"]],
sort: [mets["met:sell:sum:quantity"]]
},
"withSortView:1":
{
metrics: [mets["met:sell:sum:quantity"]],
dimensions: [dims["dim:client:id"]]
},
"subDimView:0":
{
metrics: [],
dimensions : [dims["dim:sell:datein"],subdims["subdims_day"],
subdims["subdims_month"],subdims["subdims_year"]]
},
"subDimView:1":
{
metrics: [],
dimensions: [dims["dim:sell:datein"]]
},
"joinOneView":
{
metrics: [mets["met:product:avg:pricein"]],
dimensions: [],
},
"reduceAsView":
{
metrics: [mets["met:sell:sum:quantity"],mets["met:sell:avg:quantity"],
mets["met:sell:count:quantity"]],
dimensions: [dims["dim:sell:registered"], dims["dim:product:id"],
dims["dim:seller:id"], dims["dim:client:id"],dims["dim:sell:datein"]],
sort: [mets["met:sell:sum:quantity"]]
},
"clientAverageBought:0":
{
metrics: [mets["met:sell:avg:quantity"]],
dimensions: [dims["dim:client:name"]],
clauses: [clauses["averageBought"]]
},
"clientAverageBought:1":
{
metrics: [mets["met:sell:avg:quantity"]],
dimensions: [dims["dim:client:name"], dims["dim:seller:id"]]
},
"queryNoParent":
{
metrics: [mets["met:sell:count:quantity"]],
dimensions: [subdims["subdims_none"]]
},
"queryMetsDims":
{
metrics : Object.keys(mets).map((key) => mets[key]),
dimensions : Object.keys(dims).map((key) => dims[key])
},
"queryNoMets":
{
metrics: [wrongMet],
dimensions: [dims["dim:product:name"]]
},
"queryNoDims":
{