export CPAN_MODULES = \
Business::OnlinePayment::PayPal \
+ Text::Unaccent::PurePerl \
Email::Send
export CPAN_MODULES_FORCE = \
export CPAN_MODULES = \
Excel::Writer::XLSX \
+ Text::Unaccent::PurePerl \
Business::OnlinePayment::PayPal \
Library::CallNumber::LC \
Net::Z3950::Simple2ZOOM \
export CPAN_MODULES = \
Excel::Writer::XLSX \
+ Text::Unaccent::PurePerl \
Business::OnlinePayment::PayPal \
Template::Plugin::POSIX \
export CPAN_MODULES = \
Excel::Writer::XLSX \
+ Text::Unaccent::PurePerl \
Business::ISSN \
Net::Z3950::ZOOM \
Net::Z3950::Simple2ZOOM \
export CPAN_MODULES = \
Excel::Writer::XLSX \
+ Text::Unaccent::PurePerl \
Business::CreditCard::Object \
Business::OnlinePayment::PayPal \
Template::Plugin::POSIX \
export CPAN_MODULES = \
Business::CreditCard::Object \
Business::OnlinePayment::PayPal \
+ Text::Unaccent::PurePerl
export CPAN_MODULES_FORCE = \
Business::Stripe \
'Text::Aspell' => '0',
'Text::CSV' => '0',
'Text::Glob' => '0',
+ 'Text::Unaccent::PurePerl' => '0',
'Time::HiRes' => '0',
'Time::Local' => '0',
'Unicode::Normalize' => '0',
use OpenSRF::Utils qw/:datetime/;
use OpenILS::Utils::Fieldmapper;
use OpenSRF::Utils::SettingsClient;
+use OpenILS::Application::AppUtils;
use DateTime;
use DateTime::Format::ISO8601;
use DateTime::Set;
use DateTime::SpanSet;
+use Text::Unaccent::PurePerl qw(unac_string);
+
+my $apputils = "OpenILS::Application::AppUtils";
+my $U = $apputils;
+
my $_dt_parser = DateTime::Format::ISO8601->new;
my $log = 'OpenSRF::Utils::Logger';
NOTE
);
+sub _prepare_name_argument {
+ # Get rid of extra spaces, accents, and regex characters
+ my ($search) = _clean_regex_chars(@_);
+ $search =~ s/\s//g;
+ $search = Text::Unaccent::PurePerl::unac_string($search);
+
+ return $search;
+};
+
sub _clean_regex_chars {
my ($search) = @_;
# group 2 = phone, ident
# group 3 = barcode
- my $usr = join ' AND ', map { "evergreen.lowercase(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
- my @usrv = map { "^" . _clean_regex_chars($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+ # Treatment of name fields depends on whether the org has
+ # diacritic_insensitivity turned on or off.
+
+ my $diacritic_insensitive = $U->is_true($U->ou_ancestor_setting_value($ws_ou, 'circ.patron_search.diacritic_insensitive'));
+
+ my $usr;
+ my @usrv;
+
+ if ($diacritic_insensitive) {
+ $usr = join ' AND ', map { "evergreen.unaccent_and_squash(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+ @usrv = map { "^" . _prepare_name_argument($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+
+ } else {
+ $usr = join ' AND ', map { "evergreen.lowercase(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+ @usrv = map { "^" . _clean_regex_chars($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+ }
my $addr = join ' AND ', map { "evergreen.lowercase(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
my @addrv = map { "^" . _clean_regex_chars($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
my @namev;
if (0 && $nv) {
for my $n ( qw/first_given_name second_given_name family_name/ ) {
- push @ns, "evergreen.lowercase($n) ~ ?";
+ if ($diacritic_insensitive) {
+ push @ns, "evergreen.unaccent_and_squash($n) ~ ?";
+ } else {
+ push @ns, "evergreen.lowercase($n) ~ ?";
+ }
push @namev, "^$nv";
}
$name = '(' . join(' OR ', @ns) . ')';
$protect_reserved$
LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION evergreen.unaccent_and_squash ( IN arg text) RETURNS text
+ IMMUTABLE STRICT AS $$
+ BEGIN
+ RETURN evergreen.lowercase(unaccent(regexp_replace(arg, '\s','','g')));
+ END;
+$$ LANGUAGE PLPGSQL;
+
+
COMMIT;
CREATE INDEX actor_usr_first_given_name_idx ON actor.usr (evergreen.lowercase(first_given_name));
CREATE INDEX actor_usr_second_given_name_idx ON actor.usr (evergreen.lowercase(second_given_name));
CREATE INDEX actor_usr_family_name_idx ON actor.usr (evergreen.lowercase(family_name));
+CREATE INDEX actor_usr_first_given_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(first_given_name));
+CREATE INDEX actor_usr_second_given_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(second_given_name));
+CREATE INDEX actor_usr_family_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(family_name));
CREATE INDEX actor_usr_usrname_idx ON actor.usr (evergreen.lowercase(usrname));
CREATE INDEX actor_usr_email_idx ON actor.usr (evergreen.lowercase(email));
'coust', 'description'),
'bool');
+INSERT INTO config.org_unit_setting_type
+( name, grp, label, description, datatype )
+VALUES
+('circ.patron_search.diacritic_insensitive',
+ 'circ',
+ oils_i18n_gettext('circ.patron_search.diacritic_insensitive',
+ 'Patron search diacritic insensitive',
+ 'coust', 'label'),
+ oils_i18n_gettext('circ.patron_search.diacritic_insensitive',
+ 'Match patron last, first, and middle names irrespective of usage of diacritical marks or spaces. (e.g., Ines will match Inés; de la Cruz will match Delacruz)',
+ 'coust', 'description'),
+ 'bool');
+
+INSERT INTO actor.org_unit_setting (
+ org_unit, name, value
+) VALUES (
+ (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
+ 'circ.patron_search.diacritic_insensitive',
+ 'true'
+);
CREATE EXTENSION hstore;
CREATE EXTENSION intarray;
CREATE EXTENSION pgcrypto;
+CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public;
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public;
+
+CREATE OR REPLACE FUNCTION evergreen.unaccent_and_squash ( IN arg text) RETURNS text
+ IMMUTABLE STRICT AS $$
+ BEGIN
+ RETURN evergreen.lowercase(unaccent(regexp_replace(arg, '\s','','g')));
+ END;
+$$ LANGUAGE PLPGSQL;
+
+-- The unaccented indices for patron name fields
+CREATE INDEX actor_usr_first_given_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(first_given_name));
+CREATE INDEX actor_usr_second_given_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(second_given_name));
+CREATE INDEX actor_usr_family_name_unaccent_idx ON actor.usr (evergreen.unaccent_and_squash(family_name));
+
+-- DB setting to control behavior; true by default
+INSERT INTO config.org_unit_setting_type
+( name, grp, label, description, datatype )
+VALUES
+('circ.patron_search.diacritic_insensitive',
+ 'circ',
+ oils_i18n_gettext('circ.patron_search.diacritic_insensitive',
+ 'Patron search diacritic insensitive',
+ 'coust', 'label'),
+ oils_i18n_gettext('circ.patron_search.diacritic_insensitive',
+ 'Match patron last, first, and middle names irrespective of usage of diacritical marks or spaces. (e.g., Ines will match Inés; de la Cruz will match Delacruz)',
+ 'coust', 'description'),
+ 'bool');
+
+INSERT INTO actor.org_unit_setting (
+ org_unit, name, value
+) VALUES (
+ (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
+ 'circ.patron_search.diacritic_insensitive',
+ 'true'
+);
+
+
+COMMIT;
+
--- /dev/null
+Accent Insensitive Patron Search
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+When performing a patron search, in addition to the (existing)
+case-insensitivity, these additional characteristics will govern the
+search:
+
+Accent (diacritic) insensitivity::
+Diacritics will be transformed into a plain character equivalent for comparison purposes. So if the patron name is Eugène Delacroix, for example, you could enter
+euge for the First Name, and it would match. Ligatures such as Œ are expanded into the constituent characters "OE".
+
+Space insensitivity::
+Spaces will be squashed out for comparison purposes. If the patron is, again,
+Eugène Delacroix, you could enter "de la croix" in the Last Name field and it would match.
+
+This behavior affects the Last Name, First Name, and Middle Name fields
+of the search.