Давайте проследим за ходом выполнения, используя ваше дерево примеров.
AND
/ \
leftOR rightOR
/ \ / \
X=6 Z=9 T=6 H=8
Когда мы выходим из первого цикла while
, наши стеки выглядят так:
stack = {}
comparatorStack = { AND, leftOR, rightOR }
specStack = { X=6, Z=9, T=6, H=8 }
То же состояние входит в последний цикл while
.
while (!comparatorStack.empty()) {
SearchOperationRequest searchOperationRequest = comparatorStack.pop();
Specification<U> operand1 = specStack.pop();
Specification<U> operand2 = specStack.pop();
if (searchOperationRequest.getComparator().equals(SearchComparator.AND)) {
specStack.push(Specification.where(operand1)
.and(operand2));
} else if (searchOperationRequest.getComparator().equals(SearchComparator.OR)) {
specStack.push(Specification.where(operand1)
.or(operand2));
}
}
Проблема в том, что вы возвращаете результат на specStack
. Поэтому на второй итерации вы будете извлекать результат первой итерации (rightOR
), а также Z=9
и применять к ней логику leftOr
.
Разложение дерева
Давайте сделаем шаг назад и посмотрим, как вы разлагаете дерево, более конкретно:
if (pointer.getLeft() != null) {
pointer.setRight(pointer.getLeft());
pointer.setLeft(null);
} else if (!stack.empty()) {
SearchOperationRequest temp = stack.pop();
pointer.setRight(temp);
}
Проблема с этим кодом в том, что вы изменяете узлы в дереве. В первом примере один раз наш указатель указывает на узел:
Z=9
/ \
null rightOR
Это не выглядит правильно. Вместо декомпозиции дерева с использованием стека ( Поиск в глубину ), вы можете использовать очередь ( Поиск в ширину ) и бесплатно получить нужный вам порядок.
Решает ли это проблему применения каждого логического оператора (comparator
) к правильным операндам? Не совсем, чтобы иметь возможность решить оба макета ниже, мы могли бы разложить операторы и операнды в разных рабочих процессах, вместо того, чтобы объединить их все вместе.
AND | rootAND
/ \ | / \
leftOR rightOR | leftOR rightOR
/ \ / \ | / \ / \
X=6 Z=9 T=6 H=8 | X=6 AND Z=9 H=8
| / \
| T=6 Y=3
Решение
Первое json-подобное представление в вашем посте имеет нелогичное расположение, поскольку логические операторы должны работать как с левым, так и с правым операндом. Вместо этого у вас было:
"right": {
"left": {
"column": "X",
"condition": "EQUALS",
"value": 88
},
"comparator": "AND"
}
Рассмотрим решение для симметричных представлений, в котором для каждого логического оператора присутствуют как левый, так и правый операнд.
Сначала мы обрабатываем дерево Сначала Ширина , уровень за уровнем. Между тем, мы помещаем каждый comparator
в стек, поэтому в нашем втором цикле while
мы сначала выводим последние из них.
Во втором цикле мы используем новый Queue
для хранения наших промежуточных результатов, когда мы возвращаемся к корню.
Queue<SearchOperationRequest> queue = new LinkedList<>();
Deque<SearchOperationRequest> comparatorStack = new LinkedList<>();
if (root == null || !root.isComparator()) return;
queue.add(root);
while(!queue.isEmpty()){
SearchOperationRequest node = queue.poll();
comparatorStack.push(node);
if(node.left != null && node.left.isComparator()) queue.add(node.left);
if(node.right != null && node.right.isComparator()) queue.add(node.right);
}
Queue<Specification> specQueue = new LinkedList<>();
while(!comparatorStack.isEmpty()){
SearchOperationRequest comparator = comparatorStack.pop();
// reverse operand order so already computed values are polled correctly
Specification operand2;
SearchOperationRequest pointer = comparator.getRight();
if(pointer.isTreeLeaf()) {
operand2 = converter.apply(new SpecificationSearchCriteria(pointer.getColumn(), pointer.getCondition(), pointer.getValue()));
} else {
operand2 = specQueue.poll();
}
Specification operand1;
pointer = comparator.getLeft();
if(pointer.isTreeLeaf()) {
operand1 = converter.apply(new SpecificationSearchCriteria(pointer.getColumn(), pointer.getCondition(), pointer.getValue()));
} else {
operand1 = specQueue.poll();
}
if (comparator.equals(SearchComparator.AND)) {
specQueue.add(Specification.where(operand1).and(operand2));
} else if (comparator.equals(SearchComparator.OR)) {
specQueue.add(Specification.where(operand1).or(operand2));
}
}
return specQueue.poll();
Я не тестировал код, но вы должны быть в состоянии извлечь (и реорганизовать) рабочие процессы.