Skip to content

Instantly share code, notes, and snippets.

@ivanauliaa
Last active December 10, 2021 19:23
Show Gist options
  • Save ivanauliaa/9a662cf8e2140bd2a6c5112f6f0ec7ea to your computer and use it in GitHub Desktop.
Save ivanauliaa/9a662cf8e2140bd2a6c5112f6f0ec7ea to your computer and use it in GitHub Desktop.
Express Microsoft OData filter
/**
* Generate AST from query, please check the test cases to find the correct
* input and output for this.
*/
exports.generateASTFromQuery = async (req, res) => {
try {
let qrys_prefix = prefixExpr(req.query.$filter.match(/[a-zA-Z_]+|[0-9]+|[(]+|[)]/g))
let qry_json = createJson(qrys_prefix)
return res.json(qry_json)
// return res.json({ value: {} });
} catch (e) {
return res
.status(500)
.json({ error: e.message });
}
};
function prefixExpr(qrys) {
const log_opr = ['eq', 'ne', 'gt', 'ge', 'lt', 'le']
const com_opr = ['and', 'or', 'not']
const open_par = '('
const close_par = ')'
let ent_stack = []
let opr_stack = []
qrys.filter(qry => {
if (qry == open_par) {
opr_stack.push(qry)
} else if(qry == close_par) {
let top_opr_stack = opr_stack[opr_stack.length - 1]
while(top_opr_stack != open_par) {
ent_stack.push(opr_stack.pop())
top_opr_stack = opr_stack[opr_stack.length - 1]
}
if(opr_stack.length == 1) {
opr_stack = []
} else {
opr_stack.pop()
}
} else if(log_opr.includes(qry) || com_opr.includes(qry)) {
if(!opr_stack.length) {
opr_stack.push(qry)
} else {
let top_opr_stack = opr_stack.pop()
if(top_opr_stack == open_par || (com_opr.includes(top_opr_stack) && log_opr.includes(qry))) {
opr_stack.push(top_opr_stack)
opr_stack.push(qry)
} else {
while(com_opr.includes(qry) && log_opr.includes(top_opr_stack)) {
ent_stack.push(top_opr_stack)
top_opr_stack = opr_stack.pop
}
opr_stack.push(top_opr_stack)
opr_stack.push(qry)
}
}
} else {
ent_stack.push(qry)
}
})
ent_stack.reverse()
ent_stack.filter(ent => {
opr_stack.push(ent)
})
opr_stack = opr_stack.filter(opr => typeof opr == 'string')
return opr_stack
}
function createJson(qrys_prefix) {
const log_opr = ['eq', 'ne', 'gt', 'ge', 'lt', 'le']
const com_opr = ['and', 'or', 'not']
let ent_stack = []
qrys_prefix.reverse()
qrys_prefix.filter(qry => {
if(!log_opr.includes(qry) && !com_opr.includes(qry)) {
ent_stack.push(qry)
} else {
let ent1 = ent_stack.pop()
let ent2 = ent_stack.pop()
let qry_obj
if(log_opr.includes(qry)) {
qry_obj = createObject(ent2, ent1, qry, false)
} else {
qry_obj = createObject(ent2, ent1, qry, true)
}
ent_stack.push(qry_obj)
}
})
return ent_stack[0]
}
function createObject(ent1, ent2, opr, is_com_opr) {
if(is_com_opr) {
return {
children: [
ent1,
ent2
],
value: opr
}
} else {
return {
children: [
{
value: ent1
},
{
value: ent2
}
],
value: opr
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment