First pass at adding key files
This commit is contained in:
		@@ -0,0 +1,25 @@
 | 
			
		||||
# ====================================================================
 | 
			
		||||
# Gossamer Threads Module Library - http://gossamer-threads.com/
 | 
			
		||||
#
 | 
			
		||||
#   GT::SQL::Search::NONINDEXED::Indexer
 | 
			
		||||
#   Author: Aki Mimoto
 | 
			
		||||
#   CVS Info : 087,071,086,086,085      
 | 
			
		||||
#   $Id: Indexer.pm,v 1.3 2004/01/13 01:35:20 jagerman Exp $
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
 | 
			
		||||
# ====================================================================
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
package GT::SQL::Search::NONINDEXED::Indexer;
 | 
			
		||||
#--------------------------------------------------------------------------------
 | 
			
		||||
    use strict;
 | 
			
		||||
    use vars qw/@ISA $DEBUG/;
 | 
			
		||||
    use GT::SQL::Search::Base::Indexer;
 | 
			
		||||
    @ISA = qw/ GT::SQL::Search::Base::Indexer /;
 | 
			
		||||
 | 
			
		||||
sub load {
 | 
			
		||||
    shift;
 | 
			
		||||
    return GT::SQL::Search::NONINDEXED::Indexer->new(@_)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
@@ -0,0 +1,257 @@
 | 
			
		||||
# ==================================================================
 | 
			
		||||
# Gossamer Threads Module Library - http://gossamer-threads.com/
 | 
			
		||||
#
 | 
			
		||||
#   GT::SQL::Search::NONINDEXED::Search
 | 
			
		||||
#   Author  : Alex Krohn
 | 
			
		||||
#   CVS Info : 087,071,086,086,085      
 | 
			
		||||
#   $Id: Search.pm,v 1.30 2006/08/09 06:58:39 brewt Exp $
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
 | 
			
		||||
# ==================================================================
 | 
			
		||||
#
 | 
			
		||||
# Description:
 | 
			
		||||
#   Nonindex search system
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
package GT::SQL::Search::NONINDEXED::Search;
 | 
			
		||||
# ==================================================================
 | 
			
		||||
    use strict;
 | 
			
		||||
    use vars qw/@ISA $ATTRIBS $VERSION $DEBUG/;
 | 
			
		||||
    use GT::SQL::Search::Base::Search;
 | 
			
		||||
    use GT::SQL::Condition;
 | 
			
		||||
    @ISA = qw( GT::SQL::Search::Base::Search );
 | 
			
		||||
 | 
			
		||||
    $DEBUG      = 0;
 | 
			
		||||
    $VERSION    = sprintf "%d.%03d", q$Revision: 1.30 $ =~ /(\d+)\.(\d+)/;  
 | 
			
		||||
    $ATTRIBS    = {
 | 
			
		||||
# parse based on latin characters
 | 
			
		||||
        latin_query_parse => 0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
sub load {
 | 
			
		||||
    shift;
 | 
			
		||||
    return GT::SQL::Search::NONINDEXED::Search->new(@_)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub query {
 | 
			
		||||
#--------------------------------------------------------------------------------
 | 
			
		||||
# Returns a sth based on a query
 | 
			
		||||
#
 | 
			
		||||
# Options:
 | 
			
		||||
#        - paging
 | 
			
		||||
#           mh            : max hits
 | 
			
		||||
#           nh            : number hit (or page of hits)
 | 
			
		||||
#
 | 
			
		||||
#        - searching
 | 
			
		||||
#           ww            : whole word
 | 
			
		||||
#           ma            : 1 => OR match, 0 => AND match, undefined => QUERY
 | 
			
		||||
#           substring     : search for substrings of words
 | 
			
		||||
#           bool          : 'and' => and search, 'or' => or search, '' => regular query
 | 
			
		||||
#           query         : the string of things to ask for
 | 
			
		||||
#
 | 
			
		||||
#        - filtering
 | 
			
		||||
#           field_name    : value       # Find all rows with field_name = value
 | 
			
		||||
#           field_name    : ">value"    # Find all rows with field_name > value.
 | 
			
		||||
#           field_name    : "<value"    # Find all rows with field_name < value.
 | 
			
		||||
#           field_name-gt : value       # Find all rows with field_name > value.
 | 
			
		||||
#           field_name-lt : value       # Find all rows with field_name < value.
 | 
			
		||||
#
 | 
			
		||||
# Parameters:
 | 
			
		||||
#        ( $CGI ) : a single cgi object
 | 
			
		||||
#        ( $HASH ) : a hash of the parameters
 | 
			
		||||
#
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
 | 
			
		||||
# find out what sort of a parameter we're dealing with
 | 
			
		||||
    my $input = $self->common_param(@_);
 | 
			
		||||
 | 
			
		||||
# add additional parameters if required
 | 
			
		||||
    foreach my $parameter ( keys %{$ATTRIBS} ) {
 | 
			
		||||
        if ( not exists $input->{$parameter} ) {
 | 
			
		||||
            $input->{$parameter} = $self->{$parameter};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
# parse query
 | 
			
		||||
    $self->debug( "Search Query: $$input{query}", 1 ) if ($self->{_debug});
 | 
			
		||||
    my ( $query, $rejected ) = $self->_parse_query_string( $input->{'query'} );
 | 
			
		||||
 | 
			
		||||
    $self->{rejected_keywords} = $rejected;
 | 
			
		||||
 | 
			
		||||
# setup the additional input parameters
 | 
			
		||||
    $query = $self->_preset_options( $query, $input );
 | 
			
		||||
 | 
			
		||||
    $self->debug( "Set the pre-options: ", $query ) if ($self->{_debug});
 | 
			
		||||
 | 
			
		||||
# now sort into distinct buckets
 | 
			
		||||
    my $buckets = GT::SQL::Search::Base::Search::_create_buckets( $query );
 | 
			
		||||
    $self->debug_dumper( "Created Buckets for querying: ", $buckets ) if ($self->{_debug});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    require GT::SQL::Condition;
 | 
			
		||||
    my $query_condition = new GT::SQL::Condition;
 | 
			
		||||
 | 
			
		||||
# now handle the separate possibilities
 | 
			
		||||
# the union
 | 
			
		||||
    my $union_cond     = $self->_get_condition( $buckets->{keywords},        $buckets->{phrases} );
 | 
			
		||||
    $query_condition->add(GT::SQL::Condition->new(@$union_cond, 'OR')) if $union_cond;
 | 
			
		||||
# the intersect
 | 
			
		||||
    my $intersect_cond = $self->_get_condition( $buckets->{keywords_must},   $buckets->{phrases_must} );
 | 
			
		||||
    $query_condition->add(GT::SQL::Condition->new(@$intersect_cond)) if $intersect_cond;
 | 
			
		||||
 | 
			
		||||
# the disjoin
 | 
			
		||||
    my $disjoin_cond   = $self->_get_condition( $buckets->{keywords_cannot}, $buckets->{phrases_cannot} );
 | 
			
		||||
    $query_condition->add(GT::SQL::Condition->new(@$disjoin_cond, 'OR')->not) if $disjoin_cond;
 | 
			
		||||
 | 
			
		||||
# now handle filters
 | 
			
		||||
    my $cols    = $self->{'table'}->cols();
 | 
			
		||||
    my %filters = map {
 | 
			
		||||
        (my $column = $_) =~ s/-[lg]t$//;
 | 
			
		||||
        exists $cols->{$column}
 | 
			
		||||
            ? ($_ => $input->{$_})
 | 
			
		||||
            : ()
 | 
			
		||||
    } keys %{$input};
 | 
			
		||||
 | 
			
		||||
# if there was no query nor filter return nothing.
 | 
			
		||||
    keys %$query or keys %filters or return $self->sth({});
 | 
			
		||||
 | 
			
		||||
    if (keys %filters) {
 | 
			
		||||
        $self->debug( "Creating Filters: ", \%filters ) if ($self->{_debug});
 | 
			
		||||
        $self->_add_filters( \%filters );
 | 
			
		||||
        $query_condition = GT::SQL::Condition->new( keys %$query ? $query_condition : (), $self->{filter} );
 | 
			
		||||
    }
 | 
			
		||||
    elsif ($self->{filter} and keys %{$self->{filter}} ) {
 | 
			
		||||
        $self->debug( "Filtering results", $self->{filter} ) if ($self->{_debug});
 | 
			
		||||
        $query_condition = GT::SQL::Condition->new( keys %$query ? $query_condition : (), $self->{filter} );
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        $self->debug( "No filters being used.") if ($self->{_debug});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
# now this query should probably clear the filters once it's been used, so i'll do that here
 | 
			
		||||
    $self->{filter} = undef;
 | 
			
		||||
 | 
			
		||||
    my $tbl  = $self->{table};
 | 
			
		||||
    my ($pk) = $tbl->pk;
 | 
			
		||||
 | 
			
		||||
# now run through a callback function if needed.
 | 
			
		||||
    if ($self->{callback}) {
 | 
			
		||||
 | 
			
		||||
# Warning: this slows things a heck of a lot.
 | 
			
		||||
        unless (ref $self->{callback} and ref $self->{callback} eq 'CODE') {
 | 
			
		||||
            return $self->error ('BADARGS', 'FATAL', "callback '$self->{callback}' must be a code ref!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        my $sth     = $tbl->select( [ $pk ], $query_condition );
 | 
			
		||||
        my $results = {};
 | 
			
		||||
        while (my $result = $sth->fetchrow) {
 | 
			
		||||
            $results->{$result} = undef;
 | 
			
		||||
        }
 | 
			
		||||
        $self->debug_dumper("Running results through callback. Had: " . scalar (keys %$results) . " results.", $results) if ($self->{_debug});
 | 
			
		||||
        $results = $self->{callback}->($self, $results);
 | 
			
		||||
        $self->debug_dumper("New result set: " . scalar (keys %$results) . " results.", $results) if ($self->{_debug});
 | 
			
		||||
        $self->{rows} = scalar($results ? keys %{$results} : ());
 | 
			
		||||
 | 
			
		||||
        return $self->sth( $results );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
# and now create a search sth object to handle all this
 | 
			
		||||
    $input->{nh} = (defined $input->{nh} and $input->{nh} =~ /^(\d+)$/) ? $1 : 1;
 | 
			
		||||
    $input->{mh} = (defined $input->{mh} and $input->{mh} =~ /^(\d+)$/) ? $1 : 25;
 | 
			
		||||
    $input->{so} = (defined $input->{so} and $input->{so} =~ /^(asc(?:end)?|desc(?:end)?)$/i) ? $1 : '';
 | 
			
		||||
 | 
			
		||||
# check that sb is not dangerous
 | 
			
		||||
    my $sb = $self->clean_sb($input->{sb}, $input->{so});
 | 
			
		||||
 | 
			
		||||
    my $offset = ( $input->{nh} - 1 ) * $input->{mh};
 | 
			
		||||
    $tbl->select_options($sb) if ($sb);
 | 
			
		||||
    $tbl->select_options("LIMIT $offset, $input->{mh}");
 | 
			
		||||
    my $sth = $tbl->select( $query_condition ) or return;
 | 
			
		||||
 | 
			
		||||
# so how many hits did we get?
 | 
			
		||||
    $self->{rows} = $sth->rows();
 | 
			
		||||
    if (($input->{nh} > 1) or ($self->{rows} == $input->{mh})) {
 | 
			
		||||
        $self->{rows} = $tbl->count($query_condition);
 | 
			
		||||
    }
 | 
			
		||||
    return $sth;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _get_condition {
 | 
			
		||||
#-------------------------------------------------------------------------------
 | 
			
		||||
    my ( $self, $keywords, $phrases ) = @_;
 | 
			
		||||
 | 
			
		||||
    my @list = ( keys %$keywords, keys %$phrases );
 | 
			
		||||
 | 
			
		||||
    my $tbl     = $self->{table} or return $self->error( 'NODRIVER', 'FATAL' );
 | 
			
		||||
    my @cond    = ();
 | 
			
		||||
    my %tmp     = $tbl->weight();
 | 
			
		||||
    my @weights = keys  %tmp or return;
 | 
			
		||||
    foreach my $element ( @list ) {
 | 
			
		||||
        my @where = ();
 | 
			
		||||
        foreach my $cols ( @weights ) {
 | 
			
		||||
            push @where, [$cols, 'LIKE', "%$element%"]; # Condition does quoting by default.
 | 
			
		||||
        }
 | 
			
		||||
        push @cond, GT::SQL::Condition->new(@where, 'OR');
 | 
			
		||||
    }
 | 
			
		||||
    @cond or return;
 | 
			
		||||
 | 
			
		||||
    return \@cond;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _parse_query_string {
 | 
			
		||||
#------------------------------------------------------------
 | 
			
		||||
# Parses a query string '+foo -"bar this" alpha' into a hash of
 | 
			
		||||
# words and modes.
 | 
			
		||||
#
 | 
			
		||||
    my ($self, $text) = @_;
 | 
			
		||||
    my %modes = (
 | 
			
		||||
        '+' => 'must',
 | 
			
		||||
        '-' => 'cannot',
 | 
			
		||||
        '<' => 'greater',
 | 
			
		||||
        '>' => 'less'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
# Latin will break up on actual words and punctuation.
 | 
			
		||||
    if ($self->{latin_query_parse}) {
 | 
			
		||||
        return $self->SUPER::_parse_query_string( $text );
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        my $words = {};
 | 
			
		||||
        my @terms;
 | 
			
		||||
        my $i = 0;
 | 
			
		||||
        foreach my $term (split /"/, $text) {
 | 
			
		||||
            push @terms, ($i++ % 2 ? $term : split ' ', $term);
 | 
			
		||||
        }
 | 
			
		||||
        for (my $i = 0; $i < @terms; $i++) {
 | 
			
		||||
            my $word = $terms[$i];
 | 
			
		||||
            $word =~ s/^\s*|\s*$//g;
 | 
			
		||||
            next if ($word eq '');
 | 
			
		||||
            if ($i < $#terms) {
 | 
			
		||||
                ($word eq '-') and ($word = '-' . $terms[++$i]);
 | 
			
		||||
                ($word eq '+') and ($word = '+' . $terms[++$i]);
 | 
			
		||||
            }
 | 
			
		||||
            $word         =~ s/^([<>+-])//;
 | 
			
		||||
            my $mode      = ($1 and $modes{$1} or 'can');
 | 
			
		||||
            my $substring = ($word =~ s/\*$//) || 0;
 | 
			
		||||
            if ($word =~ /\s/) {
 | 
			
		||||
                $words->{$word} = {
 | 
			
		||||
                    mode      => $mode,
 | 
			
		||||
                    phrase    => 1,
 | 
			
		||||
                    substring => $substring,
 | 
			
		||||
                    keyword   => 0,
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            elsif ($word) {
 | 
			
		||||
                $words->{$word} = {
 | 
			
		||||
                    mode      => $mode,
 | 
			
		||||
                    phrase    => 0,
 | 
			
		||||
                    substring => $substring,
 | 
			
		||||
                    keyword   => 1,
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $words;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
		Reference in New Issue
	
	Block a user