1 # Vend::DbSearch - Search indexes with Interchange
3 # $Id: RefSearch.pm,v 2.11 2007-08-09 13:40:54 pajamian Exp $
5 # Adapted for use with Interchange from Search::TextSearch
7 # Copyright (C) 2002-2007 Interchange Development Group
8 # Copyright (C) 1996-2002 Red Hat, Inc.
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public
21 # License along with this program; if not, write to the Free
22 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
25 package Vend::RefSearch;
28 @ISA = qw(Vend::Search);
30 $VERSION = substr(q$Revision: 2.11 $, 10);
33 no warnings qw(uninitialized numeric);
37 $s->{mv_one_sql_table} = 1;
38 $s->{mv_list_only} = 1; # makes perform_search only return results array
39 return Vend::Scan::perform_search($opt, undef, $s);
44 $s->{mv_return_reference} = 'HASH';
45 $s->{mv_one_sql_table} = 1;
46 $s->{mv_list_only} = 1; # makes perform_search only return results array
47 return Vend::Scan::perform_search($opt, undef, $s);
52 $s->{mv_return_reference} = 'LIST';
53 $s->{mv_one_sql_table} = 1;
54 $s->{mv_list_only} = 1; # makes perform_search only return results array
55 return Vend::Scan::perform_search($opt, undef, $s);
61 mv_index_delim => "\t",
64 verbatim_columns => 1,
68 my ($s, $options) = @_;
70 # autovivify references of nested data structures we use below, since they
71 # don't yet exist at daemon startup time before configuration is done
72 $Vend::Cfg->{ProductFiles}[0] or 1;
73 $::Variable->{MV_DEFAULT_SEARCH_TABLE} or 1;
75 @{$s}{keys %Default} = (values %Default);
76 $s->{mv_all_chars} = [1];
78 ### This is a bit of a misnomer, for really it is the base table
79 ### that we will use if no base=table param is specified
80 $s->{mv_base_directory} = $Vend::Cfg->{ProductFiles}[0];
81 $s->{mv_begin_string} = [];
83 $s->{mv_column_op} = [];
85 $s->{mv_numeric} = [];
86 $s->{mv_orsearch} = [];
87 $s->{mv_search_field} = [];
88 $s->{mv_search_file} = [ @{
89 $::Variable->{MV_DEFAULT_SEARCH_TABLE}
90 || $Vend::Cfg->{ProductFiles}
92 $s->{mv_search_group} = [];
93 $s->{mv_searchspec} = [];
94 $s->{mv_sort_option} = [];
95 $s->{mv_substring_match} = [];
98 $s->{$_} = $options->{$_};
105 my ($class, %options) = @_;
106 my $s = new Vend::Search;
113 my($s,%options) = @_;
116 my($limit_sub,$return_sub,$delayed_return);
118 my($searchfile,@searchfiles);
122 while (($key,$val) = each %options) {
126 $s->{mv_return_delim} = $s->{mv_index_delim}
127 unless defined $s->{mv_return_delim};
129 #::logDebug("Called RefSearch, object=" . ::uneval($s));
130 @specs = @{$s->{mv_searchspec}};
132 @pats = $s->spec_check(@specs);
134 if ($s->{mv_coordinate}) {
137 elsif ($s->{mv_orsearch}[0]) {
138 eval {$f = $s->create_search_or(
140 qw/mv_case mv_substring_match mv_negate/
145 eval {$f = $s->create_search_and(
147 qw/mv_case mv_substring_match mv_negate/
152 $@ and return $s->search_error("Function creation: $@");
155 #::logDebug("searchfiles=@searchfiles");
160 my $target = $s->{mv_search_reference};
162 #::logDebug("target: " . ::uneval($target));
165 my $sr = $::Instance->{SearchObject}{$label};
167 return $s->search_error("$label is not a target nor a label referencing a previous search");
169 $target= $sr->{mv_results};
170 $s->{mv_field_names} ||= $sr->{mv_field_names};
173 #::logDebug("field names=" . ::uneval($s->{mv_field_names}));
174 #::logDebug("target now: " . ::uneval($target));
176 ref($target) ne 'ARRAY'
178 (defined $target->[0] && ref($target->[0]) ne 'ARRAY')
181 return $s->search_error("Reference for search must be array of arrays");
184 if(! $s->{mv_field_names}) {
185 if($s->{head_skip}) {
186 my @lines = splice(@$target,0,$s->{mv_head_skip});
187 $s->{mv_field_names} = pop(@lines);
190 $s->{mv_field_names} = [ 0 .. (@{$target->[0]} - 1) ]
194 $s->hash_fields($s->{mv_field_names});
198 ($limit_sub, $prospect) = $s->get_limit($f);
201 $@ and return $s->search_error("Limit subroutine creation: $@");
203 $f = $prospect if $prospect;
205 eval {($return_sub, $delayed_return) = $s->get_return()};
207 $@ and return $s->search_error("Return subroutine creation: $@");
209 if(! defined $f and defined $limit_sub) {
210 #::logDebug("no f, limit, dbref=$dbref");
213 for $ref (@$target) {
214 next unless $limit_sub->($ref);
215 push @out, $return_sub->($ref);
218 elsif(defined $limit_sub) {
219 #::logDebug("f and limit, dbref=$dbref");
222 for $ref (@$target) {
223 $_ = join "\t", @$ref;
225 next unless $limit_sub->($ref);
226 push @out, $return_sub->($ref);
229 elsif (!defined $f) {
230 return $s->search_error('No search definition');
233 #::logDebug("f and no limit, dbref=$dbref");
236 for $ref (@$target) {
237 #::logDebug("f and no limit, ref=$ref");
238 $_ = join "\t", @$ref;
240 push @out, $return_sub->($ref);
245 $s->{matches} = scalar(@out);
246 #::logDebug("before delayed return: self=" . ::Vend::Util::uneval_it({%$s}));
248 if($delayed_return and $s->{matches} > 0) {
249 $s->hash_fields($s->{mv_field_names}, qw/mv_sort_field/);
250 $s->sort_search_return(\@out);
251 $delayed_return = $s->get_return(1);
252 @out = map { $delayed_return->($_) } @out;
254 #::logDebug("after delayed return: self=" . ::Vend::Util::uneval({%$s}));
256 if($s->{mv_unique}) {
258 @out = grep ! $seen{$_->[0]}++, @out;
261 if($s->{mv_max_matches} > 0) {
262 splice @out, $s->{mv_max_matches};
265 $s->{matches} = scalar(@out);
267 ## This is the normal return point unless RefSearch called by program
269 return @out if $s->{mv_return_filtered};
271 if ($s->{matches} > $s->{mv_matchlimit} and $s->{mv_matchlimit} > 0) {
273 or ::logError("Error saving matches: $!");
274 if ($s->{mv_first_match}) {
275 splice(@out,0,$s->{mv_first_match}) if $s->{mv_first_match};
276 $s->{mv_next_pointer} = $s->{mv_first_match} + $s->{mv_matchlimit};
277 $s->{mv_next_pointer} = 0
278 if $s->{mv_next_pointer} > $s->{matches};
280 elsif ($s->{mv_start_match}) {
281 my $comp = $s->{mv_start_match};
286 next unless $_->[0] eq $comp;
290 if(! $found and $s->{mv_numeric}[0]) {
293 next unless $_->[0] >= $comp;
301 next unless $_->[0] ge $comp;
307 splice(@out,0,$found);
308 $s->{mv_first_match} = $found;
309 $s->{mv_next_pointer} = $found + $s->{mv_matchlimit};
310 $s->{mv_next_pointer} = 0
311 if $s->{mv_next_pointer} > $s->{matches};
314 $#out = $s->{mv_matchlimit} - 1;
316 #::logDebug("after hash fields: self=" . ::Vend::Util::uneval_it({%$s}));
317 #::logDebug("after delayed return: self=" . ::Vend::Util::uneval_it({%$s}));
319 if(! $s->{mv_return_reference}) {
320 $s->{mv_results} = \@out;
322 elsif($s->{mv_return_reference} eq 'LIST') {
323 @out = map { join $s->{mv_return_delim}, @$_ } @out;
324 $s->{mv_results} = join $s->{mv_record_delim}, @out;
328 @names = @{ $s->{mv_field_names} }[ @{$s->{mv_return_fields}} ];
329 $names[0] eq '0' and $names[0] = 'code';
333 @{ $h } {@names} = @$_;
336 $s->{mv_results} = \@ary;
341 # Unfortunate hack need for Safe searches
342 *create_search_and = \&Vend::Search::create_search_and;
343 *create_search_or = \&Vend::Search::create_search_or;
344 *dump_options = \&Vend::Search::dump_options;
345 *escape = \&Vend::Search::escape;
346 *get_limit = \&Vend::Search::get_limit;
347 *get_return = \&Vend::Search::get_return;
348 *get_scalar = \&Vend::Search::get_scalar;
349 *hash_fields = \&Vend::Search::hash_fields;
350 *map_ops = \&Vend::Search::map_ops;
351 *more_matches = \&Vend::Search::more_matches;
352 *range_check = \&Vend::Search::range_check;
353 *restore_specs = \&Vend::Search::restore_specs;
354 *save_context = \&Vend::Search::save_context;
355 *save_more = \&Vend::Search::save_more;
356 *save_specs = \&Vend::Search::save_specs;
357 *saved_params = \&Vend::Search::saved_params;
358 *search_error = \&Vend::Search::search_error;
359 *sort_search_return = \&Vend::Search::sort_search_return;
360 *spec_check = \&Vend::Search::spec_check;
361 *splice_specs = \&Vend::Search::splice_specs;