# ==================================================================== # Gossamer Threads Module Library - http://gossamer-threads.com/ # # GT::SQL::Search::STH # Author: Aki Mimoto # CVS Info : 087,071,086,086,085 # $Id: STH.pm,v 1.25 2004/08/28 03:53:46 jagerman Exp $ # # Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. # ==================================================================== # package GT::SQL::Search::STH; #-------------------------------------------------------------------------------- use strict; use vars qw/@ISA $ATTRIBS $VERSION $DEBUG $ERRORS $ERROR_MESSAGE /; use GT::Base; @ISA = ('GT::Base'); $ATTRIBS = { '_debug' => 0, 'sth' => undef, 'results' => {}, 'db' => undef, 'table' => undef, 'index' => 0, 'order' => [], 'sb' => 'score', 'so' => '', 'score_col' => 'SCORE', 'score_sort'=> 0, 'nh' => 0, 'mh' => 0 }; $ERROR_MESSAGE = 'GT::SQL'; $ERRORS = { BADSB => 'Invalid character found in so: "%s"', }; sub init { #-------------------------------------------------------------------------------- my $self = shift; # setup the options $self->set(@_); # correct a few of the values --$self->{nh} if $self->{nh}; my $sth; my $results = $self->{results}; $self->{rows} = scalar( $results ? keys %{$results} : 0 ); # if we have asked to have sorting by another column (non score), create the part of the query that handles taht $self->debug( "Setting up sorting for GT::SQL::Search::STH" ) if ($self->{_debug}); my $sb; # clean up the sort by columns. unless ($self->{'score_sort'}) { $sb = GT::SQL::Search::Base::Search->clean_sb($self->{sb}, $self->{so}); } # setup the max hits and the offsets $self->{index} = $self->{nh} * $self->{mh} || 0; $self->{max_index} = $self->{index} + $self->{mh} - 1; # index + mh is the first record which should not be returned. if ( $self->{max_index} > $self->{rows} ) { $self->{max_index} = $self->{rows}; $self->{rows} = $self->{rows} - $self->{index}; $self->{rows} < 0 ? $self->{rows} = 0 : 0; } else { $self->{rows} = $self->{mh}; } # if we are sorting by another column, handle that if ( $sb and (keys %{$self->{results}})) { my ( $table, $pk ) = $self->_table_info(); my ( $query, $where, $st, $limit ); $where = qq!$pk in(! . join( ",", keys %{$self->{results}}) . ')'; $self->{mh} and $limit = qq!LIMIT $self->{index}, $self->{rows}!; $query = qq! SELECT $pk FROM $table WHERE $where $sb $limit !; $self->debug( "Row fetch query: $query" ) if ($self->{_debug}); $sth = $self->{table}->{driver}->prepare( $query ); $sth->execute(); # fix the counts $self->{index} = 0; $self->{max_hits} = $self->{rows}; # now return them my $order = $sth->fetchall_arrayref(); $sth->finish(); $self->{'order'} = [ map { $_->[0] } @{$order} ]; } else { $self->{'order'} = [ sort { ( $results->{$b} || return 0 ) <=> ( $results->{$a} || 0 ) } keys %{$results} ]; $self->debug_dumper( "Results will be presented in the following order: ", $self->{'order'} ) if ($self->{_debug}); } } sub cache_results { #-------------------------------------------------------------------------------- my $self = shift; my $results = $self->{'results'}; my ($sth, @records, $i, %horder, @order, $in_list); my $table = $self->{table}; my $tname = $table->name(); my ($pk) = $self->{table}->pk; use GT::SQL::Condition; # we know what we're doing here so shut off warns (complains about uninit'd values in range # if thee aren't enough elements in the order array) my $w = $^W; $^W = 0; @order = grep $_, @{$self->{'order'} || []}[$self->{index}..$self->{max_index}] or return []; $^W = $w; $i = 0; %horder = ( map { ( $_ => $i++) } @order ); $in_list = join ( ",", @order ); my $query = qq| SELECT * FROM $tname WHERE $pk IN($in_list) |; # the following is left commented out as... # if $tbl->select is used $table->hits() will not # return an accurate count of the number of all the hits. instead, will return # a value up to mh. $tbl->hits() is important because the value is used # in toolbar calculations # # $sth = $table->select( GT::SQL::Condition->new( $pk, 'IN', \"($in_list)" ) ); $sth = $table->do_query( $query ); while ( my $href = $sth->fetchrow_hashref() ) { $records[$horder{$href->{$pk}}] = \%$href } return \@records; } sub fetchrow_array { #-------------------------------------------------------------------------------- return @{ $_[0]->fetchrow_arrayref() || [] }; } sub fetchrow_arrayref { #-------------------------------------------------------------------------------- my $self = shift; my $records = $self->{cache} ||= $self->cache_results; my $href = shift @$records or return; return $self->_hash_to_array($href); } sub fetchrow_hashref { #-------------------------------------------------------------------------------- my $self = shift; my $results = $self->{'results'}; my $records = $self->{cache} ||= $self->cache_results; my $table = $self->{table}; my ($pk) = $self->{table}->pk; my $href = shift @$records or return; $href->{$self->{'score_col'}} = ( $self->{score} = $results->{$href->{$pk}} ); return $href; } sub fetchall_hashref { #-------------------------------------------------------------------------------- my $self = shift; my @results; while (my $res = $self->fetchrow_hashref) { push @results, $res; } return \@results; } sub fetchall_list { #-------------------------------------------------------------------------------- return { map { @$_ } @{shift->fetchall_arrayref} } } sub fetchall_arrayref { #-------------------------------------------------------------------------------- my $self = shift; $self->{order} or return []; my $results = $self->{results}; my ($pk) = $self->{table}->pk; my $scol = $self->{score_col}; if (!$self->{allref_cache}) { $self->{allref_cache} ||= $self->cache_results; for my $i ( 0 .. $#{$self->{allref_cache}} ) { my $element = $self->{allref_cache}->[$i]; if ( $_[0] eq 'HASH' ) { $element->{$scol} = $results->{$element->{$pk}}; } else { $element->{$scol} = $self->_hash_to_array( $element->{$scol} ); } }; } my $records = $self->{allref_cache}; return $records; } sub score { #-------------------------------------------------------------------------------- my $self = shift; return $self->{score}; } sub _hash_to_array { #-------------------------------------------------------------------------------- my $self = shift; my $href = shift or return; my $results = $self->{'results'}; my $table = $self->{table}; my $cols = $table->cols(); my $ordercols = ( $self->{ordercols} ||= [ sort { $cols->{$a}->{pos} <=> $cols->{$b}->{pos} } keys %$cols ] ); my ($pk) = $self->{table}->pk; my $aref = [ map { $href->{$_} } @$ordercols, ( $self->{score} = $results->{$href->{$pk}} ) ]; return $aref; } sub rows { #-------------------------------------------------------------------------------- my $self = shift; return $self->{rows}; } sub _table_info { #-------------------------------------------------------------------------------- my $self = shift; my $table = $self->{table}->name() or return $self->error('NOSCHEMA', 'FATAL'); my ($pk) = $self->{table}->pk; return ( $table, $pk ); } sub DESTROY { #-------------------------------------------------------------------------------- my $self = shift; $self->{'sth'} and $self->{'sth'}->finish(); } sub debug_dumper { #-------------------------------------------------------------------------------- # calls debug but also dumps all the messages my $self = shift; my $message = shift; my $level = ref $_[0] ? 1 : shift; if ( $self->{_debug} >= $level ) { require GT::Dumper; $self->debug( $message . join( "", map { GT::Dumper::Dumper($_) } @_ )) if ($self->{_debug}); } } 1;