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,
NOT (X="y" AND Y="x")
NOT operator, this container becomes a group and will have another container inside it having AND or OR
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

I've tried several ways to generate string out of it but I am having trouble with grouping deeply nested nodes.
Thanks.
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);
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))
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));
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With