/[Search-Estraier]/trunk/lib/Search/Estraier.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 /trunk/lib/Search/Estraier.pm

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

revision 61 by dpavlin, Sat Jan 7 01:21:28 2006 UTC revision 111 by dpavlin, Tue Feb 21 15:41:57 2006 UTC
# Line 4  use 5.008; Line 4  use 5.008;
4  use strict;  use strict;
5  use warnings;  use warnings;
6    
7  our $VERSION = '0.01';  our $VERSION = '0.04_3';
8    
9  =head1 NAME  =head1 NAME
10    
# Line 12  Search::Estraier - pure perl module to u Line 12  Search::Estraier - pure perl module to u
12    
13  =head1 SYNOPSIS  =head1 SYNOPSIS
14    
15    use Search::Estraier;  =head2 Simple indexer
16    my $est = new Search::Estraier();  
17            use Search::Estraier;
18    
19            # create and configure node
20            my $node = new Search::Estraier::Node(
21                    url => 'http://localhost:1978/node/test',
22                    user => 'admin',
23                    passwd => 'admin'
24            );
25    
26            # create document
27            my $doc = new Search::Estraier::Document;
28    
29            # add attributes
30            $doc->add_attr('@uri', "http://estraier.gov/example.txt");
31            $doc->add_attr('@title', "Over the Rainbow");
32    
33            # add body text to document
34            $doc->add_text("Somewhere over the rainbow.  Way up high.");
35            $doc->add_text("There's a land that I heard of once in a lullaby.");
36    
37            die "error: ", $node->status,"\n" unless (eval { $node->put_doc($doc) });
38    
39    =head2 Simple searcher
40    
41            use Search::Estraier;
42    
43            # create and configure node
44            my $node = new Search::Estraier::Node(
45                    url => 'http://localhost:1978/node/test',
46                    user => 'admin',
47                    passwd => 'admin',
48                    croak_on_error => 1,
49            );
50    
51            # create condition
52            my $cond = new Search::Estraier::Condition;
53    
54            # set search phrase
55            $cond->set_phrase("rainbow AND lullaby");
56    
57            my $nres = $node->search($cond, 0);
58    
59            if (defined($nres)) {
60                    print "Got ", $nres->hits, " results\n";
61    
62                    # for each document in results
63                    for my $i ( 0 ... $nres->doc_num - 1 ) {
64                            # get result document
65                            my $rdoc = $nres->get_doc($i);
66                            # display attribte
67                            print "URI: ", $rdoc->attr('@uri'),"\n";
68                            print "Title: ", $rdoc->attr('@title'),"\n";
69                            print $rdoc->snippet,"\n";
70                    }
71            } else {
72                    die "error: ", $node->status,"\n";
73            }
74    
75  =head1 DESCRIPTION  =head1 DESCRIPTION
76    
# Line 25  or Hyper Estraier development files on t Line 82  or Hyper Estraier development files on t
82  It is implemented as multiple packages which closly resamble Ruby  It is implemented as multiple packages which closly resamble Ruby
83  implementation. It also includes methods to manage nodes.  implementation. It also includes methods to manage nodes.
84    
85    There are few examples in C<scripts> directory of this distribution.
86    
87  =cut  =cut
88    
89  =head1 Inheritable common methods  =head1 Inheritable common methods
# Line 41  Remove multiple whitespaces from string, Line 100  Remove multiple whitespaces from string,
100  =cut  =cut
101    
102  sub _s {  sub _s {
103          my $text = $_[1] || return;          my $text = $_[1];
104            return unless defined($text);
105          $text =~ s/\s\s+/ /gs;          $text =~ s/\s\s+/ /gs;
106          $text =~ s/^\s+//;          $text =~ s/^\s+//;
107          $text =~ s/\s+$//;          $text =~ s/\s+$//;
# Line 106  sub new { Line 166  sub new {
166                          } elsif ($line =~ m/^$/) {                          } elsif ($line =~ m/^$/) {
167                                  $in_text = 1;                                  $in_text = 1;
168                                  next;                                  next;
169                          } elsif ($line =~ m/^(.+)=(.+)$/) {                          } elsif ($line =~ m/^(.+)=(.*)$/) {
170                                  $self->{attrs}->{ $1 } = $2;                                  $self->{attrs}->{ $1 } = $2;
171                                  next;                                  next;
172                          }                          }
173    
174                          warn "draft ignored: $line\n";                          warn "draft ignored: '$line'\n";
175                  }                  }
176          }          }
177    
# Line 269  sub dump_draft { Line 329  sub dump_draft {
329          my $draft;          my $draft;
330    
331          foreach my $attr_name (sort keys %{ $self->{attrs} }) {          foreach my $attr_name (sort keys %{ $self->{attrs} }) {
332                  $draft .= $attr_name . '=' . $self->{attrs}->{$attr_name} . "\n";                  next unless defined(my $v = $self->{attrs}->{$attr_name});
333                    $draft .= $attr_name . '=' . $v . "\n";
334          }          }
335    
336          if ($self->{kwords}) {          if ($self->{kwords}) {
# Line 317  sub delete { Line 378  sub delete {
378    
379  package Search::Estraier::Condition;  package Search::Estraier::Condition;
380    
381  use Carp qw/confess croak/;  use Carp qw/carp confess croak/;
382    
383  use Search::Estraier;  use Search::Estraier;
384  our @ISA = qw/Search::Estraier/;  our @ISA = qw/Search::Estraier/;
# Line 395  sub set_max { Line 456  sub set_max {
456    
457  =head2 set_options  =head2 set_options
458    
459    $cond->set_options( SURE => 1 );    $cond->set_options( 'SURE' );
460    
461      $cond->set_options( qw/AGITO NOIDF SIMPLE/ );
462    
463    Possible options are:
464    
465    =over 8
466    
467    =item SURE
468    
469    check every N-gram
470    
471    =item USUAL
472    
473    check every second N-gram
474    
475    =item FAST
476    
477    check every third N-gram
478    
479    =item AGITO
480    
481    check every fourth N-gram
482    
483    =item NOIDF
484    
485    don't perform TF-IDF tuning
486    
487    =item SIMPLE
488    
489    use simplified query phrase
490    
491    =back
492    
493    Skipping N-grams will speed up search, but reduce accuracy. Every call to C<set_options> will reset previous
494    options;
495    
496    This option changed in version C<0.04> of this module. It's backwards compatibile.
497    
498  =cut  =cut
499    
500  my $options = {  my $options = {
         # check N-gram keys skipping by three  
501          SURE => 1 << 0,          SURE => 1 << 0,
         # check N-gram keys skipping by two  
502          USUAL => 1 << 1,          USUAL => 1 << 1,
         # without TF-IDF tuning  
503          FAST => 1 << 2,          FAST => 1 << 2,
         # with the simplified phrase  
504          AGITO => 1 << 3,          AGITO => 1 << 3,
         # check every N-gram key  
505          NOIDF => 1 << 4,          NOIDF => 1 << 4,
         # check N-gram keys skipping by one  
506          SIMPLE => 1 << 10,          SIMPLE => 1 << 10,
507  };  };
508    
509  sub set_options {  sub set_options {
510          my $self = shift;          my $self = shift;
511          my $option = shift;          my $opt = 0;
512          confess "unknown option" unless ($options->{$option});          foreach my $option (@_) {
513          $self->{options} ||= $options->{$option};                  my $mask;
514                    unless ($mask = $options->{$option}) {
515                            if ($option eq '1') {
516                                    next;
517                            } else {
518                                    croak "unknown option $option";
519                            }
520                    }
521                    $opt += $mask;
522            }
523            $self->{options} = $opt;
524  }  }
525    
526    
# Line 525  sub new { Line 627  sub new {
627          my $self = {@_};          my $self = {@_};
628          bless($self, $class);          bless($self, $class);
629    
630          foreach my $f (qw/uri attrs snippet keywords/) {          croak "missing uri for ResultDocument" unless defined($self->{uri});
                 croak "missing $f for ResultDocument" unless defined($self->{$f});  
         }  
631    
632          $self ? return $self : return undef;          $self ? return $self : return undef;
633  }  }
# Line 642  Return number of documents Line 742  Return number of documents
742    
743    print $res->doc_num;    print $res->doc_num;
744    
745    This will return real number of documents (limited by C<max>).
746    If you want to get total number of hits, see C<hits>.
747    
748  =cut  =cut
749    
750  sub doc_num {  sub doc_num {
# Line 673  sub get_doc { Line 776  sub get_doc {
776    
777  Return specific hint from results.  Return specific hint from results.
778    
779    print $rec->hint( 'VERSION' );    print $res->hint( 'VERSION' );
780    
781  Possible hints are: C<VERSION>, C<NODE>, C<HIT>, C<HINT#n>, C<DOCNUM>, C<WORDNUM>,  Possible hints are: C<VERSION>, C<NODE>, C<HIT>, C<HINT#n>, C<DOCNUM>, C<WORDNUM>,
782  C<TIME>, C<LINK#n>, C<VIEW>.  C<TIME>, C<LINK#n>, C<VIEW>.
# Line 686  sub hint { Line 789  sub hint {
789          return $self->{hints}->{$key};          return $self->{hints}->{$key};
790  }  }
791    
792    =head2 hints
793    
794    More perlish version of C<hint>. This one returns hash.
795    
796      my %hints = $res->hints;
797    
798    =cut
799    
800    sub hints {
801            my $self = shift;
802            return $self->{hints};
803    }
804    
805    =head2 hits
806    
807    Syntaxtic sugar for total number of hits for this query
808    
809      print $res->hits;
810    
811    It's same as
812    
813      print $res->hint('HIT');
814    
815    but shorter.
816    
817    =cut
818    
819    sub hits {
820            my $self = shift;
821            return $self->{hints}->{'HIT'} || 0;
822    }
823    
824  package Search::Estraier::Node;  package Search::Estraier::Node;
825    
# Line 701  use URI::Escape qw/uri_escape/; Line 835  use URI::Escape qw/uri_escape/;
835    
836    my $node = new Search::HyperEstraier::Node;    my $node = new Search::HyperEstraier::Node;
837    
838    or optionally with C<url> as parametar
839    
840      my $node = new Search::HyperEstraier::Node( 'http://localhost:1978/node/test' );
841    
842    or in more verbose form
843    
844      my $node = new Search::HyperEstraier::Node(
845            url => 'http://localhost:1978/node/test',
846            debug => 1,
847            croak_on_error => 1
848      );
849    
850    with following arguments:
851    
852    =over 4
853    
854    =item url
855    
856    URL to node
857    
858    =item debug
859    
860    dumps a B<lot> of debugging output
861    
862    =item croak_on_error
863    
864    very helpful during development. It will croak on all errors instead of
865    silently returning C<-1> (which is convention of Hyper Estraier API in other
866    languages).
867    
868    =back
869    
870  =cut  =cut
871    
872  sub new {  sub new {
# Line 708  sub new { Line 874  sub new {
874          my $self = {          my $self = {
875                  pxport => -1,                  pxport => -1,
876                  timeout => 0,   # this used to be -1                  timeout => 0,   # this used to be -1
                 dnum => -1,  
                 wnum => -1,  
                 size => -1.0,  
877                  wwidth => 480,                  wwidth => 480,
878                  hwidth => 96,                  hwidth => 96,
879                  awidth => 96,                  awidth => 96,
880                  status => -1,                  status => -1,
881          };          };
882    
883          bless($self, $class);          bless($self, $class);
884    
885          my $args = {@_};          if ($#_ == 0) {
886                    $self->{url} = shift;
887            } else {
888                    my $args = {@_};
889    
890                    %$self = ( %$self, @_ );
891    
892          $self->{debug} = $args->{debug};                  warn "## Node debug on\n" if ($self->{debug});
893          warn "## Node debug on\n" if ($self->{debug});          }
894    
895            $self->{inform} = {
896                    dnum => -1,
897                    wnum => -1,
898                    size => -1.0,
899            };
900    
901          $self ? return $self : return undef;          $self ? return $self : return undef;
902  }  }
# Line 1001  Get ID of document specified by URI Line 1176  Get ID of document specified by URI
1176    
1177    my $id = $node->uri_to_id( 'file:///document/uri/42' );    my $id = $node->uri_to_id( 'file:///document/uri/42' );
1178    
1179    This method won't croak, even if using C<croak_on_error>.
1180    
1181  =cut  =cut
1182    
1183  sub uri_to_id {  sub uri_to_id {
1184          my $self = shift;          my $self = shift;
1185          my $uri = shift || return;          my $uri = shift || return;
1186          return $self->_fetch_doc( uri => $uri, path => '/uri_to_id', chomp_resbody => 1 );          return $self->_fetch_doc( uri => $uri, path => '/uri_to_id', chomp_resbody => 1, croak_on_error => 0 );
1187  }  }
1188    
1189    
# Line 1066  sub _fetch_doc { Line 1243  sub _fetch_doc {
1243                  'application/x-www-form-urlencoded',                  'application/x-www-form-urlencoded',
1244                  $arg,                  $arg,
1245                  \$resbody,                  \$resbody,
1246                    $a->{croak_on_error},
1247          );          );
1248    
1249          return if ($rv != 200);          return if ($rv != 200);
# Line 1096  sub _fetch_doc { Line 1274  sub _fetch_doc {
1274    
1275  sub name {  sub name {
1276          my $self = shift;          my $self = shift;
1277          $self->_set_info unless ($self->{name});          $self->_set_info unless ($self->{inform}->{name});
1278          return $self->{name};          return $self->{inform}->{name};
1279  }  }
1280    
1281    
# Line 1109  sub name { Line 1287  sub name {
1287    
1288  sub label {  sub label {
1289          my $self = shift;          my $self = shift;
1290          $self->_set_info unless ($self->{label});          $self->_set_info unless ($self->{inform}->{label});
1291          return $self->{label};          return $self->{inform}->{label};
1292  }  }
1293    
1294    
# Line 1122  sub label { Line 1300  sub label {
1300    
1301  sub doc_num {  sub doc_num {
1302          my $self = shift;          my $self = shift;
1303          $self->_set_info if ($self->{dnum} < 0);          $self->_set_info if ($self->{inform}->{dnum} < 0);
1304          return $self->{dnum};          return $self->{inform}->{dnum};
1305  }  }
1306    
1307    
# Line 1135  sub doc_num { Line 1313  sub doc_num {
1313    
1314  sub word_num {  sub word_num {
1315          my $self = shift;          my $self = shift;
1316          $self->_set_info if ($self->{wnum} < 0);          $self->_set_info if ($self->{inform}->{wnum} < 0);
1317          return $self->{wnum};          return $self->{inform}->{wnum};
1318  }  }
1319    
1320    
# Line 1148  sub word_num { Line 1326  sub word_num {
1326    
1327  sub size {  sub size {
1328          my $self = shift;          my $self = shift;
1329          $self->_set_info if ($self->{size} < 0);          $self->_set_info if ($self->{inform}->{size} < 0);
1330          return $self->{size};          return $self->{inform}->{size};
1331  }  }
1332    
1333    
# Line 1290  sub cond_to_query { Line 1468  sub cond_to_query {
1468    
1469          if (my @attrs = $cond->attrs) {          if (my @attrs = $cond->attrs) {
1470                  for my $i ( 0 .. $#attrs ) {                  for my $i ( 0 .. $#attrs ) {
1471                          push @args,'attr' . ($i+1) . '=' . uri_escape( $attrs[$i] );                          push @args,'attr' . ($i+1) . '=' . uri_escape( $attrs[$i] ) if ($attrs[$i]);
1472                  }                  }
1473          }          }
1474    
# Line 1319  sub cond_to_query { Line 1497  sub cond_to_query {
1497    
1498  =head2 shuttle_url  =head2 shuttle_url
1499    
1500  This is method which uses C<IO::Socket::INET> to communicate with Hyper Estraier node  This is method which uses C<LWP::UserAgent> to communicate with Hyper Estraier node
1501  master.  master.
1502    
1503    my $rv = shuttle_url( $url, $content_type, $req_body, \$resbody );    my $rv = shuttle_url( $url, $content_type, $req_body, \$resbody );
# Line 1334  use LWP::UserAgent; Line 1512  use LWP::UserAgent;
1512  sub shuttle_url {  sub shuttle_url {
1513          my $self = shift;          my $self = shift;
1514    
1515          my ($url, $content_type, $reqbody, $resbody) = @_;          my ($url, $content_type, $reqbody, $resbody, $croak_on_error) = @_;
1516    
1517            $croak_on_error = $self->{croak_on_error} unless defined($croak_on_error);
1518    
1519          $self->{status} = -1;          $self->{status} = -1;
1520    
# Line 1361  sub shuttle_url { Line 1541  sub shuttle_url {
1541    
1542          $req->headers->header( 'Host' => $url->host . ":" . $url->port );          $req->headers->header( 'Host' => $url->host . ":" . $url->port );
1543          $req->headers->header( 'Connection', 'close' );          $req->headers->header( 'Connection', 'close' );
1544          $req->headers->header( 'Authorization', 'Basic ' . $self->{auth} );          $req->headers->header( 'Authorization', 'Basic ' . $self->{auth} ) if ($self->{auth});
1545          $req->content_type( $content_type );          $req->content_type( $content_type );
1546    
1547          warn $req->headers->as_string,"\n" if ($self->{debug});          warn $req->headers->as_string,"\n" if ($self->{debug});
# Line 1375  sub shuttle_url { Line 1555  sub shuttle_url {
1555    
1556          warn "## response status: ",$res->status_line,"\n" if ($self->{debug});          warn "## response status: ",$res->status_line,"\n" if ($self->{debug});
1557    
         return -1 if (! $res->is_success);  
   
1558          ($self->{status}, $self->{status_message}) = split(/\s+/, $res->status_line, 2);          ($self->{status}, $self->{status_message}) = split(/\s+/, $res->status_line, 2);
1559    
1560            if (! $res->is_success) {
1561                    if ($croak_on_error) {
1562                            croak("can't get $url: ",$res->status_line);
1563                    } else {
1564                            return -1;
1565                    }
1566            }
1567    
1568          $$resbody .= $res->content;          $$resbody .= $res->content;
1569    
1570          warn "## response body:\n$$resbody\n" if ($resbody && $self->{debug});          warn "## response body:\n$$resbody\n" if ($resbody && $self->{debug});
# Line 1477  sub set_link { Line 1663  sub set_link {
1663          my $reqbody = 'url=' . uri_escape($url) . '&label=' . uri_escape($label);          my $reqbody = 'url=' . uri_escape($url) . '&label=' . uri_escape($label);
1664          $reqbody .= '&credit=' . $credit if ($credit > 0);          $reqbody .= '&credit=' . $credit if ($credit > 0);
1665    
1666          $self->shuttle_url( $self->{url} . '/_set_link',          if ($self->shuttle_url( $self->{url} . '/_set_link',
1667                  'text/plain',                  'application/x-www-form-urlencoded',
1668                  $reqbody,                  $reqbody,
1669                  undef                  undef
1670          ) == 200;          ) == 200) {
1671                    # refresh node info after adding link
1672                    $self->_set_info;
1673                    return 1;
1674            }
1675    }
1676    
1677    =head2 admins
1678    
1679     my @admins = @{ $node->admins };
1680    
1681    Return array of users with admin rights on node
1682    
1683    =cut
1684    
1685    sub admins {
1686            my $self = shift;
1687            $self->_set_info unless ($self->{inform}->{name});
1688            return $self->{inform}->{admins};
1689    }
1690    
1691    =head2 guests
1692    
1693     my @guests = @{ $node->guests };
1694    
1695    Return array of users with guest rights on node
1696    
1697    =cut
1698    
1699    sub guests {
1700            my $self = shift;
1701            $self->_set_info unless ($self->{inform}->{name});
1702            return $self->{inform}->{guests};
1703    }
1704    
1705    =head2 links
1706    
1707     my $links = @{ $node->links };
1708    
1709    Return array of links for this node
1710    
1711    =cut
1712    
1713    sub links {
1714            my $self = shift;
1715            $self->_set_info unless ($self->{inform}->{name});
1716            return $self->{inform}->{links};
1717  }  }
1718    
1719    
# Line 1512  sub _set_info { Line 1744  sub _set_info {
1744    
1745          return if ($rv != 200 || !$resbody);          return if ($rv != 200 || !$resbody);
1746    
1747          # it seems that response can have multiple line endings          my @lines = split(/[\r\n]/,$resbody);
1748          $resbody =~ s/[\r\n]+$//;  
1749            $self->{inform} = {};
1750    
1751            ( $self->{inform}->{name}, $self->{inform}->{label}, $self->{inform}->{dnum},
1752                    $self->{inform}->{wnum}, $self->{inform}->{size} ) = split(/\t/, shift @lines, 5);
1753    
1754            return $resbody unless (@lines);
1755    
1756            shift @lines;
1757    
1758            while(my $admin = shift @lines) {
1759                    push @{$self->{inform}->{admins}}, $admin;
1760            }
1761    
1762            while(my $guest = shift @lines) {
1763                    push @{$self->{inform}->{guests}}, $guest;
1764            }
1765    
1766            while(my $link = shift @lines) {
1767                    push @{$self->{inform}->{links}}, $link;
1768            }
1769    
1770          ( $self->{name}, $self->{label}, $self->{dnum}, $self->{wnum}, $self->{size} ) =          return $resbody;
                 split(/\t/, $resbody, 5);  
1771    
1772  }  }
1773    

Legend:
Removed from v.61  
changed lines
  Added in v.111

  ViewVC Help
Powered by ViewVC 1.1.26