OpenXE/classes/Modules/SuperSearch/SuperSearchEngine.php
2021-05-21 08:49:41 +02:00

139 lines
4.5 KiB
PHP

<?php
namespace Xentral\Modules\SuperSearch;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;
use Xentral\Components\Database\Database;
use Xentral\Modules\SuperSearch\Exception\InvalidArgumentException;
use Xentral\Modules\SuperSearch\SearchEngine\SearchTermParser;
use Xentral\Widgets\SuperSearch\Result\ResultCollection;
use Xentral\Widgets\SuperSearch\Result\ResultGroup;
use Xentral\Widgets\SuperSearch\Result\ResultItem;
final class SuperSearchEngine
{
/** @var Database $db */
private $db;
/** @var SearchTermParser $searchTermParser */
private $searchTermParser;
/**
* @param Database $database
*/
public function __construct(Database $database)
{
$this->searchTermParser = new SearchTermParser();
$this->db = $database;
}
/**
* @param string $searchTerm
* @param array|null $projectIds Projekt-IDs die der Benutzer aufrufen darf;
* null = Keine Einschränkung (nur bei Admins)
* @param array|null $moduleNames Module die der Benutzer aufrufen darf;
* null = Keine Einschränkung (nur bei Admins)
* @param int $resultLimit Anzahl der Ergebnisse
*
* @throws InvalidArgumentException
*
* @return ResultCollection
*/
public function search($searchTerm, array $projectIds = null, array $moduleNames = null, $resultLimit = 30)
{
$resultLimit = (int)$resultLimit;
if ($resultLimit < 1) {
throw new InvalidArgumentException('Parameter value $resultLimit is invalid.');
}
$searchTerm = $this->searchTermParser->parse($searchTerm);
// Ergebnisse mit Projekt-ID 0 immer anzeigen (z.b. Appstore-Ergebnisse)
if (is_array($projectIds) && !in_array(0, $projectIds, true)) {
$projectIds[] = 0;
}
if (is_array($moduleNames) && !in_array('appstore', $moduleNames, true)) {
$moduleNames[] = 'appstore';
}
$sqlProjects = '';
$sqlModules = '';
$bindValues = [
'search_term' => $searchTerm,
'result_limit' => $resultLimit,
];
if ($projectIds !== null) {
$sqlProjects = ' AND sii.project_id IN (:project_ids) ';
$bindValues['project_ids'] = (array)$projectIds;
}
if ($moduleNames !== null) {
$sqlModules = ' AND (sig.module IN (:module_names) OR sig.module IS NULL) ';
$bindValues['module_names'] = (array)$moduleNames;
}
$sql =
"SELECT
sii.index_name, sii.index_id, sig.title AS `index_title`, sii.project_id,
sii.title, sii.subtitle, sii.additional_infos, sii.link, sii.search_words
FROM `supersearch_index_item` AS `sii`
INNER JOIN `supersearch_index_group` AS `sig` ON sii.index_name = sig.name
WHERE MATCH (sii.search_words) AGAINST (:search_term IN BOOLEAN MODE)
{$sqlProjects}
{$sqlModules}
AND sii.outdated = 0 AND sig.active = 1
LIMIT 0, :result_limit";
$data = $this->db->fetchAll($sql, $bindValues);
return $this->buildResultCollection($data);
}
/**
* @param array $data
*
* @return ResultCollection
*/
private function buildResultCollection($data)
{
$lastIndexUpdate = $this->getRecentIndexTime();
$results = new ResultCollection([], $lastIndexUpdate);
foreach ($data as $item) {
if (!$results->hasGroup($item['index_name'])) {
$results->addGroup(new ResultGroup($item['index_name'], $item['index_title']));
}
/** @var ResultGroup $group */
$group = $results->getGroup($item['index_name']);
$group->addItem(ResultItem::fromDbState($item));
}
return $results;
}
/**
* Liefert den Zeitpunkt wann der Index das letzte Mal aktualisiert wurde
*
* @return DateTimeInterface|null
*/
private function getRecentIndexTime()
{
$sql =
'SELECT MAX(GREATEST(IFNULL(sig.last_full_update, 1), IFNULL(sig.last_diff_update, 1))) AS `last_update`
FROM `supersearch_index_group` AS sig WHERE sig.active = 1';
$value = $this->db->fetchValue($sql);
if (empty($value)) {
return null;
}
try {
$dateTime = new DateTimeImmutable($value);
} catch (Exception $exception) {
return null;
}
return $dateTime;
}
}