From 840b742181e7c680a0204ea437eb3d89badf321a Mon Sep 17 00:00:00 2001 From: Ere Maijala Date: Thu, 24 Oct 2019 13:33:15 +0300 Subject: [PATCH] Bug 23887: Add support for sorting in Z3950Responder. Also enables sorting by any field that supports sorting and no longer ignores invalid or unsupported sorts. Test plan: 1. Run tests: prove -v t/db_dependent/Koha/Z3950Responder/ 2. Start responder: misc/z3950responder.pl 3. Use browser to search: http://localhost:2100/biblios?version=1.2&operation=searchRetrieve&query=misth%20sortby%20title/ascending&maximumRecords=10&recordSchema=marcxml 4. Compare with: http://localhost:2100/biblios?version=1.2&operation=searchRetrieve&query=misth%20sortby%20title/descending&maximumRecords=10&recordSchema=marcxml --- .../Elasticsearch/QueryBuilder.pm | 4 +-- Koha/Z3950Responder/GenericSession.pm | 30 ++++++++++++++-- .../Koha/Z3950Responder/GenericSession.t | 36 ++++++++++++++++--- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm index 7addc5313f..77ee0c6d58 100644 --- a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm +++ b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm @@ -696,7 +696,7 @@ sub _convert_sort_fields { author => 'author', call_number => 'cn-sort', popularity => 'issues', - relevance => undef, # default + relevance => '', # default title => 'title', pubdate => 'date-of-publication', ); @@ -707,7 +707,7 @@ sub _convert_sort_fields { grep { $_->{field} } map { my ( $f, $d ) = /(.+)_(.+)/; { - field => $sort_field_convert{$f}, + field => $sort_field_convert{$f} // $f, direction => $sort_order_convert{$d} } } @sort_by; diff --git a/Koha/Z3950Responder/GenericSession.pm b/Koha/Z3950Responder/GenericSession.pm index a4552dfe5d..41eadf6148 100644 --- a/Koha/Z3950Responder/GenericSession.pm +++ b/Koha/Z3950Responder/GenericSession.pm @@ -1,6 +1,6 @@ package Koha::Z3950Responder::GenericSession; -# Copyright The National Library of Finland 2018 +# Copyright The National Library of Finland 2018-2019 # # This file is part of Koha. # @@ -61,7 +61,9 @@ sub start_search { my $query = $args->{RPN}->{'query'}->to_koha($self->{'attribute_mappings'}->{$database}); $self->log_debug(" parsed search: $query"); my @operands = $query; - (undef, $built_query) = $builder->build_query_compat( undef, \@operands, undef, undef, undef, 0); + my $sort = $self->_convert_sort_rules($args->{SRW_SORTKEYS} // ''); + + (undef, $built_query) = $builder->build_query_compat( undef, \@operands, undef, undef, $sort, 0); my ($error, $marcresults, $hits ) = $searcher->simple_search_compat($built_query, 0, $num_to_prefetch); if (defined $error) { @@ -108,4 +110,28 @@ sub fetch_record { return $resultset->{cached_results}[$offset - $resultset->{cached_offset}]; } +=head2 Internal methods + +=head3 _convert_sort_rules + + my $sort = $self->_convert_sort_rules($srw_sortkeys); + +Convert SRW_SORTKEYS from SimpleServer to Koha's format + +=cut + +sub _convert_sort_rules { + my ( $self, $srw_sortkeys ) = @_; + + my @result; + my @sortkeys = split(' ', $srw_sortkeys); + foreach my $sortkey (@sortkeys) { + my @parts = split(',', $sortkey); + my $field = $parts[0]; + my $direction = $parts[2] // 1 == 1 ? 'asc' : 'desc'; + push @result, $field . '_' . $direction; + } + return \@result; +} + 1; diff --git a/t/db_dependent/Koha/Z3950Responder/GenericSession.t b/t/db_dependent/Koha/Z3950Responder/GenericSession.t index 74d0e9fd92..51814aa17a 100755 --- a/t/db_dependent/Koha/Z3950Responder/GenericSession.t +++ b/t/db_dependent/Koha/Z3950Responder/GenericSession.t @@ -23,7 +23,7 @@ our $child; subtest 'test_search' => sub { - plan tests => 20; + plan tests => 27; t::lib::Mocks::mock_preference('SearchEngine', 'Elasticsearch'); @@ -66,18 +66,29 @@ subtest 'test_search' => sub { my $builder = Test::MockModule->new('Koha::SearchEngine::Elasticsearch::QueryBuilder'); $builder->mock('build_query_compat', sub { - my ( $self, $operators, $operands ) = @_; + my ( $self, $operators, $operands, undef, undef, $sort_by ) = @_; - return (undef, $operands->[0]); + my $query = $operands->[0]; + if (scalar(@{$sort_by})) { + $query .= ' sort ' . join(',', @{$sort_by}); + } + return (undef, $query); }); my $search = Test::MockModule->new('Koha::SearchEngine::Elasticsearch::Search'); $search->mock('simple_search_compat', sub { my ( $self, $query ) = @_; - return ('unexpected query', undef, 0) unless $query eq '((author:(author)) AND ((title:(title\(s\))) OR (title:(speciäl))))' || $query eq "(simple search)"; + return ('unexpected query', undef, 0) unless $query eq '((author:(author)) AND ((title:(title\(s\))) OR (title:(speciäl))))' + || $query eq '((author:(author)) AND ((title:(title\(s\))) OR (title:(speciäl)))) sort title_desc' + || $query eq "(simple search)"; - my @records = ($marc_record_1, $marc_record_2); + my @records; + if ($query =~ / sort title_desc/) { + @records = ($marc_record_2, $marc_record_1); + } else { + @records = ($marc_record_1, $marc_record_2); + } return (undef, \@records, 2); }); @@ -151,6 +162,21 @@ subtest 'test_search' => sub { ok($returned2, 'Record 2 returned as MARCXML'); is($returned2->as_xml, $marc_record_2->as_xml, 'Record 2 returned properly'); + $agent->get_ok("$base/biblios?operation=searchRetrieve&recordSchema=marcxml&version=1.1&maximumRecords=10&query=(dc.author%3dauthor AND (dc.title%3d\"title(s)\" OR dc.title%3dspeciäl)) sortby title%2Fdescending", 'Retrieve search results'); + $dom = XML::LibXML->load_xml(string => $agent->content()); + @nodes = $dom->getElementsByTagNameNS($ns, 'searchRetrieveResponse'); + is(scalar(@nodes), 1, 'sorted searchRetrieveResponse returned'); + @records = $nodes[0]->getElementsByTagNameNS($marc_ns, 'record'); + is(scalar(@records), 2, 'Two results returned'); + + $returned1 = MARC::Record->new_from_xml($records[0]->toString(), 'UTF-8'); + ok($returned1, 'First record returned as MARCXML'); + is($returned1->as_xml, $marc_record_2->as_xml, 'Record 2 returned first'); + + $returned2= MARC::Record->new_from_xml($records[1]->toString(), 'UTF-8'); + ok($returned2, 'Second record returned as MARCXML'); + is($returned2->as_xml, $marc_record_1->as_xml, 'Record 1 returned second'); + cleanup(); }; -- 2.25.1