Skip to content

Commit

Permalink
Added a workaround to fix the order by full text (fix omeka/omeka-s#2224
Browse files Browse the repository at this point in the history
).
  • Loading branch information
Daniel Berthereau authored and Daniel Berthereau committed Oct 7, 2024
1 parent 4a1286c commit 797c962
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 28 deletions.
57 changes: 57 additions & 0 deletions Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ public function attachListeners(SharedEventManagerInterface $sharedEventManager)
// Process before any other module in order to reset query.
+200
);

// Omeka S v4.1 does not allow to search fulltext and return scalar.
// And event "api.search.query.finalize" isn't available for scalar.
// @see https://github.com/omeka/omeka-s/pull/2224
$sharedEventManager->attach(
$adapter,
'api.search.query',
[$this, 'fixSearchFullTextScalar'],
// Process after Omeka\Module and last.
-200
);
}

// Manage exception for full text search with resource adapter.
Expand Down Expand Up @@ -682,6 +693,52 @@ public function overrideQueryResourceFullText(Event $event): void
->searchResourcesFullText($qb, $request->getContent());
}

/**
* Process fix when searching fulltext and returning scalar ids.
*
* The option "require_fix_2224" is set when a scalar search with full text
* sort is prepared in query builder.
*
* The fix is set only for the internal querier.
*
* @see https://github.com/omeka/omeka-s/pull/2224
*/
public function fixSearchFullTextScalar(Event $event): void
{
/** @var \Omeka\Api\Request $request */
$request = $event->getParam('request');
if (!$request->getOption('require_fix_2224')) {
return;
}

/** @var \Doctrine\ORM\QueryBuilder $qb */
$qb = $event->getParam('queryBuilder');
$query = $request->getContent();

$scalarField = $request->getOption('returnScalar') ?? $query['return_scalar'] ?? 'id';
$matchOrder = 'MATCH(omeka_fulltext_search.title, omeka_fulltext_search.text) AGAINST (:omeka_fulltext_search)';

$fixQb = clone $qb;
$fixQb
->select(['omeka_root.id' => 'omeka_root.' . $scalarField])
->addSelect($matchOrder . ' AS HIDDEN orderMatch')
->addGroupBy('orderMatch');
$content = array_column($fixQb->getQuery()->getScalarResult(), $scalarField, 'id');

// The response is not yet available, so store results as options of
// the request.
$request
->setOption('results', $content)
->setOption('total_results', count($content));

// Remove the order from main query and limit results and return a fake
// result that is detected early by mariadb/mysql.
$qb
->resetDQLPart('orderBy')
->setMaxResults(0)
->andWhere('1 = 0');
}

public function onFormVocabMemberSelectQuery(Event $event): void
{
$selectElement = $event->getTarget();
Expand Down
61 changes: 33 additions & 28 deletions src/Querier/InternalQuerier.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ public function query(): Response
$plugins = $this->services->get('ControllerPluginManager');
$hasReferences = $plugins->has('references');

// Omeka S v4.1 does not allow to search fulltext and return scalar ids.
// Check if the fix Omeka S is not ready.
// @see https://github.com/omeka/omeka-s/pull/2224
if (isset($this->args['fulltext_search'])
&& substr((string) $this->query->getSort(), 0, 9) === 'relevance'
) {
$requireFix2224 = file_get_contents(OMEKA_PATH . '/application/src/Api/Adapter/AbstractEntityAdapter.php');
$requireFix2224 = !strpos($requireFix2224, '$hasFullTextSearchOrder');
if ($requireFix2224) {
$this->logger->warn('The fix https://github.com/omeka/omeka-s/pull/2224 is not integrated. A workaround is used.'); // @translate
}
} else {
$requireFix2224 = null;
}

// The standard api way implies a double query, because scalar doesn't
// set the full total and doesn't use paginator.
// So get all ids, then slice it here.
Expand All @@ -84,22 +99,17 @@ public function query(): Response
if ($this->byResourceType || $isSpecificQuery) {
foreach ($this->resourceTypes as $resourceType) {
try {
$apiResponse = $api->search($resourceType, $dataQuery, ['returnScalar' => 'id']);
$totalResults = $apiResponse->getTotalResults();
$result = $apiResponse->getContent();
if ($requireFix2224) {
$apiResponse = $api->search($resourceType, $dataQuery, ['returnScalar' => 'id', 'require_fix_2224' => true]);
$totalResults = $apiResponse->getRequest()->getOption('total_results', 0);
$result = $apiResponse->getRequest()->getOption('results', []);
} else {
$apiResponse = $api->search($resourceType, $dataQuery, ['returnScalar' => 'id']);
$totalResults = $apiResponse->getTotalResults();
$result = $apiResponse->getContent();
}
} catch (\Omeka\Api\Exception\ExceptionInterface $e) {
throw new QuerierException($e->getMessage(), $e->getCode(), $e);
} catch (\Doctrine\DBAL\Exception\DriverException $e) {
// The fix Omeka S is not ready.
// @see https://github.com/omeka/omeka-s/pull/2224
$this->logger->err('The fix https://github.com/omeka/omeka-s/pull/2224 is not integrated. You cannot set an order by relevance. A slow workaround is used.'); // @translate
$apiResponse = $api->search($resourceType, $dataQuery, ['responseContent' => 'resource']);
$totalResults = $apiResponse->getTotalResults();
$result = [];
foreach ($apiResponse->getContent() as $resource) {
$id = $resource->getId();
$result[$id] = $id;
}
}
// TODO Currently experimental. To replace by a query + arg "querier=internal".
$this->response->setAllResourceIdsForResourceType($resourceType, array_map('intval', $result) ?: []);
Expand All @@ -122,22 +132,17 @@ public function query(): Response
// It is not possible to return the resource type for now with
// doctrine, but it is useless.
$mainResourceType = count($this->resourceTypes) === 1 ? reset($this->resourceTypes) : 'resources';
$apiResponse = $api->search($mainResourceType, $dataQuery, ['returnScalar' => 'id']);
$totalResults = $apiResponse->getTotalResults();
$result = $apiResponse->getContent();
if ($requireFix2224) {
$apiResponse = $api->search($mainResourceType, $dataQuery, ['returnScalar' => 'id', 'require_fix_2224' => true]);
$totalResults = $apiResponse->getRequest()->getOption('total_results', 0);
$result = $apiResponse->getRequest()->getOption('results', []);
} else {
$apiResponse = $api->search($mainResourceType, $dataQuery, ['returnScalar' => 'id']);
$totalResults = $apiResponse->getTotalResults();
$result = $apiResponse->getContent();
}
} catch (\Omeka\Api\Exception\ExceptionInterface $e) {
throw new QuerierException($e->getMessage(), $e->getCode(), $e);
} catch (\Doctrine\DBAL\Exception\DriverException $e) {
// The fix Omeka S is not ready.
// @see https://github.com/omeka/omeka-s/pull/2224
$this->logger->err('The fix https://github.com/omeka/omeka-s/pull/2224 is not integrated. You cannot set an order by relevance. A slow workaround is used.'); // @translate
$apiResponse = $api->search($mainResourceType, $dataQuery, ['responseContent' => 'resource']);
$totalResults = $apiResponse->getTotalResults();
$result = [];
foreach ($apiResponse->getContent() as $resource) {
$id = $resource->getId();
$result[$id] = $id;
}
}
// TODO Currently experimental. To replace by a query + arg "querier=internal".
$this->response->setAllResourceIdsForResourceType('resources', array_map('intval', $result) ?: []);
Expand Down
2 changes: 2 additions & 0 deletions src/Stdlib/SearchResources.php
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,8 @@ protected function searchResources(QueryBuilder $qb, array $query): self
// ->addGroupBy('orderMatch')
// So add a hidden select and remove order before count, but
// directly in the adapter.
// When requesting scalar results, the fix is included via a
// later event, awaiting integration of fix omeka/omeka-s#2224.
->addOrderBy($matchOrder, $sortOrder);
}

Expand Down

0 comments on commit 797c962

Please sign in to comment.