More generous search

Split the keyword into tokens (at spaces).
Search for every token individually, then combine results with AND.
This commit is contained in:
d-buchmann 2025-05-28 12:34:10 +02:00
parent b8d5b83eee
commit 6b4ff26a63

View file

@ -65,11 +65,16 @@ class PartSearchFilter implements FilterInterface
/** @var bool Use Internal Part number for searching */
protected bool $ipn = true;
/** @var int Helper variable for hacky array_map variable injection */
protected int $it = 0;
public function __construct(
/** @var string The string to query for */
protected string $keyword
)
{
// Transform keyword and trim excess spaces
$keyword = trim(str_replace('+', ' ', $keyword));
}
protected function getFieldsToSearch(): array
@ -125,26 +130,50 @@ class PartSearchFilter implements FilterInterface
return;
}
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field): string {
if ($this->regex) {
if($this->regex) {
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field): string {
return sprintf("REGEXP(%s, :search_query) = TRUE", $field);
}
}, $fields_to_search);
return sprintf("ILIKE(%s, :search_query) = TRUE", $field);
}, $fields_to_search);
//Add Or concatenation of the expressions to our query
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(...$expressions)
);
//Add Or concatenation of the expressions to our query
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(...$expressions)
);
//For regex, we pass the query as is, for like we add % to the start and end as wildcards
if ($this->regex) {
//For regex, we pass the query as is, save html special chars
$queryBuilder->setParameter('search_query', $this->keyword);
} else {
$queryBuilder->setParameter('search_query', '%' . $this->keyword . '%');
return;
}
//Split keyword on spaces, but limit token count to not blow up the DB
$tokens = explode(' ', $this->keyword, 5);
$params = new \Doctrine\Common\Collections\ArrayCollection();
//Perform search of every single token in every selected field, AND the where clauses
for ($i = 0; $i < sizeof($tokens); $i++) {
$this->it = $i;
$tokens[$i] = trim($tokens[$i]);
//Skip empty words (e.g. because of multiple spaces)
if ($tokens[$i] === '') continue;
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field): string {
return sprintf("ILIKE(%s, :search_query%u) = TRUE", $field, $this->it);
}, $fields_to_search);
//Aggregate the parameters for consolidated commission
$params[] = new \Doctrine\ORM\Query\Parameter('search_query' . $i, '%' . $tokens[$i] . '%');
//Add Or concatenation of the expressions to our query
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(...$expressions)
);
}
$queryBuilder->setParameters($params);
}
public function getKeyword(): string
@ -301,5 +330,4 @@ class PartSearchFilter implements FilterInterface
return $this;
}
}