1 package WebPAC::Normalize;
11 marc marc_indicators marc_repeatable_subfield
12 marc_compose marc_leader
13 marc_duplicate marc_remove
17 regex prefix suffix surround
18 first lookup join_with
30 #use base qw/WebPAC::Common/;
31 use Data::Dump qw/dump/;
32 use Storable qw/dclone/;
41 WebPAC::Normalize - describe normalisaton rules using sets
49 our $VERSION = '0.26';
53 This module uses C<conf/normalize/*.pl> files to perform normalisation
54 from input records using perl functions which are specialized for set
57 Sets are implemented as arrays, and normalisation file is valid perl, which
58 means that you check it's validity before running WebPAC using
59 C<perl -c normalize.pl>.
61 Normalisation can generate multiple output normalized data. For now, supported output
62 types (on the left side of definition) are: C<tag>, C<display>, C<search> and
67 Functions which start with C<_> are private and used by WebPAC internally.
68 All other functions are available for use within normalisation rules.
74 my $ds = WebPAC::Normalize::data_structure(
75 lookup => $lookup_hash,
77 rules => $normalize_pl_config,
78 marc_encoding => 'utf-8',
80 load_row_coderef => sub {
81 my ($database,$input,$mfn) = shift;
82 $store->load_row( database => $database, input => $input, id => $mfn );
86 Options C<row>, C<rules> and C<log> are mandatory while all
89 C<load_row_coderef> is closure only used when executing lookups, so they will
90 die if it's not defined.
92 This function will B<die> if normalizastion can't be evaled.
94 Since this function isn't exported you have to call it with
95 C<WebPAC::Normalize::data_structure>.
104 die "need row argument" unless ($arg->{row});
105 die "need normalisation argument" unless ($arg->{rules});
108 _set_lookup( $arg->{lookup} ) if defined($arg->{lookup});
109 _set_rec( $arg->{row} );
110 _set_config( $arg->{config} ) if defined($arg->{config});
111 _clean_ds( %{ $arg } );
112 $load_row_coderef = $arg->{load_row_coderef};
114 eval "$arg->{rules}";
115 die "error evaling $arg->{rules}: $@\n" if ($@);
122 Set current record hash
131 $rec = shift or die "no record hash";
136 Set current config hash
138 _set_config( $config );
146 Code of current database
164 Return hash formatted as data structure
170 my ($out, $marc_record, $marc_encoding, $marc_repeatable_subfield, $marc_indicators, $marc_leader);
171 my ($marc_record_offset, $marc_fetch_offset) = (0, 0);
179 Clean data structure hash for next record
187 ($out,$marc_record, $marc_encoding, $marc_repeatable_subfield, $marc_indicators, $marc_leader) = ();
188 ($marc_record_offset, $marc_fetch_offset) = (0,0);
189 $marc_encoding = $a->{marc_encoding};
194 Set current lookup hash
196 _set_lookup( $lookup );
208 Get current lookup hash
210 my $lookup = _get_lookup();
220 Setup code reference which will return L<data_structure> from
224 my ($database,$input,$mfn) = @_;
225 $store->load_row( database => $database, input => $input, id => $mfn );
232 confess "argument isn't CODE" unless ref($coderef) eq 'CODE';
234 $load_row_coderef = $coderef;
237 =head2 _get_marc_fields
239 Get all fields defined by calls to C<marc>
241 $marc->add_fields( WebPAC::Normalize:_get_marc_fields() );
243 We are using I<magic> which detect repeatable fields only from
244 sequence of field/subfield data generated by normalization.
246 Repeatable field is created when there is second occurence of same subfield or
247 if any of indicators are different.
249 This is sane for most cases. Something like:
255 will be created from any combination of:
257 900a-1 900a-2 900a-3 900b-1 900b-2 900c-1
261 marc('900','a', rec('200','a') );
262 marc('900','b', rec('200','b') );
263 marc('900','c', rec('200','c') );
265 which might not be what you have in mind. If you need repeatable subfield,
266 define it using C<marc_repeatable_subfield> like this:
268 marc_repeatable_subfield('900','a');
269 marc('900','a', rec('200','a') );
270 marc('900','b', rec('200','b') );
271 marc('900','c', rec('200','c') );
275 900a-1 900a-2 900a-3 900b-1 900c-1
278 There is also support for returning next or specific using:
280 while (my $mf = WebPAC::Normalize:_get_marc_fields( fetch_next => 1 ) ) {
281 # do something with $mf
284 will always return fields from next MARC record or
286 my $mf = WebPAC::Normalize::_get_marc_fields( offset => 42 );
288 will return 42th copy record (if it exists).
292 sub _get_marc_fields {
295 warn "### _get_marc_fields arg: ", dump($arg), $/ if ($debug > 2);
296 my $offset = $marc_fetch_offset;
297 if ($arg->{offset}) {
298 $offset = $arg->{offset};
299 } elsif($arg->{fetch_next}) {
300 $marc_fetch_offset++;
303 return if (! $marc_record || ref($marc_record) ne 'ARRAY');
305 warn "### full marc_record = ", dump( @{ $marc_record }), $/ if ($debug > 2);
307 my $marc_rec = $marc_record->[ $offset ];
309 warn "## _get_marc_fields (at offset: $offset) -- marc_record = ", dump( @$marc_rec ), $/ if ($debug > 1);
311 return if (! $marc_rec || ref($marc_rec) ne 'ARRAY' || $#{ $marc_rec } < 0);
313 # first, sort all existing fields
314 # XXX might not be needed, but modern perl might randomize elements in hash
315 my @sorted_marc_record = sort {
316 $a->[0] . ( $a->[3] || '' ) cmp $b->[0] . ( $b->[3] || '')
319 @sorted_marc_record = @{ $marc_rec }; ### FIXME disable sorting
324 # count unique field-subfields (used for offset when walking to next subfield)
326 map { $u->{ $_->[0] . ( $_->[3] || '') }++ } @sorted_marc_record;
329 warn "## marc_repeatable_subfield = ", dump( $marc_repeatable_subfield ), $/ if ( $marc_repeatable_subfield );
330 warn "## marc_record[$offset] = ", dump( $marc_rec ), $/;
331 warn "## sorted_marc_record = ", dump( \@sorted_marc_record ), $/;
332 warn "## subfield count = ", dump( $u ), $/;
335 my $len = $#sorted_marc_record;
340 foreach ( 0 .. $len ) {
342 # find next element which isn't visited
343 while ($visited->{$i}) {
344 $i = ($i + 1) % ($len + 1);
350 my $row = dclone( $sorted_marc_record[$i] );
352 # field and subfield which is key for
353 # marc_repeatable_subfield and u
354 my $fsf = $row->[0] . ( $row->[3] || '' );
358 print "### field so far [", $#$field, "] : ", dump( $field ), " ", $field ? 'T' : 'F', $/;
359 print "### this [$i]: ", dump( $row ),$/;
360 print "### sf: ", $row->[3], " vs ", $field->[3],
361 $marc_repeatable_subfield->{ $row->[0] . $row->[3] } ? ' (repeatable)' : '', $/,
367 if ( $#$field >= 0 ) {
369 $row->[0] ne $field->[0] || # field
370 $row->[1] ne $field->[1] || # i1
371 $row->[2] ne $field->[2] # i2
374 warn "## saved/1 ", dump( $field ),$/ if ($debug);
378 ( $row->[3] lt $field->[-2] ) # subfield which is not next (e.g. a after c)
380 ( $row->[3] eq $field->[-2] && # same subfield, but not repeatable
381 ! $marc_repeatable_subfield->{ $fsf }
385 warn "## saved/2 ", dump( $field ),$/ if ($debug);
389 # append new subfields to existing field
390 push @$field, ( $row->[3], $row->[4] );
397 if (! $marc_repeatable_subfield->{ $fsf }) {
398 # make step to next subfield
399 $i = ($i + $u->{ $fsf } ) % ($len + 1);
405 warn "## saved/3 ", dump( $field ),$/ if ($debug);
413 Change level of debug warnings
421 return $debug unless defined($l);
422 warn "debug level $l",$/ if ($l > 0);
426 =head1 Functions to create C<data_structure>
428 Those functions generally have to first in your normalization file.
432 Define new tag for I<search> and I<display>.
434 tag('Title', rec('200','a') );
440 my $name = shift or die "tag needs name as first argument";
441 my @o = grep { defined($_) && $_ ne '' } @_;
443 $out->{$name}->{tag} = $name;
444 $out->{$name}->{search} = \@o;
445 $out->{$name}->{display} = \@o;
450 Define tag just for I<display>
452 @v = display('Title', rec('200','a') );
457 my $name = shift or die "display needs name as first argument";
458 my @o = grep { defined($_) && $_ ne '' } @_;
460 $out->{$name}->{tag} = $name;
461 $out->{$name}->{display} = \@o;
466 Prepare values just for I<search>
468 @v = search('Title', rec('200','a') );
473 my $name = shift or die "search needs name as first argument";
474 my @o = grep { defined($_) && $_ ne '' } @_;
476 $out->{$name}->{tag} = $name;
477 $out->{$name}->{search} = \@o;
482 Setup fields within MARC leader or get leader
484 marc_leader('05','c');
485 my $leader = marc_leader();
490 my ($offset,$value) = @_;
493 $marc_leader->{ $offset } = $value;
501 Save value for MARC field
503 marc('900','a', rec('200','a') );
504 marc('001', rec('000') );
509 my $f = shift or die "marc needs field";
510 die "marc field must be numer" unless ($f =~ /^\d+$/);
514 $sf = shift or die "marc needs subfield";
518 my $v = $_; # make var read-write for Encode
519 next unless (defined($v) && $v !~ /^\s*$/);
520 my ($i1,$i2) = defined($marc_indicators->{$f}) ? @{ $marc_indicators->{$f} } : (' ',' ');
522 push @{ $marc_record->[ $marc_record_offset ] }, [ $f, $i1, $i2, $sf => $v ];
524 push @{ $marc_record->[ $marc_record_offset ] }, [ $f, $v ];
529 =head2 marc_repeatable_subfield
531 Save values for MARC repetable subfield
533 marc_repeatable_subfield('910', 'z', rec('909') );
537 sub marc_repeatable_subfield {
539 die "marc_repeatable_subfield need field and subfield!\n" unless ($f && $sf);
540 $marc_repeatable_subfield->{ $f . $sf }++;
544 =head2 marc_indicators
546 Set both indicators for MARC field
548 marc_indicators('900', ' ', 1);
550 Any indicator value other than C<0-9> will be treated as undefined.
554 sub marc_indicators {
555 my $f = shift || die "marc_indicators need field!\n";
557 die "marc_indicators($f, ...) need i1!\n" unless(defined($i1));
558 die "marc_indicators($f, $i1, ...) need i2!\n" unless(defined($i2));
560 $i1 = ' ' if ($i1 !~ /^\d$/);
561 $i2 = ' ' if ($i2 !~ /^\d$/);
562 @{ $marc_indicators->{$f} } = ($i1,$i2);
567 Save values for each MARC subfield explicitly
576 If you specify C<+> for subfield, value will be appended
577 to previous defined subfield.
582 my $f = shift or die "marc_compose needs field";
583 die "marc_compose field must be numer" unless ($f =~ /^\d+$/);
585 my ($i1,$i2) = defined($marc_indicators->{$f}) ? @{ $marc_indicators->{$f} } : (' ',' ');
586 my $m = [ $f, $i1, $i2 ];
588 warn "### marc_compose input subfields = ", dump(@_),$/ if ($debug > 2);
591 die "ERROR: marc_compose",dump($f,@_)," not valid (must be even).\nDo you need to add first() or join() around some argument?\n";
598 next unless (defined($v) && $v !~ /^\s*$/);
599 warn "## ++ marc_compose($f,$sf,$v) ", dump( $m ),$/ if ($debug > 1);
601 push @$m, ( $sf, $v );
607 warn "## marc_compose current marc = ", dump( $m ),$/ if ($debug > 1);
609 push @{ $marc_record->[ $marc_record_offset ] }, $m if ($#{$m} > 2);
612 =head2 marc_duplicate
614 Generate copy of current MARC record and continue working on copy
618 Copies can be accessed using C<< _get_marc_fields( fetch_next => 1 ) >> or
619 C<< _get_marc_fields( offset => 42 ) >>.
624 my $m = $marc_record->[ -1 ];
625 die "can't duplicate record which isn't defined" unless ($m);
626 push @{ $marc_record }, dclone( $m );
627 warn "## marc_duplicate = ", dump(@$marc_record), $/ if ($debug > 1);
628 $marc_record_offset = $#{ $marc_record };
629 warn "## marc_record_offset = $marc_record_offset", $/ if ($debug > 1);
634 Remove some field or subfield from MARC record.
637 marc_remove('200','a');
639 This will erase field C<200> or C<200^a> from current MARC record.
643 Will remove all fields in current MARC record.
645 This is useful after calling C<marc_duplicate> or on it's own (but, you
646 should probably just remove that subfield definition if you are not
647 using C<marc_duplicate>).
649 FIXME: support fields < 10.
656 die "marc_remove needs record number" unless defined($f);
658 my $marc = $marc_record->[ $marc_record_offset ];
660 warn "### marc_remove before = ", dump( $marc ), $/ if ($debug > 2);
664 delete( $marc_record->[ $marc_record_offset ] );
665 warn "## full marc_record = ", dump( @{ $marc_record }), $/ if ($debug > 1);
670 foreach ( 0 .. $#{ $marc } ) {
671 last unless (defined $marc->[$i]);
672 warn "#### working on ",dump( @{ $marc->[$i] }), $/ if ($debug > 3);
673 if ($marc->[$i]->[0] eq $f) {
676 splice @$marc, $i, 1;
677 warn "#### slice \@\$marc, $i, 1 = ",dump( @{ $marc }), $/ if ($debug > 3);
680 foreach my $j ( 0 .. (( $#{ $marc->[$i] } - 3 ) / 2) ) {
681 my $o = ($j * 2) + 3;
682 if ($marc->[$i]->[$o] eq $sf) {
684 splice @{$marc->[$i]}, $o, 2;
685 warn "#### slice \@{\$marc->[$i]}, $o, 2 = ", dump( @{ $marc }), $/ if ($debug > 3);
686 # is record now empty?
687 if ($#{ $marc->[$i] } == 2) {
688 splice @$marc, $i, 1;
689 warn "#### slice \@\$marc, $i, 1 = ", dump( @{ $marc }), $/ if ($debug > 3);
699 warn "### marc_remove($f", $sf ? ",$sf" : "", ") after = ", dump( $marc ), $/ if ($debug > 2);
701 $marc_record->[ $marc_record_offset ] = $marc;
704 warn "## full marc_record = ", dump( @{ $marc_record }), $/ if ($debug > 1);
707 =head2 marc_original_order
709 Copy all subfields preserving original order to marc field.
711 marc_original_order( marc_field_number, original_input_field_number );
713 Please note that field numbers are consistent with other commands (marc
714 field number first), but somewhat counter-intuitive (destination and then
717 You might want to use this command if you are just renaming subfields or
718 using pre-processing modify_record in C<config.yml> and don't need any
719 post-processing or want to preserve order of original subfields.
724 sub marc_original_order {
726 my ($to, $from) = @_;
727 die "marc_original_order needs from and to fields\n" unless ($from && $to);
729 return unless defined($rec->{$from});
731 my $r = $rec->{$from};
732 die "record field $from isn't array\n" unless (ref($r) eq 'ARRAY');
734 my ($i1,$i2) = defined($marc_indicators->{$to}) ? @{ $marc_indicators->{$to} } : (' ',' ');
735 warn "## marc_original_order($to,$from) source = ", dump( $r ),$/ if ($debug > 1);
737 foreach my $d (@$r) {
739 if (! defined($d->{subfields}) && ref($d->{subfields}) ne 'ARRAY') {
740 warn "# marc_original_order($to,$from): field $from doesn't have subfields specification\n";
744 my @sfs = @{ $d->{subfields} };
746 die "field $from doesn't have even number of subfields specifications\n" unless($#sfs % 2 == 1);
748 warn "#--> d: ",dump($d), "\n#--> sfs: ",dump(@sfs),$/ if ($debug > 2);
750 my $m = [ $to, $i1, $i2 ];
752 while (my $sf = shift @sfs) {
754 warn "#--> sf: ",dump($sf), $/ if ($debug > 2);
755 my $offset = shift @sfs;
756 die "corrupted sufields specification for field $from\n" unless defined($offset);
759 if (ref($d->{$sf}) eq 'ARRAY') {
760 $v = $d->{$sf}->[$offset] if (defined($d->{$sf}->[$offset]));
761 } elsif ($offset == 0) {
764 die "field $from subfield '$sf' need occurence $offset which doesn't exist", dump($d->{$sf});
766 push @$m, ( $sf, $v ) if (defined($v));
770 push @{ $marc_record->[ $marc_record_offset ] }, $m;
774 warn "## marc_record = ", dump( $marc_record ),$/ if ($debug > 1);
778 =head1 Functions to extract data from input
780 This function should be used inside functions to create C<data_structure> described
783 =head2 _pack_subfields_hash
785 @subfields = _pack_subfields_hash( $h );
786 $subfields = _pack_subfields_hash( $h, 1 );
788 Return each subfield value in array or pack them all together and return scalar
789 with subfields (denoted by C<^>) and values.
793 sub _pack_subfields_hash {
795 warn "## _pack_subfields_hash( ",dump(@_), " )\n" if ($debug > 1);
797 my ($h,$include_subfields) = @_;
799 if ( defined($h->{subfields}) ) {
800 my $sfs = delete $h->{subfields} || die "no subfields?";
803 my $sf = shift @$sfs;
804 push @out, '^' . $sf if ($include_subfields);
806 if ($o == 0 && ref( $h->{$sf} ) ne 'ARRAY' ) {
807 # single element subfields are not arrays
808 #warn "====> $sf $o / $#$sfs ", dump( $sfs, $h->{$sf} ), "\n";
810 push @out, $h->{$sf};
812 #warn "====> $sf $o / $#$sfs ", dump( $sfs, $h->{$sf} ), "\n";
813 push @out, $h->{$sf}->[$o];
816 if ($include_subfields) {
817 return join('', @out);
822 if ($include_subfields) {
824 foreach my $sf (sort keys %$h) {
825 if (ref($h->{$sf}) eq 'ARRAY') {
826 $out .= '^' . $sf . join('^' . $sf, @{ $h->{$sf} });
828 $out .= '^' . $sf . $h->{$sf};
833 # FIXME this should probably be in alphabetical order instead of hash order
841 Return all values in some field
845 TODO: order of values is probably same as in source data, need to investigate that
851 warn "rec1($f) = ", dump( $rec->{$f} ), $/ if ($debug > 1);
852 return unless (defined($rec) && defined($rec->{$f}));
853 warn "rec1($f) = ", dump( $rec->{$f} ), $/ if ($debug > 1);
854 if (ref($rec->{$f}) eq 'ARRAY') {
856 foreach my $h ( @{ $rec->{$f} } ) {
857 if (ref($h) eq 'HASH') {
858 push @out, ( _pack_subfields_hash( $h ) );
864 } elsif( defined($rec->{$f}) ) {
871 Return all values in specific field and subfield
879 return unless (defined($rec && $rec->{$f}));
881 warn "rec2($f,$sf) = ", dump( $rec->{$f} ), $/ if ($debug > 1);
883 if (ref($_->{$sf}) eq 'ARRAY') {
888 } grep { ref($_) eq 'HASH' && $_->{$sf} } @{ $rec->{$f} };
898 If rec() returns just single value, it will
899 return scalar, not array.
910 if ($#out == 0 && ! wantarray) {
921 Apply regex to some or all values
923 @v = regex( 's/foo/bar/g', @v );
930 #warn "r: $r\n", dump(\@_);
934 push @out, $t if ($t && $t ne '');
941 Prefix all values with a string
943 @v = prefix( 'my_', @v );
948 my $p = shift or return;
949 return map { $p . $_ } grep { defined($_) } @_;
954 suffix all values with a string
956 @v = suffix( '_my', @v );
961 my $s = shift or die "suffix needs string as first argument";
962 return map { $_ . $s } grep { defined($_) } @_;
967 surround all values with a two strings
969 @v = surround( 'prefix_', '_suffix', @v );
974 my $p = shift or die "surround need prefix as first argument";
975 my $s = shift or die "surround needs suffix as second argument";
976 return map { $p . $_ . $s } grep { defined($_) } @_;
994 Consult lookup hashes for some value
998 'ffkk/peri/mfn'.rec('000')
1000 'ffkk','peri','200-a-200-e',
1002 first(rec(200,'a')).' '.first(rec('200','e'))
1006 Code like above will be B<automatically generated> using L<WebPAC::Parse> from
1007 normal lookup definition in C<conf/lookup/something.pl> which looks like:
1010 # which results to return from record recorded in lookup
1011 sub { 'ffkk/peri/mfn' . rec('000') },
1012 # from which database and input
1014 # such that following values match
1015 sub { first(rec(200,'a')) . ' ' . first(rec('200','e')) },
1016 # if this part is missing, we will try to match same fields
1017 # from lookup record and current one, or you can override
1018 # which records to use from current record using
1019 sub { rec('900','x') . ' ' . rec('900','y') },
1022 You can think about this lookup as SQL (if that helps):
1029 sub { filter from lookuped record }
1031 sub { optional filter on current record }
1038 my ($what, $database, $input, $key, $having) = @_;
1040 confess "lookup needs 5 arguments: what, database, input, key, having\n" unless ($#_ == 4);
1042 warn "## lookup ($database, $input, $key)", $/ if ($debug > 1);
1043 return unless (defined($lookup->{$database}->{$input}->{$key}));
1045 confess "lookup really need load_row_coderef added to data_structure\n" unless ($load_row_coderef);
1048 my @having = $having->();
1050 warn "## having = ", dump( @having ) if ($debug > 2);
1052 foreach my $h ( @having ) {
1053 if (defined($lookup->{$database}->{$input}->{$key}->{$h})) {
1054 warn "lookup for $database/$input/$key/$h return ",dump($lookup->{$database}->{$input}->{$key}->{$h}),"\n" if ($debug);
1055 $mfns->{$_}++ foreach keys %{ $lookup->{$database}->{$input}->{$key}->{$h} };
1059 return unless ($mfns);
1061 my @mfns = sort keys %$mfns;
1063 warn "# lookup loading $database/$input/$key mfn ", join(",",@mfns)," having ",dump(@having),"\n" if ($debug);
1068 foreach my $mfn (@mfns) {
1069 $rec = $load_row_coderef->( $database, $input, $mfn );
1071 warn "got $database/$input/$mfn = ", dump($rec), $/ if ($debug);
1073 my @vals = $what->();
1075 push @out, ( @vals );
1077 warn "lookup for mfn $mfn returned ", dump(@vals), $/ if ($debug);
1080 # if (ref($lookup->{$k}) eq 'ARRAY') {
1081 # return @{ $lookup->{$k} };
1083 # return $lookup->{$k};
1088 warn "## lookup returns = ", dump(@out), $/ if ($debug);
1097 =head2 save_into_lookup
1099 Save value into lookup. It associates current database, input
1100 and specific keys with one or more values which will be
1101 associated over MFN.
1103 MFN will be extracted from first occurence current of field 000
1104 in current record, or if it doesn't exist from L<_set_config> C<_mfn>.
1106 my $nr = save_into_lookup($database,$input,$key,sub {
1107 # code which produce one or more values
1110 It returns number of items saved.
1112 This function shouldn't be called directly, it's called from code created by
1117 sub save_into_lookup {
1118 my ($database,$input,$key,$coderef) = @_;
1119 die "save_into_lookup needs database" unless defined($database);
1120 die "save_into_lookup needs input" unless defined($input);
1121 die "save_into_lookup needs key" unless defined($key);
1122 die "save_into_lookup needs CODE" unless ( defined($coderef) && ref($coderef) eq 'CODE' );
1124 warn "## save_into_lookup rec = ", dump($rec), " config = ", dump($config), $/ if ($debug > 2);
1127 defined($rec->{'000'}->[0]) ? $rec->{'000'}->[0] :
1128 defined($config->{_mfn}) ? $config->{_mfn} :
1129 die "mfn not defined or zero";
1133 foreach my $v ( $coderef->() ) {
1134 $lookup->{$database}->{$input}->{$key}->{$v}->{$mfn}++;
1135 warn "# saved lookup $database/$input/$key [$v] $mfn\n" if ($debug > 1);
1144 Consult config values stored in C<config.yml>
1146 # return database code (key under databases in yaml)
1147 $database_code = config(); # use _ from hash
1148 $database_name = config('name');
1149 $database_input_name = config('input name');
1150 $tag = config('input normalize tag');
1152 Up to three levels are supported.
1157 return unless ($config);
1165 warn "### getting config($p)\n" if ($debug > 1);
1167 my @p = split(/\s+/,$p);
1169 $v = $config->{ '_' }; # special, database code
1172 my $c = dclone( $config );
1174 foreach my $k (@p) {
1175 warn "### k: $k c = ",dump($c),$/ if ($debug > 1);
1176 if (ref($c) eq 'ARRAY') {
1178 warn "config($p) taking first occurence of '$k', probably not what you wanted!\n";
1182 if (! defined($c->{$k}) ) {
1193 warn "## config( '$p' ) = ",dump( $v ),$/ if ($v && $debug);
1194 warn "config( '$p' ) is empty\n" if (! $v);
1201 Returns unique id of this record
1205 Returns C<42/2> for 2nd occurence of MFN 42.
1210 my $mfn = $config->{_mfn} || die "no _mfn in config data";
1211 return $mfn . $#{$marc_record} ? $#{$marc_record} + 1 : '';
1216 Joins walues with some delimiter
1218 $v = join_with(", ", @v);
1224 warn "### join_with('$d',",dump(@_),")\n" if ($debug > 2);
1225 my $v = join($d, grep { defined($_) && $_ ne '' } @_);
1226 return '' unless defined($v);
1232 Split record subfield on some regex and take one of parts out
1234 $a_before_semi_column =
1235 split_rec_on('200','a', /\s*;\s*/, $part);
1237 C<$part> is optional number of element. First element is
1240 If there is no C<$part> parameter or C<$part> is 0, this function will
1241 return all values produced by splitting.
1246 die "split_rec_on need (fld,sf,regex[,part]" if ($#_ < 2);
1248 my ($fld, $sf, $regex, $part) = @_;
1249 warn "### regex ", ref($regex), $regex, $/ if ($debug > 2);
1251 my @r = rec( $fld, $sf );
1253 warn "### first rec($fld,$sf) = ",dump($v),$/ if ($debug > 2);
1255 return '' if ( ! defined($v) || $v =~ /^\s*$/);
1257 my @s = split( $regex, $v );
1258 warn "## split_rec_on($fld,$sf,$regex,$part) = ",dump(@s),$/ if ($debug > 1);
1259 if ($part && $part > 0) {
1260 return $s[ $part - 1 ];
1270 set( key => 'value' );
1276 warn "## set ( $k => ", dump($v), " )", $/ if ( $debug );
1287 my $k = shift || return;
1288 my $v = $hash->{$k};
1289 warn "## get $k = ", dump( $v ), $/ if ( $debug );
1295 if ( count( @result ) == 1 ) {
1296 # do something if only 1 result is there
1302 warn "## count ",dump(@_),$/ if ( $debug );