Skip to content

Facet Summary Processor misbehavior on Date Range + Search Query #283

@DiegoPino

Description

@DiegoPino

What?

For some strange reason (reason I'm opening this ISSUE) when a Facet Range Widget + a normal Search Query is done and there are no results the Facet Summary Links for both the date Facet entry and the Search item are incorrect and do not allow to "disable" just one of them.

What is strange is if we add an extra facet selection to this (even without results) the Links are OK.

This is happening somewhere here:

public function build(FacetsSummaryInterface $facets_summary, array $build, array $facets) {
$config = $facets_summary->getProcessorConfigs()[$this->getPluginId()];
$results_count = array_sum(array_map(function ($it) {
/** @var \Drupal\facets\FacetInterface $it */
return count($it->getResults());
}, $facets));
if (($results_count == 0 ) || ($config['settings']['enable_query'] ?? FALSE)) {
/** @var \drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source_plugin */
$facet_source_plugin = \Drupal::service(
'plugin.manager.facets.facet_source'
)->createInstance($facets_summary->getFacetSourceId());
$search_id = $facet_source_plugin->getDisplay()->getPluginId();
$results_query = \Drupal::service('search_api.query_helper')->getResults(
$search_id
);
}
if ($results_count == 0 && $results_query) {
$results = [];
foreach ($facets as $facet) {
$build_stage_processors = $facet->getProcessorsByStage(ProcessorInterface::STAGE_BUILD);
$active_values = $facet->getActiveItems();
// Need to reset this bc each facet might have a different Query processor.
$facet_results = [];
if (empty($active_values)) {
continue;
}
foreach ($active_values as $active_value) {
if ($facet->getQueryType() == 'search_api_date' && !is_array($active_value)) {
if (in_array(
'date_item', array_keys($build_stage_processors)
)
) {
try {
$active_value = $this->getTimestamp($active_value, $build_stage_processors['date_item']->getConfiguration()['granularity'] ?? 1);
} catch (\Exception $exception) {
// Do nothing if the String to Timestamp Date failed.
continue;
}
}
}
$facet_results[] = [
'filter' => $active_value,
'count' => 1,
];
}
$configuration = [
'query' => $results_query->getQuery(),
'facet' => $facet,
'results' => $facet_results ?? [],
];
$query_type = \Drupal::service('plugin.manager.facets.query_type')
->createInstance(
$facet->getQueryType(), $configuration
);
$query_type->getConfiguration();
$query_type->build();
// This will just rebuild the widget bc the facets
// were already built and statically cached so ...\Drupal::service('facets.manager')->build($facet);
$facet_results = $facet->getResults();
foreach ($build_stage_processors as $processor) {
if (!$processor instanceof
\Drupal\facets\Processor\BuildProcessorInterface
) {
throw new InvalidProcessorException("The processor {$processor->getPluginDefinition()['id']} has a build definition but doesn't implement the required BuildProcessorInterface interface");
}
$facet_results = $processor->build($facet, $facet_results);
}
$facet->setResults($facet_results);
// Accumulate URLs?
foreach( $facet->getResults() as $result) {
$urls[] = $result->getUrl();
}
$results = array_merge(
$results, $this->buildResultTree($facet->getResults())
);
}
$build['#items'] = $results;
if ($config['settings']['enable_empty_message'] ?? FALSE) {
$item = [
'#theme' => 'facets_summary_empty',
'#message' => [
'#type' => 'processed_text',
'#text' => $config['settings']['text']['value'],
'#format' => $config['settings']['text']['format'],
],
];
array_unshift($build['#items'], $item);
}
}
if (($config['settings']['enable_query'] ?? FALSE) && $results_query) {
// The original View
/** @var \Drupal\views\ViewExecutable $view */
$view = $results_query->getQuery()->getOptions()['search_api_view'];
if ($view->getDisplay()->displaysExposed()) {
$exposed_input = $view->getExposedInput();
$view->getRequest()->getRequestUri();
$keys_to_filter = [];
$key_with_search_value = [] ;
// Oh gosh, blocks...
if (!$view->getDisplay()->isDefaultDisplay() && empty($view->getDisplay()->options['filters'] ?? [] )) {
$filters = $view->getDisplay()->handlers['filter'];
foreach ($filters as $filter) {
/* @var \Drupal\views\Plugin\views\ViewsHandlerInterface $filter */
if ($filter->getPluginId() == 'search_api_fulltext' && $filter->isExposed()) {
$keys_to_filter[] = $filter->options['expose']['operator_id'] ?? NULL;
$keys_to_filter[] = $filter->options['expose']['identifier'] ?? NULL;
$key_with_search_value[] = $filter->options['expose']['identifier'] ?? NULL;
}
elseif ($filter->getPluginId() == 'sbf_advanced_search_api_fulltext'
&& $filter->isExposed()
) {
$extra_keys_to_filter = [];
$keys_to_filter[] =$filter->options['expose']['operator_id'] ?? NULL;
$keys_to_filter[] = $filter->options['expose']['identifier'] ?? NULL;
$key_with_search_value[] = $filter->options['expose']['identifier'] ?? NULL;
$keys_to_filter[] = $filter->options['expose']['searched_fields_id'] ?? NULL;
$keys_to_filter[] = $filter->options['expose']['advanced_search_operator_id']
?? NULL;
foreach ($keys_to_filter as $key_to_filter) {
for (
$i = 1;
$i < $filter->options['expose']['advanced_search_fields_count'] ?? 1;
$i++
) {
$extra_keys_to_filter[] = $key_to_filter . '_' . $i;
if (in_array($key_to_filter, $key_with_search_value)){
$key_with_search_value[] = $key_to_filter . '_' . $i;
}
}
}
$keys_to_filter = array_filter($keys_to_filter);
$keys_to_filter = array_merge(
$keys_to_filter, $extra_keys_to_filter
);
}
}
}
else {
foreach ($view->getDisplay()->options['filters'] as $filter) {
if ($filter['plugin_id'] == 'search_api_fulltext'
&& $filter['exposed']
) {
$keys_to_filter[] = $filter['expose']['operator_id'] ?? NULL;
$keys_to_filter[] = $filter['expose']['identifier'] ?? NULL;
$key_with_search_value[] = $filter['expose']['identifier'] ??
NULL;
}
elseif ($filter['plugin_id'] == 'sbf_advanced_search_api_fulltext'
&& $filter['exposed']
) {
$extra_keys_to_filter = [];
$keys_to_filter[] = $filter['expose']['operator_id'] ?? NULL;
$keys_to_filter[] = $filter['expose']['identifier'] ?? NULL;
$key_with_search_value[] = $filter['expose']['identifier'] ??
NULL;
$keys_to_filter[] = $filter['expose']['searched_fields_id'] ??
NULL;
$keys_to_filter[]
= $filter['expose']['advanced_search_operator_id']
?? NULL;
foreach ($keys_to_filter as $key_to_filter) {
for (
$i = 1;
$i < $filter['expose']['advanced_search_fields_count'] ?? 1;
$i++
) {
$extra_keys_to_filter[] = $key_to_filter . '_' . $i;
if (in_array($key_to_filter, $key_with_search_value)) {
$key_with_search_value[] = $key_to_filter . '_' . $i;
}
}
}
$keys_to_filter = array_filter($keys_to_filter);
$keys_to_filter = array_merge(
$keys_to_filter, $extra_keys_to_filter
);
}
}
}
$request = $view->getRequest();
$facet_source = $facets_summary->getFacetSource();
$urlGenerator = \Drupal::service('facets.utility.url_generator');
$url_active = $urlGenerator->getUrlForRequest(
$request, $facet_source ? $facet_source->getPath() : NULL
);
$params = $request->query->all();
$search_terms = [];
foreach ($params as $key => $param) {
if (in_array($key, $keys_to_filter)) {
$search_term = NULL;
if (in_array($key, $key_with_search_value)) {
$search_terms[] = $exposed_input[$key] ?? NULL;
}
unset($params[$key]);
}
}
$search_terms = array_filter($search_terms, function ($element) {
return ((is_string($element) && '' !== trim($element)) || is_numeric($element));
});
if (count($search_terms)) {
$url_active->setOption('query', $params);
$item = [
'#theme' => 'facets_result_item__summary',
'#value' => implode(" ", array_map(function($string) {
return '"' . $string . '"';
}, $search_terms)),
'#show_count' => FALSE,
'#count' => 0,
'#is_active' => TRUE,
];
$item = (new Link($item, $url_active))->toRenderable();
$item['#wrapper_attributes'] = [
'class' => [
'facet-summary-item--facet',
'facet-summary-item--query',
],
];
$build['#items'][] = $item;
}
}
}
return $build;
}

How to test (make it fail)?

Enable this Facet summary processor with all its options and disable the existing "Show a message when no results" built in one. Make a Faceted Search using a Date Range Facet. Then search for something that you know does not exist. When no results are returned this processor will try to generate Facet Summaries using an alternative method but the links for each Facet Summary will be wrong.

How to see the odd part working?

Enable this Facet summary processor with all its options and disable the existing "Show a message when no results" built in one. Make a Faceted Search using a Date Range Facet. The select another Facet (e.g Digital Object Type). Then search for something that you know does not exist. When no results are returned this processor will try to generate Facet Summaries using an alternative method and the links will all be right allowing you to deselect one of the choices without affecting the others.

Metadata

Metadata

Assignees

Labels

Drupal ViewsAsk and you should receiveFacetsSafely navigating the too many choicesSub ModulesWhen you need more .info.yml files to keep life organizedUI driven hintsShow stuff on screen so people have less guessing to doUXLike UI but with an XbugSomething isn't workinghelp wantedExtra attention is neededquestionFurther information is requested

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions