From e02f34dbe844d1cd2193486593f832af3193ecd7 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Fri, 17 Mar 2017 17:46:59 -0400 Subject: [PATCH] LP#1673857: add search filter for copy_tags Copy tags can be used as a search filter in the catalog. Two variations are supported: * copy_tag(type_code, search_terms) Search for records that have copies that are linked to tags whose value matches the search terms and whose type's config.copy_tag_type.code matches the specified type_code. E.g., "copy_tag(bookplate, donated by jane smith)" * copy_tag(*, search_terms) Search for records that have copies that are linked to tags whose value matches the search terms, regardless of type. The copy_tag() search filter takes the OPAC-visiblity (as determined by asset.copy_tag.pub) of the tag into account. Mike Rylander also contributed to this patch. Signed-off-by: Galen Charlton Signed-off-by: Josh Stompro Signed-off-by: Galen Charlton --- .../Storage/Driver/Pg/QueryParser.pm | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm index 0f7c1f0aef..babaa46b29 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm @@ -688,6 +688,9 @@ __PACKAGE__->add_search_filter( 'record_list' ); __PACKAGE__->add_search_filter( 'has_browse_entry' ); +# copy_tag(copy_tag_code,copy_tag_search) +__PACKAGE__->add_search_filter( 'copy_tag' ); + # used internally, but generally not user-settable __PACKAGE__->add_search_filter( 'preferred_language' ); __PACKAGE__->add_search_filter( 'preferred_language_weight' ); @@ -713,6 +716,7 @@ use OpenSRF::Utils::Logger qw($logger); use OpenSRF::Utils qw/:datetime/; use Data::Dumper; use OpenILS::Application::AppUtils; +use OpenILS::Utils::Normalize qw/search_normalize/; my $apputils = "OpenILS::Application::AppUtils"; our %_dfilter_controlled_cache = (); @@ -1300,6 +1304,51 @@ sub flatten { } } } + } elsif ($filter->name eq 'copy_tag') { + my $valid_copy_tag_search = 0; + my $copy_tag_type; + my $tag_value; + if (@{$filter->args} >= 2) { # must have at least two parts, tag (or *) and terms + my @fargs = @{$filter->args}; + $copy_tag_type = shift(@fargs); + $tag_value = join(' ', @fargs); + $valid_copy_tag_search = 1; + } + if ($valid_copy_tag_search) { + my $norm_value = search_normalize($tag_value); + my @tokens = split /\s+/, $norm_value; + + my $filter_alias = "$filter"; + $filter_alias =~ s/^.*\(0(x[0-9a-fA-F]+)\)$/$1/go; + $filter_alias =~ s/\|/_/go; + + $with .= ",\n " if $with; + $with .= "copy_tag_${filter_alias} AS (\n"; + $with .= " SELECT cn.record AS record FROM config.copy_tag_type cctt\n"; + $with .= " JOIN asset.copy_tag acpt ON (cctt.code = acpt.tag_type)\n"; + $with .= " JOIN asset.copy_tag_copy_map acptcm ON (acpt.id = acptcm.tag)\n"; + $with .= " JOIN asset.copy cp ON (acptcm.copy = cp.id)\n"; + $with .= " JOIN asset.call_number cn ON (cp.call_number = cn.id)\n"; + $with .= " WHERE 1 = 1 \n"; + if ($copy_tag_type ne '*') { + $with .= " AND cctt.code = " . $self->QueryParser->quote_value($copy_tag_type) . "\n"; + } + if (@tokens) { + $with .= ' AND acpt.value @@ to_tsquery(' . $self->QueryParser->quote_value(join(' & ', @tokens)) . ")\n"; + } + if (!$self->find_modifier('staff')) { + $with .= " AND acpt.pub IS TRUE\n"; + } + $with .= " )"; + + my $optimize_join = 1 if $self->top_plan and !$NOT; + $from .= "\n" . ${spc} x 3 . ( $optimize_join ? 'INNER' : 'LEFT') . " JOIN copy_tag_${filter_alias} ON copy_tag_${filter_alias}.record = m.source"; + + if (!$optimize_join) { + $where .= $joiner if $where ne ''; + $where .= "(copy_tag_${filter_alias} IS " . ( $NOT ? 'NULL)' : 'NOT NULL)'); + } + } } elsif ($filter->name eq 'record_list') { if (@{$filter->args} > 0) { my $key = 'm.source'; -- 2.43.2