Bug 18235: ES - Facets configurable
authorJonathan Druart <jonathan.druart@bugs.koha-community.org>
Wed, 8 Mar 2017 15:04:46 +0000 (12:04 -0300)
committerNick Clemens <nick@bywatersolutions.com>
Thu, 28 Mar 2019 13:48:53 +0000 (13:48 +0000)
This patch adds a new section 'Facet order' in the Biblio tab of the
'Search engine configuration' admin page of the Elastic mappings.
The idea is to let the librarians define the facet to display and order
them as their needs.

The ergonomic is not perfect and I am open to any suggestions.

Test plan:
Move up and down the field list to order the facets
Hide/show some facets
Rebuild index
At the OPAC and the staff interface you should see the changes on the
search result page.

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

Signed-off-by: Nicolas Legrand <nicolas.legrand@bulac.fr>

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

Koha/SearchEngine/Elasticsearch.pm
Koha/SearchEngine/Elasticsearch/Search.pm
admin/searchengine/elasticsearch/mappings.pl
admin/searchengine/elasticsearch/mappings.yaml
koha-tmpl/intranet-tmpl/prog/en/modules/admin/searchengine/elasticsearch/mappings.tt

index 9a9e50b..9c9947a 100644 (file)
@@ -284,7 +284,15 @@ sub reset_elasticsearch_mappings {
             my $field_type = $data->{type};
             my $field_label = $data->{label};
             my $mappings = $data->{mappings};
-            my $search_field = Koha::SearchFields->find_or_create({ name => $field_name, label => $field_label, type => $field_type }, { key => 'name' });
+            my $facet_order = $data->{facet_order};
+            my $search_field = Koha::SearchFields->find_or_create({ name => $field_name });
+            $search_field->update(
+                {
+                    label       => $field_label,
+                    type        => $field_type,
+                    facet_order => $facet_order
+                }
+            );
             for my $mapping ( @$mappings ) {
                 my $marc_field = Koha::SearchMarcMaps->find_or_create({ index_name => $index_name, marc_type => $mapping->{marc_type}, marc_field => $mapping->{marc_field} });
                 $search_field->add_to_search_marc_maps($marc_field, { facet => $mapping->{facet} || 0, suggestible => $mapping->{suggestible} || 0, sort => $mapping->{sort} } );
@@ -964,6 +972,22 @@ sub _read_configuration {
     return $configuration;
 }
 
+sub get_facetable_fields {
+    my ($self) = @_;
+
+    # These should correspond to the ES field names, as opposed to the CCL
+    # things that zebra uses.
+    my @search_field_names = qw( author itype location su-geo se subject ccode holdingbranch homebranch );
+    my @faceted_fields = Koha::SearchFields->search(
+        { name => { -in => \@search_field_names }, facet_order => { '!=' => undef } }, { order_by => ['facet_order'] }
+    );
+    my @not_faceted_fields = Koha::SearchFields->search(
+        { name => { -in => \@search_field_names }, facet_order => undef }, { order_by => ['facet_order'] }
+    );
+    # This could certainly be improved
+    return ( @faceted_fields, @not_faceted_fields );
+}
+
 1;
 
 __END__
index 3436174..f30c139 100644 (file)
@@ -436,18 +436,25 @@ sub _convert_facets {
 
     # These should correspond to the ES field names, as opposed to the CCL
     # things that zebra uses.
-    # TODO let the library define the order using the interface.
-    my %type_to_label = (
-        author => { order => 1, label => 'Authors', },
-        itype => { order => 2, label => 'ItemTypes', },
-        location => { order => 3, label => 'Location', },
-        'su-geo' => { order => 4, label => 'Places', },
-        'title-series' => { order => 5, label => 'Series', },
-        subject => { order => 6, label => 'Topics', },
-        ccode => { order => 7, label => 'CollectionCodes',},
-        holdingbranch => { order => 8, label => 'HoldingLibrary' },
-        homebranch => { order => 9, label => 'HomeLibrary' }
+    my %type_to_label;
+    my %label = (
+        author         => 'Authors',
+        itype          => 'ItemTypes',
+        location       => 'Location',
+        'su-geo'       => 'Places',
+        'title-series' => 'Series',
+        subject        => 'Topics',
+        ccode          => 'CollectionCodes',
+        holdingbranch  => 'HoldingLibrary',
+        homebranch     => 'HomeLibrary',
     );
+    my @facetable_fields =
+      Koha::SearchEngine::Elasticsearch->get_facetable_fields;
+    for my $f (@facetable_fields) {
+        next unless defined $f->facet_order;
+        $type_to_label{ $f->name } =
+          { order => $f->facet_order, label => $label{ $f->name } };
+    }
 
     # We also have some special cases, e.g. itypes that need to show the
     # value rather than the code.
@@ -505,5 +512,4 @@ sub _convert_facets {
     return \@facets;
 }
 
-
 1;
index 2c64db0..de891ac 100755 (executable)
@@ -18,6 +18,7 @@
 use Modern::Perl;
 use CGI;
 use Scalar::Util qw(looks_like_number);
+use List::Util qw( first );
 use C4::Koha;
 use C4::Output;
 use C4::Auth;
@@ -71,17 +72,18 @@ if ( $op eq 'edit' ) {
 
     $schema->storage->txn_begin;
 
-    my @field_name = $input->param('search_field_name');
-    my @field_label = $input->param('search_field_label');
-    my @field_type = $input->param('search_field_type');
-    my @field_weight = $input->param('search_field_weight');
+    my @field_name = $input->multi_param('search_field_name');
+    my @field_label = $input->multi_param('search_field_label');
+    my @field_type = $input->multi_param('search_field_type');
+    my @field_weight = $input->multi_param('search_field_weight');
 
-    my @index_name          = $input->param('mapping_index_name');
-    my @search_field_name  = $input->param('mapping_search_field_name');
-    my @mapping_sort        = $input->param('mapping_sort');
-    my @mapping_facet       = $input->param('mapping_facet');
-    my @mapping_suggestible = $input->param('mapping_suggestible');
-    my @mapping_marc_field  = $input->param('mapping_marc_field');
+    my @index_name          = $input->multi_param('mapping_index_name');
+    my @search_field_name  = $input->multi_param('mapping_search_field_name');
+    my @mapping_sort        = $input->multi_param('mapping_sort');
+    my @mapping_facet       = $input->multi_param('mapping_facet');
+    my @mapping_suggestible = $input->multi_param('mapping_suggestible');
+    my @mapping_marc_field  = $input->multi_param('mapping_marc_field');
+    my @faceted_field_names = $input->multi_param('display_facet');
 
     eval {
 
@@ -105,10 +107,14 @@ if ( $op eq 'edit' ) {
                 $search_field->weight($field_weight);
             }
 
+            my $facet_order = first { $faceted_field_names[$_] eq $field_name } 0 .. $#faceted_field_names;
+            $search_field->facet_order(defined $facet_order ? $facet_order + 1 : undef);
             $search_field->store;
         }
 
         Koha::SearchMarcMaps->search( { marc_type => $marc_type, } )->delete;
+        my @facetable_fields = Koha::SearchEngine::Elasticsearch->get_facetable_fields();
+        my @facetable_field_names = map { $_->name } @facetable_fields;
 
         for my $i ( 0 .. scalar(@index_name) - 1 ) {
             my $index_name          = $index_name[$i];
@@ -118,6 +124,7 @@ if ( $op eq 'edit' ) {
             my $mapping_suggestible = $mapping_suggestible[$i];
             my $mapping_sort        = $mapping_sort[$i];
             $mapping_sort = undef if $mapping_sort eq 'undef';
+            $mapping_facet = ( grep {/^$search_field_name$/} @facetable_field_names ) ? $mapping_facet : 0;
 
             my $search_field = Koha::SearchFields->find({ name => $search_field_name }, { key => 'name' });
             # TODO Check mapping format
@@ -180,15 +187,20 @@ for my $index_name (@index_names) {
     );
 
     my @mappings;
+    my @facetable_fields = Koha::SearchEngine::Elasticsearch->get_facetable_fields();
+    my @facetable_field_names = map { $_->name } @facetable_fields;
+
     while ( my $s = $search_fields->next ) {
+        my $name = $s->name;
         push @mappings,
-          { search_field_name  => $s->name,
+          { search_field_name  => $name,
             search_field_label => $s->label,
             search_field_type  => $s->type,
             marc_field         => $s->get_column('marc_field'),
             sort               => $s->get_column('sort') // 'undef', # To avoid warnings "Use of uninitialized value in lc"
             suggestible        => $s->get_column('suggestible'),
             facet              => $s->get_column('facet'),
+            is_facetable       => ( grep {/^$name$/} @facetable_field_names ) ? 1 : 0,
           };
     }
 
@@ -203,9 +215,11 @@ while ( my $search_field = $search_fields->next ) {
     push @all_search_fields, $search_field_unblessed;
 }
 
+my @facetable_fields = Koha::SearchEngine::Elasticsearch->get_facetable_fields();
 $template->param(
     indexes           => \@indexes,
     all_search_fields => \@all_search_fields,
+    facetable_fields  => \@facetable_fields,
     messages          => \@messages,
 );
 
index 5cb2c18..ebd8dba 100644 (file)
@@ -1265,6 +1265,7 @@ biblios:
         sort: ~
         suggestible: 0
     type: string
+    facet_order: 1
   author-in-order:
     label: author-in-order
     mappings:
@@ -1493,6 +1494,7 @@ biblios:
         sort: ~
         suggestible: ''
     type: ''
+    facet_order: 7
   classification-source:
     label: classification-source
     mappings:
@@ -1980,6 +1982,7 @@ biblios:
         sort: 0
         suggestible: ''
     type: string
+    facet_order: 8
   homebranch:
     label: homelibrary
     mappings:
@@ -1999,6 +2002,7 @@ biblios:
         sort: 0
         suggestible: ''
     type: string
+    facet_order: 9
   host-item:
     label: host-item
     mappings:
@@ -2273,6 +2277,7 @@ biblios:
         sort: ~
         suggestible: ''
     type: string
+    facet_order: 2
   koha-auth-number:
     label: koha-auth-number
     mappings:
@@ -2690,6 +2695,7 @@ biblios:
         sort: 0
         suggestible: ''
     type: ''
+    facet_order: 3
   lost:
     label: lost
     mappings:
@@ -3643,6 +3649,7 @@ biblios:
         sort: ~
         suggestible: '1'
     type: string
+    facet_order: 6
   subject-name-personal:
     label: subject-name-personal
     mappings:
@@ -4324,6 +4331,7 @@ biblios:
         sort: ~
         suggestible: ''
     type: string
+    facet_order: 5
   title-uniform:
     label: title-uniform
     mappings:
index db27fce..acd675c 100644 (file)
@@ -53,6 +53,9 @@
                 } );
             }
         });
+        $("#facet_biblios > table").tableDnD( {
+            onDragClass: "dragClass",
+        } );
     });
 </script>
 <style>
@@ -260,15 +263,20 @@ a.add, a.delete {
                               </select>
                             </td>
                             <td>
-                              <select name="mapping_facet">
-                                [% IF mapping.facet %]
-                                  <option value="0">No</option>
-                                  <option value="1" selected="selected">Yes</option>
-                                [% ELSE %]
-                                  <option value="0" selected="selected">No</option>
-                                  <option value="1">Yes</option>
-                                [% END %]
-                              </select>
+                              [% IF mapping.is_facetable %]
+                                <select name="mapping_facet">
+                                  [% IF mapping.facet %]
+                                    <option value="0">No</option>
+                                    <option value="1" selected="selected">Yes</option>
+                                  [% ELSE %]
+                                    <option value="0" selected="selected">No</option>
+                                    <option value="1">Yes</option>
+                                  [% END %]
+                                </select>
+                              [% ELSE %]
+                                <input type="hidden" name="mapping_facet" value="0" />
+                                No
+                              [% END %]
                             </td>
                             <td>
                               <select name="mapping_suggestible">
@@ -332,6 +340,51 @@ a.add, a.delete {
                         </tr>
                       </tfoot>
                     </table>
+
+                    [% IF index.index_name == 'biblios' %]
+                        <h3>Facet order</h3>
+                        <div id="facet_[% index.index_name %]">
+                            <table>
+                                <thead>
+                                    <tr>
+                                        <th>Search field</th>
+                                        <th>Label</th>
+                                        <th>Display</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    [% FOREACH f IN facetable_fields %]
+                                        <tr>
+                                            <td>
+                                                [% f.name %]
+                                            </td>
+                                            <td>
+                                                [% SWITCH f.name %]
+                                                [% CASE 'author' %]Authors
+                                                [% CASE 'itype' %]Item Types
+                                                [% CASE 'location' %]Locations
+                                                [% CASE 'su-geo' %]Places
+                                                [% CASE 'se' %]Series
+                                                [% CASE 'subject' %]Topics
+                                                [% CASE 'ccode' %]Collections
+                                                [% CASE 'holdingbranch' %]Holding libraries
+                                                [% CASE 'homebranch' %]Home libraries
+                                                [% CASE %][% f %]
+                                                [% END %]
+                                            </td>
+                                            <td>
+                                                [% IF f.facet_order %]
+                                                    <input type="checkbox" name="display_facet" value="[% f.name %]" checked="checked" />
+                                                [% ELSE %]
+                                                    <input type="checkbox" name="display_facet" value="[% f.name %]" />
+                                                [% END %]
+                                            </td>
+                                        </tr>
+                                    [% END %]
+                                </tbody>
+                            </table>
+                        </div>
+                    [% END %]
                 </div>
             [% END %]
         </div>