Bug 17170: Add the ability to create 'saved searches' for use as filters when searching the catalog

This patch alters the code in the ES QueryBuilder. Reflecting
how things are handled in build_query_compat we clean the query,
but not the limits. In Zebra we simply recursivly call buildQuery,
but the ES query structure

This patchset adds a new ability to save searches on the staff client, and display them in the results
page on staff or opac as a new filter.

New filters can be added from the results page after a search, and there is an admin page for updating
deleting and renaming filters

There is a new permission to control management of these filters

New filters can be added that are not displayed along with facets, this allows for building custom links
using these filters to keep URLs shorter

Due to bug 30528 testing in ES is recommended

To test:
 1 - Apply patches and update database and restart all
 2 - Enable new system preference 'SavedSearchFilters'
 3 - As superlibrarian perform a search in staff client, something broad like 'a'
 4 - Note new 'Save search as filter' link on results page
 5 - Click it, save search as new filter, check 'Staff client' visibility
 6 - Perform another search
 7 - Note the filter now appears above facets
 8 - Click to it filter results
 9 - Note results are limited by the new filter, and it is checked in the facets
10 - Confirm click the [x] removes the filter
11 - Go to administration->search filters
12 - Confirm the filter appears
13 - Edit and mark as OPAC visible
14 - Test OPAC to ensure it shows and can be applied/removed
15 - Copy URL with filter applied
16 - In adminsitration mark filter as not visible on staff or opac
17 - Confirm link above still works
18 - Create a new staff with catalogue and search filters permission
19 - Ensure they can access/save filters
20 - Remove filter permission and ensure they cannot
21 - Disable system preference
22 - Confirm links to search filters page are removed from admin home and admin sidebar
23 - Confirm filters do not appear on results and cannot be created
24 - Enable pref
25 - Create a filter
26 - From search filters page, click 'Edit search'
27 - Confirm you are taken to advanced search page letting you know which filter you are editing
28 - Confirm you can change searhc options and save
29 - Confirm you can perform the search from this page

Sponsored-by: Sponsored by: Round Rock Public Library [https://www.roundrocktexas.gov/departments/library/]

Signed-off-by: Michal Urban <michalurban177@gmail.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Nick Clemens 2022-04-18 14:51:36 +00:00 committed by Tomas Cohen Arazi
parent 2fa8c5e297
commit f5271d1a74
No known key found for this signature in database
GPG Key ID: 0A272EA1B2F3C15F
5 changed files with 51 additions and 19 deletions

View File

@ -1301,12 +1301,15 @@ sub buildQuery {
$limit .= "$this_limit";
$limit_desc .= " $this_limit";
} elsif ( $this_limit =~ '^search_filter:' ) {
# Here we will get the query as a string, append to the limits, and pass through buildQuery
# again to clean the terms and handle nested filters
$limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
my ($filter_id) = ( $this_limit =~ /^search_filter:(.*)$/ );
my $search_filter = Koha::SearchFilters->find( $filter_id );
next unless $search_filter;
my $expanded = $search_filter->expand_filter;
my ( $error, undef, undef, undef, undef, $fixed_limit, undef, undef, undef ) = buildQuery ( undef, undef, undef, $expanded, undef, undef, $lang);
my ($expanded_lim, $query_lim) = $search_filter->expand_filter;
push @$expanded_lim, $query_lim;
my ( $error, undef, undef, undef, undef, $fixed_limit, undef, undef, undef ) = buildQuery ( undef, undef, undef, $expanded_lim, undef, undef, $lang);
$limit .= " and " if $limit || $query;
$limit .= "$fixed_limit";
$limit_desc .= " $limit";

View File

@ -1070,13 +1070,18 @@ sub _fix_limit_special_cases {
push @new_lim, "date-of-publication:[$start TO $end]";
}
elsif( $l =~ /^search_filter:/ ){
# Here we are going to get the query as a string, clean it, and take care of the part of the limit
# Calling build_query_compat here is avoided because we generate more complex query structures
my ($filter_id) = ( $l =~ /^search_filter:(.*)$/ );
my $search_filter = Koha::SearchFilters->find( $filter_id );
next unless $search_filter;
my $expanded = $search_filter->expand_filter;
foreach my $e ( @{$self->_fix_limit_special_cases($expanded)} ) {
push @new_lim, $self->clean_search_term( $e );
my ($expanded_lim,$query_lim) = $search_filter->expand_filter;
# In the case of nested filters we need to expand them all
foreach my $el ( @{$self->_fix_limit_special_cases($expanded_lim)} ){
push @new_lim, $el;
}
# We need to clean the query part as we have built a string from the original search
push @new_lim, $self->clean_search_term( $query_lim );
}
elsif ( $l =~ /^yr,st-numeric[=:]/ ) {
my ($date) = ( $l =~ /^yr,st-numeric[=:](.*)$/ );

View File

@ -43,10 +43,10 @@ sub to_api_mapping {
=head3 expand_filter
my $expanded_filter = $filter->expand_filter;
my ($expanded_limit, $query_limit) = $filter->expand_filter;
Returns the filter as an arrayref of limit queries suitable to
be passed to QueryBuilder
Returns the filter as an arrayref of limit queries, and the query parts combined
into a string suitable to be passed to QueryBuilder
=cut
@ -74,9 +74,7 @@ sub expand_filter {
$query_limit .= $limit;
}
push @$limits, "(".$query_limit.")" if $query_limit;
return $limits;
return ($limits, $query_limit);
}
=head2 Internal methods

View File

@ -785,7 +785,7 @@ subtest 'build_query_compat() SearchLimitLibrary tests' => sub {
};
subtest "Handle search filters" => sub {
plan tests => 4;
plan tests => 7;
my $qb;
@ -797,15 +797,28 @@ subtest "Handle search filters" => sub {
my $filter = Koha::SearchFilter->new({
name => "test",
query => q|{"operands":["cat","bat","rat"],"indexes":["kw","ti","au"],"operators":["AND","OR"]}|,
limits => q|{"limits":["mc-itype,phr:BK","available"]}|,
limits => q|{"limits":["mc-itype,phr:BK","mc-itype,phr:MU","available"]}|,
})->store;
my $filter_id = $filter->id;
my ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc ) = $qb->build_query_compat( undef, undef, undef, ["search_filter:$filter_id"] );
is( $limit,q{(itype:("BK")) AND (onloan:false) AND (((cat) AND title:(bat) OR author:(rat)))},"Limit correctly formed");
is( $limit,q{(onloan:false) AND ((cat) AND title:(bat) OR author:(rat)) AND itype:(("BK") OR ("MU"))},"Limit correctly formed");
is( $limit_cgi,"&limit=search_filter%3A$filter_id","CGI limit is not expanded");
is( $limit_desc,q{(itype:("BK")) AND (onloan:false) AND (((cat) AND title:(bat) OR author:(rat)))},"Limit description is correctly expanded");
is( $limit_desc,q{(onloan:false) AND ((cat) AND title:(bat) OR author:(rat)) AND itype:(("BK") OR ("MU"))},"Limit description is correctly expanded");
$filter = Koha::SearchFilter->new({
name => "test",
query => q|{"operands":["su:biography"],"indexes":[],"operators":[]}|,
limits => q|{"limits":[]}|,
})->store;
$filter_id = $filter->id;
( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc ) = $qb->build_query_compat( undef, undef, undef, ["search_filter:$filter_id"] );
is( $limit,q{(subject:biography)},"Limit correctly formed for ccl type query");
is( $limit_cgi,"&limit=search_filter%3A$filter_id","CGI limit is not expanded");
is( $limit_desc,q{(subject:biography)},"Limit description is correctly handled for ccl type query");
};

View File

@ -98,7 +98,7 @@ subtest 'build_query_compat() SearchLimitLibrary tests' => sub {
};
subtest "Handle search filters" => sub {
plan tests => 4;
plan tests => 7;
my $qb;
@ -110,14 +110,27 @@ subtest "Handle search filters" => sub {
my $filter = Koha::SearchFilter->new({
name => "test",
query => q|{"operands":["cat","bat","rat"],"indexes":["kw","ti","au"],"operators":["AND","OR"]}|,
limits => q|{"limits":["mc-itype,phr:BK","available"]}|,
limits => q|{"limits":["mc-itype,phr:BK","mc-itype,phr:MU","available"]}|,
})->store;
my $filter_id = $filter->id;
my ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc ) = $qb->build_query_compat( undef, undef, undef, ["search_filter:$filter_id"] );
is( $limit,q{(kw=(cat) AND ti=(bat) OR au=(rat)) and (mc-itype,phr=BK) and (( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) ))},"Limit correctly formed");
is( $limit,q{kw=(cat) AND ti=(bat) OR au=(rat) and (mc-itype,phr=BK or mc-itype,phr=MU) and (( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) ))},"Limit correctly formed");
is( $limit_cgi,"&limit=search_filter%3A$filter_id","CGI limit is not expanded");
is( $limit_desc,q{(kw=(cat) AND ti=(bat) OR au=(rat)) and (mc-itype,phr=BK) and (( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) ))},"Limit description is correctly expanded");
is( $limit_desc,q{kw=(cat) AND ti=(bat) OR au=(rat) and (mc-itype,phr=BK or mc-itype,phr=MU) and (( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) ))},"Limit description is correctly expanded");
$filter = Koha::SearchFilter->new({
name => "test",
query => q|{"operands":["su:biography"],"indexes":[],"operators":[]}|,
limits => q|{"limits":[]}|,
})->store;
$filter_id = $filter->id;
( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc ) = $qb->build_query_compat( undef, undef, undef, ["search_filter:$filter_id"] );
is( $limit,q{(su=biography)},"Limit correctly formed for ccl type query");
is( $limit_cgi,"&limit=search_filter%3A$filter_id","CGI limit is not expanded");
is( $limit_desc,q{(su=biography)},"Limit description is correctly handled for ccl type query");
};