Support metarecord indexing and searching
authorBill Erickson <berickxx@gmail.com>
Wed, 25 Sep 2019 18:56:10 +0000 (14:56 -0400)
committerBill Erickson <berickxx@gmail.com>
Mon, 3 Feb 2020 22:13:59 +0000 (17:13 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/catalog/elastic.service.ts
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Elastic.pm
Open-ILS/src/perlmods/lib/OpenILS/Elastic/Bib/Search.pm

index 94e72a9..22cbe7c 100644 (file)
@@ -25,7 +25,6 @@ export class ElasticService {
         if (ctx.marcSearch.isSearchable()) { return true; }
 
         if ( ctx.termSearch.isSearchable() &&
-            !ctx.termSearch.groupByMetarecord &&
             !ctx.termSearch.fromMetarecord &&
             !ctx.termSearch.hasBrowseEntry) {
             return true;
@@ -45,9 +44,11 @@ export class ElasticService {
 
         const requestBody = this.compileRequestBody(ctx);
 
-        const method = ctx.isStaff ?
-            'open-ils.search.elastic.bib_search.staff' :
-            'open-ils.search.elastic.bib_search';
+        let method = ctx.termSearch.isMetarecordSearch() ?
+            'open-ils.search.elastic.bib_search.metabib' :
+            'open-ils.search.elastic.bib_search'
+
+        if (ctx.isStaff) { method += '.staff'; }
 
         // Extract just the bits that get sent to ES.
         const elasticStruct: Object = requestBody.toJSON();
index 75934bf..2fc0c37 100644 (file)
@@ -154,15 +154,23 @@ __PACKAGE__->register_method(
 __PACKAGE__->register_method(
     method   => 'bib_search',
     api_name => 'open-ils.search.elastic.bib_search.staff',
-    signature => {
-        desc => q/
-            Staff version of open-ils.search.elastic.bib_search
+    signature => {desc => q/Staff version of open-ils.search.elastic.bib_search /}
+);
 
-        /
-    }
+__PACKAGE__->register_method(
+    method   => 'bib_search',
+    api_name => 'open-ils.search.elastic.bib_search.metabib',
+    signature => {desc => q/Staff version of open-ils.search.elastic.bib_search /}
 );
 
-# Translate a bib search API call into something consumable by Elasticsearch
+__PACKAGE__->register_method(
+    method   => 'bib_search',
+    api_name => 'open-ils.search.elastic.bib_search.metabib.staff',
+    signature => {desc => q/Staff version of open-ils.search.elastic.bib_search /}
+);
+
+
+# Augment and relay an Elastic query to the Elasticsearch backend.
 # Translate search results into a structure consistent with a bib search
 # API response.
 sub bib_search {
@@ -172,15 +180,28 @@ sub bib_search {
     init();
 
     my $staff = ($self->api_name =~ /staff/);
+    my $meta = ($self->api_name =~ /metabib/);
 
     return {count => 0, ids => []} unless $query && $query->{query};
 
     # Only ask ES to return the 'id' field from the source bibs in
     # the response object, since that's all we need.
-    $query->{_source} = ['id'];
+    $query->{_source} = [$meta ? 'metarecord' : 'id'];
 
     my $elastic_query = compile_elastic_query($query, $options, $staff);
 
+    my $from = $elastic_query->{from} || 0;
+    my $size = $elastic_query->{size} || 20;
+
+    if ($meta) {
+        $elastic_query->{collapse} = {field => 'metarecord'};
+        # ES field collapse queries return counts for matched documents
+        # instead of matched groups.  To determine the metarecord hit
+        # count (up to 1k), fetch up to 1k responses and count them.
+        $elastic_query->{from} = 0;
+        $elastic_query->{size} = 1000;
+    }
+
     my $es = OpenILS::Elastic::BibSearch->new('main');
 
     $es->connect;
@@ -198,12 +219,26 @@ sub bib_search {
     # This is not guaranteed to be 1-to-1 given key shuffling, but meh.
     my $cache_key = md5_hex(OpenSRF::Utils::JSON->perl2JSON($elastic_query));
 
+    my $hits = $results->{hits}->{hits};
+    if ($meta) {
+        # count the number of groups represented in the result set.
+        $results->{hits}->{total} = scalar(@$hits);
+
+        # Only return the requested window of hits to the caller.
+        $hits = [ grep {defined $_} @$hits[$from .. ($from + $size)] ];
+    }
+
+    my $ids = [
+        map {[
+            $meta ? $_->{_source}->{'metarecord'} : $_->{_id}, 
+            undef, 
+            $_->{_score}
+        ]} @$hits
+    ];
+
     return {
+        ids => $ids,
         count => $results->{hits}->{total},
-        ids => [
-            map { [$_->{_id}, undef, $_->{_score}] } 
-                grep {defined $_} @{$results->{hits}->{hits}}
-        ],
         facets => format_facets($results->{aggregations}),
         cache_key => $cache_key,
         facet_key => $cache_key.'_facets'
index b328be0..8e19383 100644 (file)
@@ -52,6 +52,7 @@ my $BASE_PROPERTIES = {
     bib_source  => {type => 'integer'},
     create_date => {type => 'date'},
     edit_date   => {type => 'date'},
+    metarecord  => {type => 'integer'},
 
     # Holdings summaries.  For bib-search, we don't need
     # copy-specific details, only aggregate visibility information.
@@ -322,9 +323,11 @@ SELECT
     bre.edit_date, 
     bre.source AS bib_source,
     bre.deleted,
+    mmrsm.metarecord,
     (elastic.bib_record_properties(bre.id)).*
 FROM biblio.record_entry bre
-WHERE id IN ($ids_str)
+LEFT JOIN metabib.metarecord_source_map mmrsm ON (mmrsm.source = bre.id)
+WHERE bre.id IN ($ids_str)
 SQL
 
     return $self->get_db_rows($sql);
@@ -381,6 +384,7 @@ sub populate_bib_index_batch {
                 # some values are repeated per field. 
                 # extract them from the first entry.
                 $body->{bib_source} = $field->{bib_source};
+                $body->{metarecord} = $field->{metarecord};
 
                 # ES ikes the "T" separator for ISO dates
                 ($body->{create_date} = $field->{create_date}) =~ s/ /T/g;