Skip to content

Commit

Permalink
Complete revision of the QueryParamsBuilder and the templates for nes…
Browse files Browse the repository at this point in the history
…ted fields and multiple facets
  • Loading branch information
thomas-sc committed Jan 8, 2025
1 parent 199eee2 commit 0f29edd
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 125 deletions.
262 changes: 156 additions & 106 deletions Classes/Common/QueryParamsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ class QueryParamsBuilder
protected string $indexName = '';
protected bool $searchAll = false;

public static function createQueryParamsBuilder(array $searchParams, array $settings): QueryParamsBuilder
// ToDo: @Matthias: check searchAll condition

public static function createQueryParamsBuilder(array $searchParams, array $settings): self
{
$queryParamsBuilder = new QueryParamsBuilder();
$queryParamsBuilder = new self();

return $queryParamsBuilder->
setSettings($settings)->
Expand Down Expand Up @@ -65,7 +67,6 @@ public function setSearchParams($searchParams): QueryParamsBuilder
return $this;
}

//Todo: get Config for bibIndex, aggs etc. from extension config?
public function getQueryParams(): array
{
$commonConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('liszt_common');
Expand All @@ -84,7 +85,7 @@ public function getQueryParams(): array
];

if ($this->searchAll == false) {
$this->query['body']['aggs'] = self::getAggs($this->settings, $this->indexName);
$this->query['body']['aggs'] = $this->getAggs();
}

$this->setCommonParams();
Expand All @@ -95,14 +96,15 @@ public function getQueryParams(): array
return $this->query;
}

public function getCountQueryParams(): array
// count is not needed anymore because we use this parameter from search request
/* public function getCountQueryParams(): array
{
$this->query = [ 'body' => [ ] ];
$this->setCommonParams();
return $this->query;
}
}*/
private function getIndexName(): string
{
if (isset($this->params['index'])) {
Expand All @@ -114,86 +116,117 @@ private function getIndexName(): string
join(',');
}

private static function getAggs(array $settings, string $index): array


private function getAggs(): array
{
return Collection::wrap($settings)->
recursive()->
get('entityTypes')->
filter(function($entityType) use ($index) {return $entityType->get('indexName') === $index;})->
values()->
get(0)->
get('filters')->
mapWithKeys(function($entityType) {
if ($entityType['type'] == 'terms') {
return [$entityType['field'] => [
'terms' => [
'field' => $entityType['field'] . '.keyword'
]
]];
$settings = $this->settings;
$index = $this->indexName;
$filterParams = $this->params['filter'] ?? [];
$filterTypes = $this->getFilterTypes();

return Collection::wrap($settings)
->recursive()
->get('entityTypes')
->filter(function($entityTypes) use ($index) {
return $entityTypes->get('indexName') === $index;
})
->values()
->get(0)
->get('filters')
->mapWithKeys(function($entityType) use ($filterParams, $filterTypes) {

$entityField = $entityType['field'];
$entityTypeKey = $entityType['key'] ?? null;

// create filter in aggs for filtering aggs (without filtering the own key for multiple selections)
$filters = array_values(
array_filter(
array_map(
function ($key, $values) use ($entityField, $filterTypes) {
if ($key !== $entityField) {
// handle nested fields
if (($filterTypes[$key]['type'] == 'nested') && (isset($filterTypes[$key]['key']))) {
return [
'nested' => [
'path' => $key,
'query' => [
'bool' => [
'filter' => [
'terms' => [ $key.'.'.$filterTypes[$key]['key'].'.keyword' => array_keys($values)]
]
]
]
]
];
}
// handle all other fields (not nested fields)
return ['terms' => [$key . '.keyword' => array_keys($values)]];
}
return null; // exclude own key for multiple selects
},
array_keys($filterParams),
$filterParams
)
)
);

// match_all if filters are empty because elastics error without the filter key
if (empty($filters)) {
$filters = [
['match_all' => (object) []]
];
}
if ($entityType['type'] == 'keyword') {
return [$entityType['field'] => [
'terms' => [
'field' => $entityType['field']

// special aggs for nested fields
if ($entityType['type'] === 'nested') {

return [
$entityType['field'] => [
'filter' => [
'bool' => [
'filter' => $filters
]
],
'aggs' => [
'filtered_params' => [
'nested' => [
'path' => $entityField
],
'aggs' => [
$entityField => [
'terms' => [
'field' => $entityField . '.' . $entityTypeKey . '.keyword',
'size' => 15,
]
]
]
]
]
]
]];
];

}

// all other (not nested fields)
return [
$entityType['field'] => [
'nested' => [
'path' => $entityType['field']
],
$entityField => [
'aggs' => [
'names' => [
$entityField => [
'terms' => [
'script' => [
'source' => $entityType['script'],
'lang' => 'painless'
],
'size' => 15,
'field' => $entityField . '.keyword',
'min_doc_count' => 0
]
]
],
'filter' => [
'bool' => [
'filter' => $filters
]
]
]
];
})->
toArray();
}

private static function getFilter(array $field): array
{
if (
isset($field['type']) &&
$field['type'] == 'terms'
) {
return [
'terms' => [
$field['name'] . '.keyword' => $field['value']
]
];
}

if (
isset($field['type']) &&
$field['type'] == 'keyword'
) {
return [
'terms' => [
$field['name'] => $field['value']
]
];
}

return [
'nested' => [
'path' => $field['name'],
'query' => [
'match' => [
$field['name'] . '.' . $field['path'] => $field['value']
]
]
]
];
})->toArray();
}

/**
Expand All @@ -206,7 +239,7 @@ private function setCommonParams(): void
$this->query['index'] = $index;

// set body
if (!isset($this->params['searchText']) || $this->params['searchText'] == '') {
if (empty($this->params['searchText'])) {
$this->query['body']['query'] = [
'bool' => [
'must' => [[
Expand Down Expand Up @@ -239,43 +272,31 @@ private function setCommonParams(): void
];
}

// set filters
$filterTypes = $this->getFilterTypes();
$query = $this->query;
Collection::wrap($this->params['filter'] ?? [])
->each(function($value, $key) use (&$query) {
// get array keys from $value as new array for multiple facettes
->each(function($value, $key) use (&$query, $filterTypes) {
$value = array_keys($value);
// $value = array('Rochester','Bonn');

if ($key !== 'creators') {
$query['body']['query']['bool']['filter'][] = self::getFilter([
'name' => $key,
//'type' => $field['type'],
'type' => 'terms',
'value' => $value
]);

// post_filter for multiple selection facettes and OR function to combine results from multiple facettes
/* $query['body']['post_filter']['bool']['should'][] = self::getFilter([
'name' => $key,
//'type' => $field['type'],
'type' => 'terms',
'value' => $value
],
);*/
if (($filterTypes[$key]['type'] == 'nested') && (isset($filterTypes[$key]['key']))) {

// nested filter query (for multiple Names)
$query['body']['post_filter']['bool']['filter'][] = [
'nested' => [
'path' => $key,
'query' => [
'terms' => [
$key.'.'.$filterTypes[$key]['key'].'.keyword' => $value
]
]
]
];

} else {
// its not a filter query because they need 100% match (with spaces from f_creators_name)
// better would be to build the field 'fullName' at build time with PHP?
$query['body']['query']['bool']['must'][] = [
'nested' => [
'path' => 'creators',
'query' => [
'match' => [
'creators.fullName' => $value
]
]

// post_filter, runs the search without considering the aggregations, for muliple select aggregations we run the filters again on each agg in getAggs()
$query['body']['post_filter']['bool']['filter'][] = [
'terms' => [
$key . '.keyword' => $value
]
];
}
Expand All @@ -284,4 +305,33 @@ private function setCommonParams(): void

}



/**
* Retrieves filter types based on the current indexName and settings from extension.
*
* @return array
*/
private function getFilterTypes(): array
{
return Collection::wrap($this->settings)
->recursive()
->get('entityTypes')
->filter(function ($entityType) {
return $entityType->get('indexName') === $this->indexName;
})
->values()
->get(0)
->get('filters')
->mapWithKeys(function ($filter) {
return [
$filter['field'] => [
'type' => $filter['type'],
'key' => $filter['key'] ?? ''
]
];
})
->all();
}

}
2 changes: 1 addition & 1 deletion Classes/Interfaces/ElasticSearchServiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ public function getElasticInfo(): array;

public function search(array $searchParams, array $settings): Collection;

public function count(array $searchParams, array $settings): int;
// public function count(array $searchParams, array $settings): int;

}
7 changes: 4 additions & 3 deletions Classes/Services/ElasticSearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function search(array $searchParams, array $settings): Collection
{
$this->init();
$this->params = QueryParamsBuilder::createQueryParamsBuilder($searchParams, $settings)->getQueryParams();
print_r($this->params);
//print_r($this->params);
// ToDo: handle exceptions!
$response = $this->client->search($this->params)->asArray();
$aggs = $response['aggregations'];
Expand All @@ -65,12 +65,13 @@ public function search(array $searchParams, array $settings): Collection
return new Collection($response);
}

public function count(array $searchParams, array $settings): int
// Count is not needed, we use this parameter from search request
/* public function count(array $searchParams, array $settings): int
{
$this->init();
$this->params = QueryParamsBuilder::createQueryParamsBuilder($searchParams, $settings)->getCountQueryParams();
$response = $this->client->count($this->params);
return $response['count'];
}
}*/

}
Loading

0 comments on commit 0f29edd

Please sign in to comment.