Skip to content

Commit

Permalink
Merge pull request #885 from City-of-Helsinki/UHF-11002
Browse files Browse the repository at this point in the history
UHF-11002
  • Loading branch information
hyrsky authored Jan 21, 2025
2 parents 251ebb1 + 43149eb commit faf583f
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ field_mapper_config:
value: '$._source.name[0]'
tid:
value: '$._source.tid[0]'
location:
value: '$._source.field_location'
storage_client_id: helfi_news_neighbourhoods
storage_client_config: { }
persistent_cache_max_age: 86400
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,11 @@ function helfi_paragraphs_news_list_update_9009() : void {
\Drupal::service('helfi_platform_config.config_update_helper')
->update('helfi_paragraphs_news_list');
}

/**
* UHF-11002: Update news list external entities.
*/
function helfi_paragraphs_news_list_update_9010() : void {
\Drupal::service('helfi_platform_config.config_update_helper')
->update('helfi_paragraphs_news_list');
}
114 changes: 85 additions & 29 deletions modules/helfi_paragraphs_news_list/src/ElasticExternalEntityBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected function request(
array $parameters,
) : array {
try {
return $this->client->search($parameters)->asArray();
return $this->client->search($parameters)?->asArray() ?? [];
}
catch (ElasticsearchException | TransportException $e) {
Error::logException($this->logger, $e);
Expand Down Expand Up @@ -135,6 +135,78 @@ protected function getFieldMapping(string $field) : string {
return $field;
}

/**
* Get callback that builds elasticsearch query fragment for given operator.
*
* @param ?string $op
* Query operation.
*
* @return callable
* Handler.
*/
protected function getOperatorCallback(?string $op): callable {
return match($op) {
'IN' => static function (array $value, string $fieldName) : array {
$inGroup = [];
foreach ($value as $v) {
$inGroup[] = ['term' => [$fieldName => $v]];
}
return [
'query' => [
'bool' => [
'must' => [
['bool' => ['should' => $inGroup]],
],
],
],
];
},
'CONTAINS' => static function (string $value, string $fieldName) : array {
return [
'query' => [
'bool' => [
'must' => [
[
'regexp' => [
$fieldName => [
'value' => $value . '.*',
'case_insensitive' => TRUE,
],
],
],
],
],
],
];
},
'GEO_DISTANCE_SORT' => static function (array $value, string $fieldName) : array {
[$coordinates, $options] = $value;

return [
'sort' => [
[
'_geo_distance' => [
$fieldName => $coordinates,
...$options,
],
],
],
];
},
default => static function (string|int|null $value, string $fieldName) : array {
return [
'query' => [
'bool' => [
'must' => [
['term' => [$fieldName => $value]],
],
],
],
];
},
};
}

/**
* Builds the elastic query for given parameters.
*
Expand All @@ -147,7 +219,10 @@ protected function getFieldMapping(string $field) : string {
* The query.
*/
protected function buildQuery(array $parameters, array $sorts) : array {
$query = [];
$body = [
'sort' => [],
'query' => [],
];

foreach ($parameters as $parameter) {
['field' => $field, 'value' => $value, 'operator' => $op] = $parameter;
Expand All @@ -156,29 +231,9 @@ protected function buildQuery(array $parameters, array $sorts) : array {
if (!$value) {
continue;
}
$callback = match($op) {
'IN' => function (array $value, string $fieldName) : array {
$inGroup = [];
foreach ($value as $v) {
$inGroup[] = ['term' => [$fieldName => $v]];
}
return ['bool' => ['should' => $inGroup]];
},
'CONTAINS' => function (string $value, string $fieldName) : array {
return [
'regexp' => [
$fieldName => [
'value' => $value . '.*',
'case_insensitive' => TRUE,
],
],
];
},
default => function (string|int|null $value, string $fieldName) : array {
return ['term' => [$fieldName => $value]];
},
};
$query['bool']['must'][] = $callback($value, $fieldName);

$callback = $this->getOperatorCallback($op);
$body = array_merge_recursive($body, $callback($value, $fieldName));
}

$sortQuery = [];
Expand All @@ -189,12 +244,13 @@ protected function buildQuery(array $parameters, array $sorts) : array {
$sortQuery[$fieldName] = ['order' => strtolower($direction)];
}

$body = array_merge_recursive($body, [
'sort' => $sortQuery,
]);

return [
'index' => $this->index,
'body' => [
'sort' => $sortQuery,
'query' => $query,
],
'body' => $body,
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,14 @@ final class NewsNeighbourhoods extends TermBase {
*/
protected string $vid = 'news_neighbourhoods';

/**
* {@inheritdoc}
*/
protected function getFieldMapping(string $field) : string {
return match($field) {
'location' => 'field_location',
default => parent::getFieldMapping($field),
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Drupal\Tests\helfi_paragraphs_news_list\Kernel\ExternalEntityStorage;

use Drupal\external_entities\Entity\Query\External\Query;
use Elastic\Elasticsearch\Client;

/**
* Tests news tags storage client.
*
Expand All @@ -25,4 +28,70 @@ protected function getVid(): string {
return 'news_neighbourhoods';
}

/**
* Tests geo_distance sorting.
*/
public function testGeoDistanceQuery(): void {
$client = $this->prophesize(Client::class);
// Test geo distance sort.
$client->search([
'index' => 'news_terms',
'body' => [
'sort' => [
[
'_geo_distance' => [
'field_location' => [
'lat' => 48.8584,
'lon' => 2.2945,
],
'unit' => 'km',
'order' => 'asc',
'distance_type' => 'plane',
'mode' => 'min',
'ignore_unmapped' => FALSE,
],
],
],
'query' => [
'bool' => [
'must' => [
['term' => ['vid' => $this->getVid()]],
],
],
],
],
])
->shouldBeCalled()
->willReturn($this->createElasticsearchResponse([]));

$query = $this->getSut($client->reveal())
->getQuery();

$this->assertInstanceOf(Query::class, $query);

// Drupal query interface is not quite flexible enough to support all the
// options and parameters geo_distance sort needs, so the implementation
// uses setParameter from external_entities Query class.
$query->setParameter('location', [
[
'lat' => 48.8584,
'lon' => 2.2945,
],
[
'unit' => 'km',
// Geo distance sort direction.
'order' => 'asc',
// 'arc' is more accurate, but within
// a city it should not matter.
'distance_type' => 'plane',
// What to do in case a field has several geo points.
'mode' => 'min',
// Unmapped field cause the search to fail.
'ignore_unmapped' => FALSE,
],
], 'GEO_DISTANCE_SORT');

$query->accessCheck(FALSE)->execute();
}

}

0 comments on commit faf583f

Please sign in to comment.