OrEqualsToIn.php 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\Files\Search\QueryOptimizer;
  7. use OC\Files\Search\SearchBinaryOperator;
  8. use OC\Files\Search\SearchComparison;
  9. use OCP\Files\Search\ISearchBinaryOperator;
  10. use OCP\Files\Search\ISearchComparison;
  11. use OCP\Files\Search\ISearchOperator;
  12. /**
  13. * transform (field == A OR field == B ...) into field IN (A, B, ...)
  14. */
  15. class OrEqualsToIn extends ReplacingOptimizerStep {
  16. public function processOperator(ISearchOperator &$operator): bool {
  17. if (
  18. $operator instanceof ISearchBinaryOperator &&
  19. $operator->getType() === ISearchBinaryOperator::OPERATOR_OR
  20. ) {
  21. $groups = $this->groupEqualsComparisonsByField($operator->getArguments());
  22. $newParts = array_map(function (array $group) {
  23. if (count($group) > 1) {
  24. // because of the logic from `groupEqualsComparisonsByField` we now that group is all comparisons on the same field
  25. /** @var ISearchComparison[] $group */
  26. $field = $group[0]->getField();
  27. $values = array_map(function (ISearchComparison $comparison) {
  28. /** @var string|integer|bool|\DateTime $value */
  29. $value = $comparison->getValue();
  30. return $value;
  31. }, $group);
  32. $in = new SearchComparison(ISearchComparison::COMPARE_IN, $field, $values);
  33. $pathEqHash = array_reduce($group, function ($pathEqHash, ISearchComparison $comparison) {
  34. return $comparison->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true) && $pathEqHash;
  35. }, true);
  36. $in->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, $pathEqHash);
  37. return $in;
  38. } else {
  39. return $group[0];
  40. }
  41. }, $groups);
  42. if (count($newParts) === 1) {
  43. $operator = $newParts[0];
  44. } else {
  45. $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $newParts);
  46. }
  47. parent::processOperator($operator);
  48. return true;
  49. }
  50. parent::processOperator($operator);
  51. return false;
  52. }
  53. /**
  54. * Non-equals operators are put in a separate group for each
  55. *
  56. * @param ISearchOperator[] $operators
  57. * @return ISearchOperator[][]
  58. */
  59. private function groupEqualsComparisonsByField(array $operators): array {
  60. $result = [];
  61. foreach ($operators as $operator) {
  62. if ($operator instanceof ISearchComparison && $operator->getType() === ISearchComparison::COMPARE_EQUAL) {
  63. $result[$operator->getField()][] = $operator;
  64. } else {
  65. $result[] = [$operator];
  66. }
  67. }
  68. return array_values($result);
  69. }
  70. }