Skip to content

Commit

Permalink
Solr: Add support for displaying checkbox facet result counts. (#4060)
Browse files Browse the repository at this point in the history
  • Loading branch information
EreMaijala authored Nov 7, 2024
1 parent 1390c2e commit e92629f
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 35 deletions.
4 changes: 4 additions & 0 deletions config/vufind/facets.ini
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ top_rows = 2
;orFacets = *
; Do we want any facets to be collapsed by default?
;collapsedFacets = *
; Should we display result counts for checkbox facets (default is false)?
; Note that this does not currently support [CustomFilters], and with Blender
; all active backends need to support counts for correct results.
;checkboxFacetCounts = true

; This can be used to sort specific facet fields alphabetically by index value
; (which normally results in alphabetical order).
Expand Down
20 changes: 3 additions & 17 deletions module/VuFind/src/VuFind/AjaxHandler/GetSideFacets.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* PHP version 8
*
* Copyright (C) The National Library of Finland 2018-2023.
* Copyright (C) The National Library of Finland 2018-2024.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -227,10 +227,10 @@ protected function formatFacets(
) {
$response = [];
$facetSet = $recommend->getFacetSet();
$checkboxCounts = $results->getOptions()->displayCheckboxFacetCounts();
foreach ($facets as $facet) {
if (strpos($facet, ':')) {
$response[$facet]['checkboxCount']
= $this->getCheckboxFacetCount($facet, $results);
$response[$facet]['checkboxCount'] = $checkboxCounts ? $recommend->getCheckboxFacetCount($facet) : null;
} else {
$context['facet'] = $facet;
$context['cluster'] = $facetSet[$facet] ?? [
Expand All @@ -246,18 +246,4 @@ protected function formatFacets(
}
return $response;
}

/**
* Get the result count for a checkbox facet
*
* @param string $facet Facet
* @param Results $results Search results
*
* @return int|null
*/
protected function getCheckboxFacetCount($facet, Results $results)
{
// There's currently no good way to return counts for checkbox filters.
return null;
}
}
58 changes: 56 additions & 2 deletions module/VuFind/src/VuFind/Recommend/SideFacets.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,16 @@ public function setConfig($settings)
public function init($params, $request)
{
$mainFacets = $this->mainFacets;
$checkboxFacets = $this->checkboxFacets;
if ($request != null && ($enabledFacets = $request->get('enabledFacets', null)) !== null) {
$mainFacets = array_intersect_key($mainFacets, array_flip($enabledFacets));
$checkboxFacets = array_intersect_key($checkboxFacets, array_flip($enabledFacets));
}
// Turn on side facets in the search results:
foreach ($mainFacets as $name => $desc) {
$params->addFacet($name, $desc, in_array($name, $this->orFacets));
}
foreach ($this->checkboxFacets as $name => $desc) {
foreach ($checkboxFacets as $name => $desc) {
$params->addCheckboxFacet($name, $desc);
}
}
Expand All @@ -284,10 +286,17 @@ public function init($params, $request)
*/
public function getCheckboxFacetSet()
{
return $this->results->getParams()->getCheckboxFacets(
$result = $this->results->getParams()->getCheckboxFacets(
array_keys($this->checkboxFacets),
$this->showDynamicCheckboxFacets
);
// Add counts if available:
$checkboxCounts = $this->results->getOptions()->displayCheckboxFacetCounts();
foreach ($result as &$facet) {
$facet['count'] = $checkboxCounts ? $this->getCheckboxFacetCount($facet['filter']) : null;
}
unset($facet);
return $result;
}

/**
Expand Down Expand Up @@ -493,4 +502,49 @@ public function getHierarchicalFacetSortOptions()
{
return $this->hierarchicalFacetSortOptions;
}

/**
* Get the result count for a checkbox facet
*
* @param string $facet Facet
*
* @return ?int
*/
public function getCheckboxFacetCount(string $facet): ?int
{
$checkboxFacets = $this->results->getParams()->getCheckboxFacets();
$delimitedFacets = $this->results->getParams()->getOptions()->getDelimitedFacets(true);
foreach ($checkboxFacets as $checkboxFacet) {
if ($facet !== $checkboxFacet['filter']) {
continue;
}
[$field, $value] = explode(':', $facet, 2);
$checkboxResults = $this->results->getFacetList([$field => $value]);
if (!isset($checkboxResults[$field]['list'])) {
return null;
}
$count = 0;
$truncate = substr($value, -1) === '*';
if ($truncate) {
$value = substr($value, 0, -1);
}
foreach ($checkboxResults[$field]['list'] as $item) {
$itemValue = $item['value'];
if ($delimiter = $delimitedFacets[$field] ?? '') {
[$itemValue] = explode($delimiter, $itemValue);
}
if (
$itemValue == $value
|| ($truncate
&& preg_match('/^' . preg_quote($value, '/') . '/', $item['value']))
|| ($item['value'] == 'true' && $value == '1')
|| ($item['value'] == 'false' && $value == '0')
) {
$count += $item['count'];
}
}
return $count;
}
return null;
}
}
18 changes: 18 additions & 0 deletions module/VuFind/src/VuFind/Search/Base/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,13 @@ abstract class Options implements TranslatorAwareInterface
*/
protected $displayCitationLinksInResults;

/**
* Should we display checkbox facet counts in results?
*
* @var bool
*/
protected $displayCheckboxFacetCounts;

/**
* Constructor
*
Expand Down Expand Up @@ -421,6 +428,7 @@ public function __construct(\VuFind\Config\PluginManager $configLoader)
= $facetSettings?->HierarchicalExcludeFilters?->toArray() ?? [];
$this->hierarchicalFacetFilters
= $facetSettings?->HierarchicalFacetFilters?->toArray() ?? [];
$this->displayCheckboxFacetCounts = (bool)($facetSettings->Results_Settings->checkboxFacetCounts ?? false);

$searchSettings = $configLoader->get($this->searchIni);
$this->retainFiltersByDefault = $searchSettings->General->retain_filters_by_default ?? true;
Expand Down Expand Up @@ -1307,6 +1315,16 @@ public function displayCitationLinksInResults(): bool
return $this->displayCitationLinksInResults;
}

/**
* Should we display counts for checkbox facets in results?
*
* @return bool
*/
public function displayCheckboxFacetCounts(): bool
{
return $this->displayCheckboxFacetCounts;
}

/**
* Configure autocomplete preferences from an .ini file.
*
Expand Down
24 changes: 22 additions & 2 deletions module/VuFind/src/VuFind/Search/Solr/Params.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ class Params extends \VuFind\Search\Base\Params
*/
protected $defaultFacetLabelCheckboxSections = ['CheckboxFacets'];

/**
* Virtual field name used for custom filters
*
* @var string
*/
protected $customFilterFieldName;

/**
* Constructor
*
Expand Down Expand Up @@ -164,6 +171,7 @@ public function __construct(
$config->Results_Settings->sorted_by_index->toArray()
);
}
$this->customFilterFieldName = $config->CustomFilters->custom_filter_field ?? 'vufind';
}

/**
Expand Down Expand Up @@ -262,6 +270,19 @@ public function getFacetSettings()
}
}
}

// Add checkbox facets for checkbox counts:
if ($this->checkboxFacets && $this->getOptions()->displayCheckboxFacetCounts()) {
foreach (array_keys($this->checkboxFacets) as $facetField) {
// Ignore custom filters using a virtual field:
if ($facetField === $this->customFilterFieldName) {
continue;
}
$facetField = '{!ex=' . $facetField . '_filter}' . $facetField;
$facetSet['field'][] = $facetField;
}
}

return $facetSet;
}

Expand Down Expand Up @@ -719,7 +740,6 @@ public function getCheckboxFacets(
$facets = parent::getCheckboxFacets($include, $includeDynamic);

$config = $this->configLoader->get($this->getOptions()->getFacetsIni());
$filterField = $config->CustomFilters->custom_filter_field ?? 'vufind';

// Special case -- inverted checkbox facets should always appear, even on
// the "no results" screen, since setting them actually EXPANDS rather than
Expand All @@ -728,7 +748,7 @@ public function getCheckboxFacets(
// Append colon on end to ensure that $customFilter is always set.
[$field, $customFilter] = explode(':', $facet['filter'] . ':');
if (
$field == $filterField
$field === $this->customFilterFieldName
&& isset($config->CustomFilters->inverted_filters[$customFilter])
) {
$facets[$i]['alwaysVisible'] = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ class SearchFacetsTest extends \VuFindTest\Integration\MinkTestCase
/**
* Get filtered search
*
* @param string $building Building filter to use
*
* @return Element
*/
protected function getFilteredSearch(): Element
protected function getFilteredSearch(string $building = 'weird_ids.mrc'): Element
{
$session = $this->getMinkSession();
$session->visit($this->getVuFindUrl() . '/Search/Results?filter%5B%5D=building%3A"weird_ids.mrc"');
$session->visit($this->getVuFindUrl() . '/Search/Results?filter%5B%5D=building%3A"' . $building . '"');
return $session->getPage();
}

Expand Down Expand Up @@ -1146,4 +1148,77 @@ public function testOrFacets(): void
$this->findCssAndGetText($page, '.search-header .search-stats')
);
}

/**
* Data provider for testCheckboxFacets
*
* @return array
*/
public static function checkboxFacetsProvider(): array
{
return [
[false, false],
[false, true],
[true, false],
[true, true],
];
}

/**
* Test checkbox facets
*
* @param bool $deferred Are deferred facets enabled?
* @param bool $counts Are checkbox facet counts enabled?
*
* @dataProvider checkboxFacetsProvider
*
* @return void
*/
public function testCheckboxFacets(bool $deferred, bool $counts): void
{
$this->changeConfigs(
[
'searches' => [
'General' => [
'default_side_recommend[]'
=> ($deferred ? 'SideFacetsDeferred' : 'SideFacets') . ':Results:CheckboxFacets',
],
],
'facets' => [
'Results_Settings' => [
'checkboxFacetCounts' => $counts,
],
'CheckboxFacets' => [
'format:Book' => 'Books',
'illustrated:Illustrated' => 'Illustrated',
],
],
]
);
$page = $this->getFilteredSearch('authoritybibs.mrc');
$this->waitForPageLoad($page);

// format:Book is also a normal facet, but count should still be empty unless enabled:
$filter = $this->findCss($page, '.checkbox-filter');
$this->assertNotNull($filter);
$this->assertEquals('Books', $this->findCssAndGetText($filter->getParent(), '.icon-link__label'));
$this->assertEquals($counts ? '9' : '', $this->findCssAndGetText($filter, '.avail-count'));

// illustrated:Illustrated is only a checkbox facet:
$filter2 = $this->findCss($page, '.checkbox-filter', null, 1);
$this->assertNotNull($filter2);
$this->assertEquals('Illustrated', $this->findCssAndGetText($filter2, '.icon-link__label'));
$illustratedCount = $this->findCssAndGetText($filter2->getParent(), '.avail-count');
$this->assertEquals($counts ? '2' : '', $illustratedCount);

// If we have counts, apply the checkbox facet and check result count:
if ($counts) {
$filter2->click();
$this->waitForPageLoad($page);
$this->assertStringContainsString(
"Showing 1 - $illustratedCount results of $illustratedCount",
$this->findCssAndGetText($page, '.search-header .search-stats')
);
}
}
}
Loading

0 comments on commit e92629f

Please sign in to comment.