Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create string out of multi dimensional children array with group nodes

I have this deeply nested array with group of node which i want to create query string something like

(FULL_NAME="x" AND NOT(AGE="30" OR AGE="40" AND (ADDRESS="y" AND STREET="z" AND NOT(USER="admin" OR USER="super admin"))) AND TITLE="Developer")

The json data i have is,

  • list of nodes to receive user input
  • operator to handle multiple node joining
  • if user selects to NOT, the main container which contains it will not have the nodes so it will look like this NOT (X="y" AND Y="x")
  • The first vertical operators are joiner of children and if we choose to have NOT operator, this container becomes a group and will have another container inside it having AND or OR
  • user can choose between NOT, AND, OR but cannot have same operator if they tries to group them which only applicable if it was NOT operator.

My array looks like:

[
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
]

which looks like this in the UI ui-preview

I've tried several ways to generate string out of it but I am having trouble with grouping deeply nested nodes.

Thanks.

like image 927
Robin Avatar asked Oct 19 '20 11:10

Robin


4 Answers

You could take for nodes and children different callbacks and collect the items for getting a string.

const
    data = [{ uuid: "b7f0ddf4-0290-4c7e-bb59-771aa46bc850", operator: "AND", isMain: true, nodes: [{ values: { fieldValue: { FieldName: "ORIGINAL_FILE_NAME" }, operator: "=", primaryOperandValue: "new" }, uuid: "779fb920-eb7f-4441-9b5a-886c7a41e271" }], children: [{ uuid: "7467b8c9-212e-41b8-ac02-04296b95c88c", operator: "NOT", nodes: [], children: [{ operator: "AND", uuid: "eaad7c96-0e8f-466b-a255-1075a8e68647", nodes: [{ uuid: "f6057d1b-56d7-4ee6-ac5b-332fbd180fd4", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: "BETWEEN", primaryOperandValue: "x", secondaryOperandValue: "y" } }], children: [{ uuid: "95fd2b08-cc49-498a-bd9f-c50dc55bc39f", operator: "NOT", nodes: [], children: [{ uuid: "7637ecc1-28b4-47d7-a602-cd172fb5e269", operator: "OR", nodes: [{ uuid: "0598a915-5818-4c6e-a3d5-6724f893871a", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: " > ", primaryOperandValue: "30", secondaryOperandValue: null } }], children: [] }] }] }] }, { uuid: "78218b5b-b18b-4418-beed-b3418361785f", operator: "OR", nodes: [{ uuid: "ec956407-4fc6-46df-baa7-d2233711dc20", values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "ENDS_WITH", primaryOperandValue: "log", secondaryOperandValue: null } }, { values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "BEGINS_WITH", primaryOperandValue: "log", secondaryOperandValue: null }, uuid: "6724e913-6e98-47b6-b6af-972a20f0173d" }], children: [] }] }],
    QUOTE = '"',
    wrap = string => `(${string})`,
    quote = string => `${QUOTE}${string}${QUOTE}`,
    isUnary = s => ['NOT'].includes(s),
    getNodes = ({ values: { fieldValue: { FieldName }, operator, primaryOperandValue, secondaryOperandValue } }) => secondaryOperandValue === null || secondaryOperandValue === undefined
        ? `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)}`
        : `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)} AND ${quote(secondaryOperandValue)}`,
    getChildren = ({ operator, nodes = [], children = [] }) => {
        const values = [...nodes.map(getNodes), ...children.map(getChildren)]
        return isUnary(operator)
            ? `${operator} ${values.join('')}`
            : wrap(values.join(` ${operator} `));
    },
    result = data.map(getChildren).join('');

console.log(result);
like image 70
Nina Scholz Avatar answered Nov 20 '22 07:11

Nina Scholz


Try using recursion, e.g

const queryObject = [
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
]

const operatorString = (items) => items.map(
  ({operator, nodes, children, isMain }) => {
    
    const nodeString = nodes.map(({ values }) => {
      const {fieldValue, operator, primaryOperandValue, secondaryOperandValue} = values
      return fieldValue.FieldName + ' ' + operator + (secondaryOperandValue ?  '("' + primaryOperandValue + '", "' + secondaryOperandValue + '")' : ' "' + primaryOperandValue + '"')
    }).join(" " + operator + " ")
    
    return "( " + nodeString + ' ' + operator + operatorString(children) + " )"

  }
).join("")
console.log(operatorString(queryObject))
like image 35
Owen Kelvin Avatar answered Nov 20 '22 06:11

Owen Kelvin


Not complete, but hopefully this can give you a good, readable start

function processNodes( node ) {
  var nodeStr = " Node [" + node.values.operator + "]";
  
  switch (node.values.operator) {
  
    case "=": 
    case " > ":
    case "<":
    case "ENDS_WITH":
    case "BEGINS_WITH":
      nodeStr = node.values.fieldValue.FieldName;
      nodeStr += " " + node.values.operator;
      nodeStr += " " + node.values.primaryOperandValue;
      break;
  }
  return nodeStr;
}

function processCommands ( cmdArray, operator ) {
  var cmdStr = '';

  for (var i = 0; i < cmdArray.length; i++)
  {
  
    if (cmdArray[i].nodes.length > 0) {  
    cmdStr += " ( ";
      for (var j = 0; j < cmdArray[i].nodes.length; j++) {
        if (j > 0) {
          cmdStr += " " + cmdArray[i].operator + " ";
        }
        cmdStr += processNodes(cmdArray[i].nodes[j]);
        
      }
      cmdStr += " ) ";
    }
    if ( cmdStr || cmdArray[i].children.length > 0) {
        cmdStr += " " + cmdArray[i].operator + " ";
    }
      
    if (cmdArray[i].children.length > 0) {      
      cmdStr += " " + processCommands(cmdArray[i].children, cmdArray[i].operator);
    }
  }

  return cmdStr;

}


var cmdArray = [
    {
       "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
       "operator":"AND",
       "isMain":true,
       "nodes":[
          {
             "values":{
                "fieldValue":{
                   "FieldName":"ORIGINAL_FILE_NAME",
                },
                "operator":"=",
                "primaryOperandValue":"new"
             },
             "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
          }
       ],
       "children":[
          {
             "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
             "operator":"NOT",
             "nodes":[],
             "children":[
                {
                   "operator":"AND",
                   "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                   "nodes":[
                      {
                         "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                         "values":{
                            "fieldValue":{
                               "FieldName":"CONTROL_NUMBER",
                            },
                            "operator":"BETWEEN",
                            "primaryOperandValue":"x",
                            "secondaryOperandValue":"y"
                         }
                      }
                   ],
                   "children":[
                      {
                         "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                         "operator":"NOT",
                         "nodes":[],
                         "children":[
                            {
                               "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                               "operator":"OR",
                               "nodes":[
                                  {
                                     "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                     "values":{
                                        "fieldValue":{
                                           "FieldName":"CONTROL_NUMBER",
                                        },
                                        "operator":" > ",
                                        "primaryOperandValue":"30",
                                        "secondaryOperandValue":null
                                     }
                                  }
                               ],
                               "children":[]
                            }
                         ]
                      }
                   ]
                }
             ]
          },
          {
             "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
             "operator":"OR",
             "nodes":[
                {
                   "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                   "values":{
                      "fieldValue":{
                         "FieldName":"EMAIL_ANY_ADDRESS",
                      },
                      "operator":"ENDS_WITH",
                      "primaryOperandValue":"log",
                      "secondaryOperandValue":null
                   }
                },
                {
                   "values":{
                      "fieldValue":{
                         "FieldName":"EMAIL_ANY_ADDRESS",
                      },
                      "operator":"BEGINS_WITH",
                      "primaryOperandValue":"log",
                      "secondaryOperandValue":null
                   },
                   "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
                }
             ],
             "children":[
                
             ]
          }
       ]
    }
 ];

console.log(processCommands(cmdArray));
 
like image 2
Richard Hubley Avatar answered Nov 20 '22 05:11

Richard Hubley


You could do something like this. It uses a handful of helper functions and a main recursive function

const init = () => {
    
    //  special logic to handle javascript to SQL
    const nodeToClause = (node) => {
        let r = [];
        switch (node.values.operator) {
            case 'BETWEEN':
               r = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue,node.values.secondaryOperandValue];
                break;
            case '=':
            case 'BEGINS_WITH':
            case 'ENDS_WITH':
                r = [node.values.fieldValue.FieldName,node.values.operator,`"${node.values.primaryOperandValue}"`];
                break;
            default:
               r = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue];
                break;
        }
        return r.join(' ');
    };
    //  serialize
    const toSQL = (statement) => {
        let r = '';
        const glue = ' ' + statement.operator + ' ';
        if (statement.nodes.length) {
            r = statement.nodes.map(nodeToClause).join(glue);    
        }
        return r;
    };
    // helps with formatting. not necessary, but nice
    const tabs = (n) => {
        const t = ['\n'];
        let i = 0;
        while (i < n) {
            t.push('\t');
            i++;
        }
        return t.join('');
    };
    //  the recursive function that does all the work
    const recurse = (queryFragment, joiner, level=0) => {
        let r = "";
        if (Array.isArray(queryFragment)) {
            r = queryFragment.map(frag => {
                return recurse(frag, frag.operator, level);
            }).join(joiner);
        } else {
            r = tabs(level) + toSQL(queryFragment);
            if ("children" in queryFragment && queryFragment.children.length) {
                r += ' ' + queryFragment.operator + ' (' +  recurse(queryFragment.children,queryFragment.operator,level+1) + tabs(level) + ') ';
            }
        }
        return r;
    }; 
    
    //  load the data into DOM
    const x = recurse(query,';');
    output.innerText = x;

};

// output to screen
window.setTimeout(init, 997);
const output = document.getElementById('output');

// test data
const query = [
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
];
pre {
    -moz-tab-size:    4;
    -o-tab-size:      4;
    tab-size:         4;
    font-family: "Courier";
    font-weight: bold;
    color: saddlebrown;
}
body {
   background-color: #bbbf3e94;
}
<pre class="text" id="output"></pre>
like image 1
code_monk Avatar answered Nov 20 '22 06:11

code_monk