File Coverage

blib/lib/KinoSearch1/Search/SearchServer.pm
Criterion Covered Total %
statement 22 85 25.8
branch 0 22 0.0
condition 0 3 0.0
subroutine 8 16 50.0
pod 2 8 25.0
total 32 134 23.8


line stmt bran cond sub pod time code
1             package KinoSearch1::Search::SearchServer;
2 2     2   55275 use strict;
  2         5  
  2         62  
3 2     2   9 use warnings;
  2         3  
  2         53  
4 2     2   548 use KinoSearch1::Util::ToolSet;
  2         6  
  2         268  
5 2     2   11 use base qw( KinoSearch1::Util::Class );
  2         3  
  2         572  
6              
7             BEGIN {
8 2     2   19 __PACKAGE__->init_instance_vars(
9             # params/members
10             searchable => undef,
11             port => undef,
12             password => undef,
13             # members
14             sock => undef,
15              
16             );
17             }
18              
19 2     2   18 use IO::Socket::INET;
  2         6  
  2         19  
20 2     2   8373 use IO::Select;
  2         3267  
  2         106  
21 2     2   990 use Storable qw( nfreeze thaw );
  2         9691  
  2         2269  
22              
23             sub init_instance {
24 0     0 1   my $self = shift;
25              
26 0 0         confess("Missing required param 'password'")
27             unless defined $self->{password};
28              
29             # establish a listening socket
30 0 0         confess("Invalid port") unless $self->{port} =~ /^\d+$/;
31 0           my $sock = IO::Socket::INET->new(
32             LocalPort => $self->{port},
33             Proto => 'tcp',
34             Listen => SOMAXCONN,
35             Reuse => 1,
36             );
37 0 0         confess("No socket: $!") unless $sock;
38 0           $sock->autoflush(1);
39 0           $self->{sock} = $sock;
40             }
41              
42             my %dispatch = (
43             get_field_names => \&do_get_field_names,
44             max_doc => \&do_max_doc,
45             doc_freq => \&do_doc_freq,
46             doc_freqs => \&do_doc_freqs,
47             search_hit_collector => \&do_search_hit_collector,
48             fetch_doc => \&do_fetch_doc,
49             terminate => undef,
50             );
51              
52             sub serve {
53 0     0 1   my $self = shift;
54 0           my $main_sock = $self->{sock};
55 0           my $read_set = IO::Select->new($main_sock);
56              
57 0           while ( my @ready = $read_set->can_read ) {
58 0           for my $readhandle (@ready) {
59             # if this is the main handle, we have a new client, so accept
60 0 0         if ( $readhandle == $main_sock ) {
61 0           my $client_sock = $main_sock->accept;
62              
63             # verify password
64 0           my $pass = <$client_sock>;
65 0 0         chomp($pass) if defined $pass;
66 0 0 0       if ( defined($pass) && $pass eq $self->{password} ) {
67 0           $read_set->add($client_sock);
68 0           print $client_sock "accepted\n";
69             }
70             else {
71 0           print $client_sock "password incorrect\n";
72             }
73             }
74             # otherwise it's a client sock, so process the request
75             else {
76 0           my $client_sock = $readhandle;
77 0           my ( $check_val, $buf, $len, $method, $args );
78 0           chomp( $method = <$client_sock> );
79              
80             # if "done", the client's closing
81 0 0         if ( $method eq 'done' ) {
    0          
    0          
82 0           $read_set->remove($client_sock);
83 0           $client_sock->close;
84 0           next;
85             }
86             # remote signal to close the server
87             elsif ( $method eq 'terminate' ) {
88 0           $read_set->remove($client_sock);
89 0           $client_sock->close;
90 0           $main_sock->close;
91 0           return;
92             }
93             # sanity check the method name
94             elsif ( !$dispatch{$method} ) {
95 0           print $client_sock "ERROR: Bad method name: $method\n";
96 0           next;
97             }
98              
99             # process the method call
100 0           read( $client_sock, $buf, 4 );
101 0           $len = unpack( 'N', $buf );
102 0           read( $client_sock, $buf, $len );
103 0           my $response = $dispatch{$method}->( $self, thaw($buf) );
104 0           my $frozen = nfreeze($response);
105 0           my $packed_len = pack( 'N', bytes::length($frozen) );
106 0           print $client_sock $packed_len . $frozen;
107             }
108             }
109             }
110             }
111              
112             sub do_get_field_names {
113 0     0 0   my ( $self, $args ) = @_;
114 0           return $self->{searchable}->get_field_names(%$args);
115             }
116              
117             sub do_doc_freq {
118 0     0 0   my ( $self, $args ) = @_;
119 0           my $doc_freq = $self->{searchable}->doc_freq( $args->{term} );
120 0           return { doc_freq => $doc_freq };
121             }
122              
123             sub do_doc_freqs {
124 0     0 0   my ( $self, $args ) = @_;
125 0           return $self->{searchable}->doc_freqs( $args->{terms} );
126             }
127              
128             sub do_search_hit_collector {
129 0     0 0   my ( $self, $args ) = @_;
130              
131 0 0         confess("remote filtered search not supported")
132             if defined $args->{filter};
133 0           my $collector = KinoSearch1::Search::HitQueueCollector->new(
134             size => $args->{num_wanted} );
135              
136 0           my $scorer = $args->{weight}->scorer( $self->{searchable}->get_reader );
137              
138 0 0         if ( defined $scorer ) {
139 0           $scorer->score_batch(
140             hit_collector => $collector,
141             end => $self->{searchable}->max_doc,
142             );
143             }
144 0           my $hit_queue = $collector->get_hit_queue;
145 0           my $hit_docs = $hit_queue->hits;
146 0           my %score_docs;
147 0           $score_docs{ $_->get_id } = $_->get_score for @$hit_docs;
148 0           return \%score_docs;
149             }
150              
151             sub do_max_doc {
152 0     0 0   my ( $self, $args ) = @_;
153 0           my $max_doc = $self->{searchable}->max_doc;
154 0           return { max_doc => $max_doc };
155             }
156              
157             sub do_fetch_doc {
158 0     0 0   my ( $self, $args ) = @_;
159 0           return $self->{searchable}->fetch_doc( $args->{doc_num} );
160             }
161              
162             1;
163              
164             __END__