-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
Signed-off-by: Robin Appelman <robin@icewind.nl>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace OC\Files\Search\QueryOptimizer; | ||
|
||
use OCP\Files\Search\ISearchBinaryOperator; | ||
use OCP\Files\Search\ISearchOperator; | ||
|
||
/** | ||
* replace single argument AND and OR operations with their single argument | ||
*/ | ||
class FlattenSingleArgumentBinaryOperation extends ReplacingOptimizerStep { | ||
public function processOperator(ISearchOperator &$operator): bool { | ||
parent::processOperator($operator); | ||
if ( | ||
$operator instanceof ISearchBinaryOperator && | ||
count($operator->getArguments()) === 1 && | ||
( | ||
$operator->getType() === ISearchBinaryOperator::OPERATOR_OR || | ||
$operator->getType() === ISearchBinaryOperator::OPERATOR_AND | ||
) | ||
) { | ||
$operator = $operator->getArguments()[0]; | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
namespace OC\Files\Search\QueryOptimizer; | ||
|
||
use OC\Files\Search\SearchBinaryOperator; | ||
use OCP\Files\Search\ISearchBinaryOperator; | ||
use OCP\Files\Search\ISearchOperator; | ||
|
||
/** | ||
* Attempt to transform | ||
* | ||
* (A AND B) OR (A AND C) into A AND (B OR C) | ||
*/ | ||
class MergeDistributiveOperations extends ReplacingOptimizerStep { | ||
public function processOperator(ISearchOperator &$operator): bool { | ||
if ( | ||
$operator instanceof ISearchBinaryOperator && | ||
$this->isAllSameBinaryOperation($operator->getArguments()) | ||
) { | ||
$topLevelType = $operator->getType(); | ||
|
||
$groups = $this->groupBinaryOperatorsByChild($operator->getArguments(), 0); | ||
$outerOperations = array_map(function(array $operators) use ($topLevelType) { | ||
/** @var ISearchBinaryOperator $firstArgument */ | ||
$firstArgument = $operators[0]; | ||
$outerType = $firstArgument->getType(); | ||
$extractedLeftHand = $firstArgument->getArguments()[0]; | ||
|
||
$rightHandArguments = array_map(function(ISearchOperator $inner) { | ||
/** @var ISearchBinaryOperator $inner */ | ||
$arguments = $inner->getArguments(); | ||
array_shift($arguments); | ||
if (count($arguments) === 1){ | ||
return $arguments[0]; | ||
} | ||
return new SearchBinaryOperator($inner->getType(), $arguments); | ||
}, $operators); | ||
$extractedRightHand = new SearchBinaryOperator($topLevelType, $rightHandArguments); | ||
return new SearchBinaryOperator( | ||
$outerType, | ||
[$extractedLeftHand, $extractedRightHand] | ||
); | ||
}, $groups); | ||
$operator = new SearchBinaryOperator($topLevelType, $outerOperations); | ||
parent::processOperator($operator); | ||
return true; | ||
} | ||
return parent::processOperator($operator); | ||
} | ||
|
||
/** | ||
* Check that a list of operators is all the same type of (non-empty) binary operators | ||
* | ||
* @param ISearchOperator[] $operators | ||
* @return bool | ||
* @psalm-assert-if-true ISearchBinaryOperator[] $operators | ||
*/ | ||
private function isAllSameBinaryOperation(array $operators): bool { | ||
$operation = null; | ||
foreach ($operators as $operator) { | ||
if (!$operator instanceof SearchBinaryOperator) { | ||
return false; | ||
} | ||
if (!$operator->getArguments()) { | ||
return false; | ||
} | ||
if ($operation === null) { | ||
$operation = $operator->getType(); | ||
} else { | ||
if ($operation !== $operator->getType()) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Group a list of binary search operators that have a common argument | ||
* | ||
* @param ISearchBinaryOperator[] $operators | ||
* @return ISearchBinaryOperator[][] | ||
*/ | ||
private function groupBinaryOperatorsByChild(array $operators, int $index): array { | ||
$result = []; | ||
foreach($operators as $operator) { | ||
$childKey = (string) $operator->getArguments()[0]; | ||
Check failure on line 87 in lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php GitHub Actions / static-code-analysisInvalidCast
Check failure Code scanning / Psalm InvalidCast Error
OCP\Files\Search\ISearchOperator cannot be cast to string
|
||
$result[$childKey][] = $operator; | ||
} | ||
return array_values($result); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
namespace OC\Files\Search\QueryOptimizer; | ||
|
||
use OC\Files\Search\SearchBinaryOperator; | ||
use OC\Files\Search\SearchComparison; | ||
use OCP\Files\Search\ISearchBinaryOperator; | ||
use OCP\Files\Search\ISearchComparison; | ||
use OCP\Files\Search\ISearchOperator; | ||
|
||
/** | ||
* transform (field == A OR field == B ...) into field IN (A, B, ...) | ||
*/ | ||
class OrEqualsToIn extends ReplacingOptimizerStep { | ||
public function processOperator(ISearchOperator &$operator): bool { | ||
if ( | ||
$operator instanceof ISearchBinaryOperator && | ||
$operator->getType() == ISearchBinaryOperator::OPERATOR_OR | ||
) { | ||
$groups = $this->groupEqualsComparisonsByField($operator->getArguments()); | ||
Check failure on line 20 in lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php GitHub Actions / static-code-analysisInvalidArgument
Check failure Code scanning / Psalm InvalidArgument Error
Argument 1 of OC\Files\Search\QueryOptimizer\OrEqualsToIn::groupEqualsComparisonsByField expects OCP\Files\Search\ISearchOperator, but array<array-key, OCP\Files\Search\ISearchOperator> provided
|
||
$newParts = array_map(function(array $group) { | ||
if (count($group) > 1) { | ||
// because of the logic from `groupEqualsComparisonsByField` we now that group is all comparisons on the same field | ||
/** @var ISearchComparison[] $group */ | ||
$field = $group[0]->getField(); | ||
$values = array_map(function(ISearchComparison $comparison) { | ||
return $comparison->getValue(); | ||
}, $group); | ||
$in = new SearchComparison(ISearchComparison::COMPARE_IN, $field, $values); | ||
Check failure on line 29 in lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php GitHub Actions / static-code-analysisInvalidArgument
Check failure Code scanning / Psalm InvalidArgument Error
Argument 3 of OC\Files\Search\SearchComparison::__construct expects DateTime|array<array-key, DateTime|int|string>|int|string, but array<array-key, DateTime|array<array-key, DateTime|int|string>|int|string> provided
|
||
$in->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, $group[0]->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)); | ||
return $in; | ||
} else { | ||
return $group[0]; | ||
} | ||
}, $groups); | ||
if (count($newParts) === 1) { | ||
$operator = $newParts[0]; | ||
} else { | ||
$operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $newParts); | ||
} | ||
return true; | ||
} | ||
parent::processOperator($operator); | ||
return false; | ||
} | ||
|
||
/** | ||
* @param array $operators | ||
* @return string|null | ||
Check failure on line 49 in lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php GitHub Actions / static-code-analysisMismatchingDocblockReturnType
Check failure Code scanning / Psalm MismatchingDocblockReturnType Error
Docblock has incorrect return type 'null|string', should be 'bool'
|
||
*/ | ||
private function isAllEquals(array $operators): bool { | ||
foreach ($operators as $operator) { | ||
if (!$operator instanceof ISearchComparison || $operator->getType() !== ISearchComparison::COMPARE_EQUAL) { | ||
return false; | ||
} | ||
} | ||
return $field; | ||
} | ||
|
||
/** | ||
* Non-equals operators are put in a separate group for each | ||
* | ||
* @param ISearchOperator $operators | ||
Check failure on line 63 in lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php GitHub Actions / static-code-analysisMismatchingDocblockParamType
Check failure Code scanning / Psalm MismatchingDocblockParamType Error
Parameter $operators has wrong type 'OCP\Files\Search\ISearchOperator', should be 'array<array-key, mixed>'
|
||
* @return ISearchOperator[][] | ||
*/ | ||
private function groupEqualsComparisonsByField(array $operators): array { | ||
$result = []; | ||
foreach ($operators as $operator) { | ||
if ($operator instanceof ISearchComparison && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { | ||
$key = $operator->getField() . $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true); | ||
$result[$key][] = $operator; | ||
} else { | ||
$result[] = [$operator]; | ||
} | ||
} | ||
return array_values($result); | ||
} | ||
} |