Ответ 1
Пусть выполняется поток выполнения, используя дерево примеров.
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
логику 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
Это не выглядит правильным. Вместо того, чтобы разлагать дерево с помощью стека (Depth First Search), вы можете использовать очередь (Breadth First Search) и получить заказы, которые вы получите бесплатно.
Решает ли это проблему применения каждого логического оператора (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"
}
Рассмотрим решение для симметричных представлений, где оба левого и правого операнда присутствуют для каждого логического оператора.
Сначала мы обрабатываем дерево Breadth First, уровень за уровнем. В то же время, мы ставим каждый 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();
Я не тестировал код, но вы должны иметь возможность извлекать (и реорганизовывать) рабочие процессы.