Skip to content

Instantly share code, notes, and snippets.

@malexsan1
Created February 9, 2020 18:41
Show Gist options
  • Save malexsan1/762115fb3d33441657101ae8956d414a to your computer and use it in GitHub Desktop.
Save malexsan1/762115fb3d33441657101ae8956d414a to your computer and use it in GitHub Desktop.
var movieDatabase = {
movie: [
{ id: 1, name: 'Avatar', directorID: 1 },
{ id: 2, name: 'Titanic', directorID: 1 },
{ id: 3, name: 'Infamous', directorID: 2 },
{ id: 4, name: 'Skyfall', directorID: 3 },
{ id: 5, name: 'Aliens', directorID: 1 },
],
actor: [
{ id: 1, name: 'Leonardo DiCaprio' },
{ id: 2, name: 'Sigourney Weaver' },
{ id: 3, name: 'Daniel Craig' },
],
director: [
{ id: 1, name: 'James Cameron' },
{ id: 2, name: 'Douglas McGrath' },
{ id: 3, name: 'Sam Mendes' },
],
actor_to_movie: [
{ movieID: 1, actorID: 2 },
{ movieID: 2, actorID: 1 },
{ movieID: 3, actorID: 2 },
{ movieID: 3, actorID: 3 },
{ movieID: 4, actorID: 3 },
{ movieID: 5, actorID: 2 },
],
};
/**
* operators = SELECT | FROM | WHERE | JOIN
*
*/
const SQLOp = Symbol('sql_operation');
const SQLValue = Symbol('sql_value');
const SELECT_Op = Symbol('SELECT');
const FROM_Op = Symbol('FROM');
const ON_Op = Symbol('ON');
const WHERE_Op = Symbol('WHERE');
const JOIN_Op = Symbol('JOIN');
const operations = {
select: SELECT_Op,
SELECT: SELECT_Op,
from: FROM_Op,
FROM: FROM_Op,
on: ON_Op,
ON: ON_Op,
where: WHERE_Op,
WHERE: WHERE_Op,
join: JOIN_Op,
JOIN: JOIN_Op,
};
function SQLEngine(database) {
this.database = database;
this.parseQuery = function(query) {
const tokens = query.split(' ');
let AST = [];
let c = 0;
const peek = () => tokens[c];
const consume = () => tokens[c++];
const sqlOperations = Object.keys(operations);
const parseOperation = () => {
const value = consume().toLowerCase();
const node = { val: value, type: operations[value], values: [] };
while (peek() && !sqlOperations.includes(peek())) {
if (operations[value] === WHERE_Op || operations[value] === ON_Op) {
node.values.push(...parseTestValues());
} else {
node.values.push(parseValue());
}
}
return node;
};
const parseValue = () => {
return consume().replace(',', '');
};
const parseTestValues = () => {
const firstOperand = consume();
const comparison = consume();
let secondOperand = '';
while (peek() && !sqlOperations.includes(peek())) {
secondOperand += `${consume()} `;
}
return [firstOperand, comparison, secondOperand.trim().replace("'", '')];
};
const parseExpr = () => {
while (peek()) {
AST.push(parseOperation());
}
};
parseExpr();
return AST;
};
this.evalFROM = function(queries = []) {
const fromQueries = queries.filter(q => q.type === FROM_Op);
console.log(fromQueries);
};
this.evalSELECT = function(queries = []) {
const result = [];
const { values } = queries.find(q => q.type === SELECT_Op);
const whereQuerie = queries.find(q => q.type === WHERE_Op);
const selectObj = values.reduce((acc, v) => {
const [table, column] = v.split('.');
if (acc[table]) {
acc[table].push(column);
} else {
acc[table] = [column];
}
return acc;
}, {});
Object.entries(selectObj).forEach(([table, columns]) => {
result.push(
...this.database[table].map(row =>
columns.reduce(
(acc, c) => ({
...acc,
[c]: row[c],
}),
{},
),
),
);
});
};
this.execute = function(query) {
const queries = this.parseQuery(query);
this.evalSELECT(queries);
};
}
var engine = new SQLEngine(movieDatabase);
engine.execute('SELECT movie.name FROM movie WHERE movie.directorID = 1');
// engine.execute(
// 'SELECT movie.name, actor.name ' +
// 'FROM movie ' +
// 'JOIN actor_to_movie ON actor_to_movie.movieID = movie.id ' +
// 'JOIN actor ON actor_to_movie.actorID = actor.id ' +
// "WHERE actor.name <> 'Daniel Craig'",
// );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment