Skip to content
Snippets Groups Projects
Commit c8f42380 authored by Matt Glaman's avatar Matt Glaman Committed by Matt Glaman
Browse files

Issue #3094191 by mglaman: Support fulltext searching

parent 07bfb8b5
Branches
Tags
No related merge requests found
......@@ -3,6 +3,7 @@
namespace Drupal\jsonapi_search_api\Resource;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Http\Exception\CacheableBadRequestHttpException;
use Drupal\Core\Url;
use Drupal\jsonapi\JsonApiResource\Link;
......@@ -12,12 +13,42 @@ use Drupal\jsonapi\ResourceResponse;
use Drupal\jsonapi_resources\Resource\EntityResourceBase;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\ParseMode\ParseModeInterface;
use Drupal\search_api\ParseMode\ParseModePluginManager;
use Drupal\search_api\Query\QueryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* JSON:API Resource to return Search API index results.
*/
final class IndexResource extends EntityResourceBase {
final class IndexResource extends EntityResourceBase implements ContainerInjectionInterface {
/**
* The parse mode manager.
*
* @var \Drupal\search_api\ParseMode\ParseModePluginManager
*/
private $parseModeManager;
/**
* Constructs a new IndexResource object.
*
* @param \Drupal\search_api\ParseMode\ParseModePluginManager $parse_mode_manager
* The parse mode manager.
*/
public function __construct(ParseModePluginManager $parse_mode_manager) {
$this->parseModeManager = $parse_mode_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): IndexResource {
return new self(
$container->get('plugin.manager.search_api.parse_mode')
);
}
/**
* Process the resource request.
......@@ -33,11 +64,13 @@ final class IndexResource extends EntityResourceBase {
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\search_api\SearchApiException
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function process(Request $request, IndexInterface $index): ResourceResponse {
$cacheability = new CacheableMetadata();
// Ensure that different pages will be cached separately.
$cacheability->addCacheContexts(['url.query_args:page']);
$cacheability->addCacheContexts(['url.query_args:filter']);
$query = $index->query();
......@@ -48,6 +81,10 @@ final class IndexResource extends EntityResourceBase {
}
$query->range($pagination->getOffset(), $pagination->getSize());
if ($request->query->has('filter')) {
$this->applyFiltersToQuery($request, $query);
}
// Get the results and convert to JSON:API resource object data.
$results = $query->execute();
$result_entities = array_map(static function (ItemInterface $item) {
......@@ -62,6 +99,28 @@ final class IndexResource extends EntityResourceBase {
return $response;
}
/**
* Apply filters to the index query.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
* @param \Drupal\search_api\Query\QueryInterface $query
* The query.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
protected function applyFiltersToQuery(Request $request, QueryInterface $query): void {
$parse_mode = $this->parseModeManager->createInstance('terms');
assert($parse_mode instanceof ParseModeInterface);
$query->setParseMode($parse_mode);
$filter = $request->query->get('filter');
if (isset($filter['fulltext'])) {
$query->keys($filter['fulltext']);
unset($filter['fulltext']);
}
}
/**
* Get pagination for the request.
*
......
<?php declare(strict_types = 1);
namespace Drupal\Tests\Functional\jsonapi_search_api;
namespace Drupal\Tests\jsonapi_search_api\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
......@@ -125,4 +125,43 @@ final class IndexResourceTest extends BrowserTestBase {
$this->assertArrayHasKey('first', $response_document['links']);
}
/**
* Tests the results contain the index values.
*/
public function testCollectionFulltext() {
$url = Url::fromRoute('jsonapi_search_api.index_database_search_index', [], [
'query' => [
'filter' => [
'fulltext' => 'föö',
],
],
]);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$response = $this->request('GET', $url, $request_options);
$this->assertSame(200, $response->getStatusCode(), var_export(Json::decode((string) $response->getBody()), TRUE));
$response_document = Json::decode((string) $response->getBody());
$this->assertCount(1, $response_document['data'], var_export($response_document, TRUE));
$this->assertSame([1], array_map(static function (array $data) {
return $data['attributes']['drupal_internal__id'];
}, $response_document['data']));
$url = Url::fromRoute('jsonapi_search_api.index_database_search_index', [], [
'query' => [
'filter' => [
'fulltext' => 'foo',
],
],
]);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$response = $this->request('GET', $url, $request_options);
$this->assertSame(200, $response->getStatusCode(), var_export(Json::decode((string) $response->getBody()), TRUE));
$response_document = Json::decode((string) $response->getBody());
$this->assertCount(4, $response_document['data'], var_export($response_document, TRUE));
$this->assertSame([1, 2, 4, 5], array_map(static function (array $data) {
return $data['attributes']['drupal_internal__id'];
}, $response_document['data']));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment