2

I'm building a logic expression parser. I really like the way mongoDB structures their logic statements. However, I'm thinking if this is the best structure to store and parse logic expressions.

const logicPayload = [
  { varA: { $eq: "meow" } },
  { $and: [{ varB: { $lt: "varD" } }, { varB: { $gte: "varC" } }] },
  { $not: [{ varB: { $eq: "varA" } }] }
];

Given the payload above, how can i design a function to parse the expression. varA, varB varC and varD will be resolve at runtime.

A dummy resolve function is below.

const resovleValue = varID => {
  switch (varID) {
    case "varA":
      return "meow";
      break;
    case "varB":
      return 100;
    case "varC":
      return 100;
    case "varD":
      return 983;
  }
};

I'm trying to create the logic function below

/**
 * This logic function must only return true or false
 */
const logicFunction = logicPayload => {};

Any guidance is much appreciated. thanks.

1 Answer 1

2

Here's a logic parser that uses recursion to deal with the internal arrays of logic.

It'll stop processing as soon it finds a rule that returns false.

function checkRule(rule) {
  let key1 = Object.keys(rule)[0];
  let comp = Object.values(rule)[0];
  
  if(/^[$](and|or|not)$/.test(key1) && Array.isArray(comp)) {
    let aon = key1;
    let rules = comp;
    let results = [];
    
    for(r of rules){
      let valid = checkRule(r);
      results.push(valid);
      //console.log(`${aon} ${JSON.stringify(r)}: ${valid}`);
      if(aon === '$and' && !valid) return false;
      if(aon === '$or' && valid) return true;
      if(aon === '$not' && valid) return false;
    }
     //console.log (JSON.stringify(results));
     if(aon === '$and' && results.some((x)=>!x))
         return false;
     if(aon === '$or' && results.every((x)=>!x))
         return false;
     if(aon === '$not' && results.some((x)=>x))
         return false;
}
else {
  let operator = Object.keys(comp)[0];
  let key2 = comp[operator];
  let val1 = resolveValue(key1) || key1;
  let val2 = resolveValue(key2) || key2;
  //console.log(`\t${val1} ${operator} ${val2}`);
  
  switch(operator) {
    case '$eq': 
     if(!(val1 == val2)) return false;
     break;
    case '$ne': 
    case '$neq':
     if(!(val1 != val2)) return false;
     break;
    case '$lt': 
     if(!(val1 < val2)) return false;
     break;
    case '$le': 
    case '$lte': 
     if(!(val1 <= val2)) return false;
     break;
    case '$gt': 
     if(!(val1 > val2)) return false;
     break;
    case '$ge':
    case '$gte': 
     if(!(val1 >= val2)) return false;
     break;
    default: 
      return false;
   }
    
  }
  
  return true;
}

function logicFunction (logicPayload) {
   let results = [];
   
   for (rule of logicPayload) {
   	 let valid = checkRule(rule);
   	 //console.log(`${valid}: ${JSON.stringify(rule)}`);
   	 results.push(valid);
   	 if(!valid) return false;
   }
   return results.every((x)=>x);
};

const resolveValue = (varID) => {
  switch (varID) {
    case "varA": return "meow";
    case "varB": return 100;
    case "varC": return 100;
    case "varD": return 983;
  }
};


let logicPayload = [
  { varA: { $eq: "meow" } },
  { $and: [
      { varB: { $lt: "varD" } },
      { varB: { $gte: "varC" } }
   ] },
  { $not: [ { varB: { $eq: "varA" } } ] }
];

 let result = logicFunction(logicPayload);
 console.log(result);
  

Sign up to request clarification or add additional context in comments.

6 Comments

thanks for the fast response and for that comprehensive answer. would like to ask, any opinion on using the current payload vs changing it to this const logicPayload = [ [varA, "$eq", "meow"], [ "$and", [ [varB, "$lt", "varD"], varB, "$gte", "varC"] ] ], ["$or", [varB, "$notEq", varA], [varC, "$gt", varD]] Am wondering which is the better structure and why. thanks for the guidance
Well, in the current version it checks if it's array to see if it's an $and/$not/$or. So you'd have to use an other trick. Should be possible, f.e. checking if there's a $ in the first element. Btw, I noticed that in the logicPayload it's hard to know if it's a string or a variable. F.e. what if you have a "meow" variable.
Actually, in ES6 compatible code you could use Class for the equations. But I'm guessing you're looking more for a format that can be send as a JSON string.
since both methods are possible, which you believe is easier, better. how can I access if arrays or objects work better. in terms of the variables (keys) in logicPayload, i'm checking if it's a mongoID string. so all variables need to pass ObjectId.isValid( variable) so that's why we have another function for resolving the variable by querying the mongo database.
Personal I find the first, with objects in arrays, more readable.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.