/[Grep]/lib/Grep/Search/KinoSearch.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /lib/Grep/Search/KinoSearch.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

lib/Grep/Search.pm revision 110 by dpavlin, Wed Mar 14 20:02:19 2007 UTC lib/Grep/Search/KinoSearch.pm revision 190 by dpavlin, Fri May 23 21:05:42 2008 UTC
# Line 1  Line 1 
1  package Grep::Search;  package Grep::Search::KinoSearch;
2    
3  use strict;  use strict;
4  use warnings;  use warnings;
 use base qw( Class::Accessor Jifty::Object );  
 Grep::Search->mk_accessors( qw( analyzer store writer create index_path ) );  
5    
6  use Data::Dump qw/dump/;  use Data::Dump qw/dump/;
7  use Lucene;  use KinoSearch::InvIndexer;
8    use KinoSearch::Searcher;
9  use Jifty::Util;  use Jifty::Util;
10    
11  my $debug = 0;  my $debug = 1;
12    
13  =head1 NAME  =head1 NAME
14    
15  Grep::Search - full text search  Grep::Search::KinoSearch - full text search using L<KinoSearch>
16    
17  =head1 METHODS  =head1 METHODS
18    
19  =head2 new  =head2 invindexer
20    
21    my $search = Grep::Search->new();  Accessor to call any method defined on L<KinoSearch::InvIndexer>
22    
23  =cut    $search->invindexer->delete_by_term( 'id', 42 );
24    
25  sub new {  =cut
         my $class = shift;          
         my $self = $class->SUPER::new(@_);  
26    
27          my $index_path = Jifty::Util->app_root . '/var/lucene';  our $indexes;
28    
29          $self->index_path( $index_path );  sub invindexer {
30            my $self = shift;
31            my $invindexer;
32            my $index_path = Jifty::Util->app_root . '/var/invindex';
33    
34          if (! -e "$index_path/segments") {          if ( $invindexer = $indexes->{$index_path} ) {
35                  $self->create( 1 );                  $self->log->debug("Using cached index $index_path");
                 $self->log->debug("Creating new index $index_path");  
36          } else {          } else {
37                  $self->create( 0 );                  if ( $self->create || -e $index_path ) {
38                  $self->log->debug("Opening index: $index_path");                          $self->log->debug("Creating new index $index_path");
39                            $invindexer = KinoSearch::InvIndexer->new( invindex => Grep::Search::KinoSearch::Schema->clobber( $index_path ) )
40                                    or die "can't create index $index_path: $!";
41                            $self->create( 0 );
42                    } else {
43                            $self->log->debug("Opening index: $index_path");
44                            $invindexer = KinoSearch::InvIndexer->new( invindex => Grep::Search::KinoSearch::Schema->open( $index_path ) )
45                                    or die "can't open index $index_path: $!";
46                    }
47                    $indexes->{$index_path} = $invindexer;
48          }          }
49    
50          $self->analyzer( new Lucene::Analysis::Standard::StandardAnalyzer() );          return $invindexer;
         $self->log->debug($self->analyzer . " created");  
   
         $self->store( Lucene::Store::FSDirectory->getDirectory( $index_path, $self->create ) );  
         $self->log->debug($self->store, " created");  
   
         return $self;  
51  }  }
52    
   
53  =head2 add  =head2 add
54    
55    $search->add( $record, $owner_id );    $search->add( $doc_hash );
56    
57  =cut  =cut
58    
59  sub add {  sub add {
60          my $self = shift;          my $self = shift;
61            my $doc = shift;
62          my $i = shift or die "no record to add";          $self->invindexer->add_doc( $doc );
63          my $uid = shift;          return 1;
   
         die "record not Jifty::Record but ", ref $i unless ($i->isa('Jifty::Record'));  
   
         my $pk = { $i->primary_keys };  
   
         my $doc = new Lucene::Document;  
   
         my @columns = map { $_->name } $i->columns;  
   
         foreach my $c ( @columns ) {  
   
                 my $v = $i->$c;  
   
                 if ( ref($v) ne '' ) {  
   
                         foreach my $f_c ( qw/id name title/ ) {  
                                 if ( $i->$c->can( $f_c ) ) {  
                                         my $f_v = $i->$c->$f_c || $i->$c->{values}->{ $f_c };  
                                         my $col = $c . '_' . $f_c;  
                                         if ( $f_v ) {  
                                                 warn "  # $col = $f_v\n" if ($debug);  
                                                 $doc->add(Lucene::Document::Field->Text( $col, $f_v ));  
                                         } else {  
                                                 warn "  . $col is NULL\n" if ($debug);  
                                         }  
                                 }  
                         }  
   
                         if ($v->isa('Jifty::DateTime')) {  
                                 warn "  d $c = $v\n" if ($debug);  
                                 $doc->add(Lucene::Document::Field->Keyword( $c, "$v" ));  
                         } else {  
                                 warn "  s $c = $v [",ref($v),"]\n" if ($debug);  
                         }  
                         next;  
                 }  
   
                 next if (! defined($v) || $v eq '');  
   
                 $v =~ s/<[^>]+>/ /gs;  
   
                 if ( defined( $pk->{$c} ) ) {  
                         $doc->add(Lucene::Document::Field->Keyword( $c, $v ));  
                         warn "  * $c = $v\n" if ($debug);  
                 } else {  
                         $doc->add(Lucene::Document::Field->Text( $c, $v ));  
                         warn "  + $c = ", $self->snippet( 50, $v ), "\n" if ($debug);  
                 }  
         }  
   
         # add _owner_id to speed up filtering of search results  
         $uid ||= Jifty->web->current_user->id;  
         $doc->add(Lucene::Document::Field->Keyword( '_owner_id', $uid ));  
   
         if (! defined( $self->writer )) {  
                 $self->writer( new Lucene::Index::IndexWriter( $self->store, $self->analyzer, $self->create ) );  
                 $self->log->debug($self->writer, " created");  
         }  
   
         $self->writer->addDocument($doc);  
   
         $self->log->debug("added ", $i->id, " for user $uid to index");  
64  }  }
65    
66  =head2 collection  =head2 search
67    
68    my $ItemCollection = $search->collection( 'search query' );    my $fetch_hit_coderef = $self->search('search query');
69    
70  =cut  =cut
71    
72  sub collection {  sub search {
73          my $self = shift;          my $self = shift;
74    
75          my $q = shift or die "no q?";          my $q = shift or die "no q?";
76    
77          return if ( $self->create );          my $index_path = Jifty::Util->app_root . '/var/invindex';
78            my $searcher = KinoSearch::Searcher->new(
79          my $searcher = new Lucene::Search::IndexSearcher($self->store);                  invindex => Grep::Search::KinoSearch::Schema->open( $index_path ), );
80          $self->log->debug("$searcher created");          $self->log->debug("$searcher created");
         my $parser = new Lucene::QueryParser("content", $self->analyzer);  
         $self->log->debug("$parser created");  
81    
82          my $full_q = "($q) AND _owner_id:" . Jifty->web->current_user->id;          my $full_q = "($q)";
83    
84          my $query = $parser->parse( $full_q );          my $uid = Jifty->web->current_user->id;
85            $full_q .= ' AND _owner_id:' . $uid if (defined $uid);
86    
87          $self->log->debug("searching for '$q' using ", $query->toString);          $self->log->debug("searching for '$q' using $full_q");
88    
89          my $hits = $searcher->search($query);          my $query_parser = KinoSearch::QueryParser->new(
90          my $num_hits = $hits->length();                  schema => Grep::Search::KinoSearch::Schema->new,
91                    fields => [ qw/ title link content summary category author / ],
92          $self->log->debug("found $num_hits results");          );
93            $query_parser->set_heed_colons(1);       # enable field:value AND/OR/NOT syntax
94          my $collection = Grep::Model::ItemCollection->new();          my $query = $query_parser->parse( $full_q );
95            my $hits = $searcher->search(
96          my @results;                  query           => $query,
97    #               offset          => $offset,
98          for ( my $i = 0; $i < $num_hits; $i++ ) {  #               num_wanted      => $hits_per_page,
99            );
100                  my $doc = $hits->doc( $i );  
101            $self->hits( $hits->total_hits );
102                  my $score = $hits->score($i);  
103                  my $title = $doc->get("title");          return sub {
104                  my $id = $doc->get("id");                  return $hits->fetch_hit;
105            };
                 $self->log->debug("result $i $score $title");  
   
                 my $item = Grep::Model::Item->new();  
                 my ($ok,$msg) = $item->load_by_cols( id => $id );  
   
                 if ( $ok ) {  
                         $collection->add_record( $item );  
                 } else {  
                         warn "can't load item $id\n";  
                 }  
   
         }  
   
         undef $hits;  
         undef $query;  
         undef $parser;  
         $searcher->close;  
         undef $searcher;  
   
         return $collection;  
106  }  }
107    
108  =head2 finish  =head2 finish
# Line 195  sub collection { Line 113  sub collection {
113    
114  sub finish {  sub finish {
115          my $self = shift;          my $self = shift;
116          if ($self->writer) {          if ($self->invindexer) {
117                  $self->log->debug("closing index");                  $self->log->debug("closing index");
118                  $self->writer->close;                  $self->invindexer->finish;
119          }          }
120    
         $self->writer( undef );  
         $self->store( undef );  
         $self->create( undef );  
         $self->analyzer( undef );  
   
121          $self->log->debug("finish");          $self->log->debug("finish");
122    
123          return;          undef $self;
 }  
   
 =for TODO  
124    
125  sub _signal {          return 1;
         my $s = shift;  
         warn "catched SIG $s\n";  
         finish();  
         exit(0);  
126  }  }
127    
128  $SIG{'__DIE__'} = \&_signal;  package Grep::Search::KinoSearch::KeywordField;
129  $SIG{'INT'} = \&_signal;  use base qw( KinoSearch::Schema::FieldSpec );
130  $SIG{'QUIT'} = \&_signal;  sub analyzed { 0 }
131    #sub indexed { 1 }
132    #sub stored { 1 }
133    sub vectorized { 0 }
134    
135  =cut  package Grep::Search::KinoSearch::Schema;
136    
137  =head2 snippet  =head1 NAME
138    
139    my $short = $self->snippet( 50, $text );  Grep::Search::KinoSearch::Schema - schema definition for full-text search
140    
141  =cut  =cut
142    
143  sub snippet {  use base 'KinoSearch::Schema';
144          my $self = shift;  use KinoSearch::Analysis::PolyAnalyzer;
145    
146          my $len = shift or die "no len?";  our %fields = (
147          my $m = join(" ", @_);          id                              => 'Grep::Search::KinoSearch::KeywordField',
148    
149          $m =~ s/\s+/ /gs;          in_feed_id              => 'Grep::Search::KinoSearch::KeywordField',
150            in_feed_url             => 'Grep::Search::KinoSearch::KeywordField',
151            in_feed_title   => 'KinoSearch::Schema::FieldSpec',
152            in_feed_owner   => 'Grep::Search::KinoSearch::KeywordField',
153            in_feed_created_on      => 'Grep::Search::KinoSearch::KeywordField',
154    
155          if (length($m) > $len) {          title                   => 'KinoSearch::Schema::FieldSpec',
156                  return substr($m,0,$len) . '...';          link                    => 'Grep::Search::KinoSearch::KeywordField',
157          } else {          content                 => 'KinoSearch::Schema::FieldSpec',
158                  return $m;          summary                 => 'KinoSearch::Schema::FieldSpec',
159          }          category                => 'KinoSearch::Schema::FieldSpec',
160            author                  => 'KinoSearch::Schema::FieldSpec',
161            created_on              => 'Grep::Search::KinoSearch::KeywordField',
162            last_update             => 'Grep::Search::KinoSearch::KeywordField',
163    
164            _owner_id               => 'Grep::Search::KinoSearch::KeywordField',
165    );
166    
167    sub analyzer {
168            return KinoSearch::Analysis::PolyAnalyzer->new( language => 'en' );
169  }  }
170    
171  1;  1;

Legend:
Removed from v.110  
changed lines
  Added in v.190

  ViewVC Help
Powered by ViewVC 1.1.26