File Coverage

blib/lib/LucyX/Search/Filter.pm
Criterion Covered Total %
statement 94 95 98.9
branch 10 16 62.5
condition 2 6 33.3
subroutine 24 24 100.0
pod 4 6 66.6
total 134 147 91.1


line stmt bran cond sub pod time code
1             # Licensed to the Apache Software Foundation (ASF) under one or more
2             # contributor license agreements. See the NOTICE file distributed with
3             # this work for additional information regarding copyright ownership.
4             # The ASF licenses this file to You under the Apache License, Version 2.0
5             # (the "License"); you may not use this file except in compliance with
6             # the License. You may obtain a copy of the License at
7             #
8             # http://www.apache.org/licenses/LICENSE-2.0
9             #
10             # Unless required by applicable law or agreed to in writing, software
11             # distributed under the License is distributed on an "AS IS" BASIS,
12             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13             # See the License for the specific language governing permissions and
14             # limitations under the License.
15              
16 2     2   1230 use strict;
  2         3  
  2         44  
17 2     2   6 use warnings;
  2         2  
  2         83  
18              
19             package LucyX::Search::Filter;
20 2     2   84 BEGIN { our @ISA = qw( Lucy::Search::Query ) }
21             our $VERSION = '0.006000_002';
22             $VERSION = eval $VERSION;
23 2     2   7 use Carp;
  2         2  
  2         91  
24 2     2   7 use Storable qw( nfreeze thaw );
  2         2  
  2         84  
25 2     2   7 use Scalar::Util qw( blessed weaken );
  2         2  
  2         66  
26 2     2   6 use bytes;
  2         2  
  2         30  
27 2     2   34 no bytes;
  2         2  
  2         4  
28              
29             # Inside-out member vars.
30             our %query;
31             our %cached_bits;
32              
33             sub new {
34 2     2 1 301 my ( $either, %args ) = @_;
35 2         4 my $query = delete $args{query};
36 2 50 33     35 confess("required parameter query is not a Lucy::Search::Query")
37             unless ( blessed($query)
38             && $query->isa('Lucy::Search::Query') );
39 2         15 my $self = $either->SUPER::new(%args);
40 2         256 $self->_init_cache;
41 2         3 $query{$$self} = $query;
42 2         11 $self->set_boost(0);
43 2         4 return $self;
44             }
45              
46             sub DESTROY {
47 3     3   362 my $self = shift;
48 3         11 delete $query{$$self};
49 3         14 delete $cached_bits{$$self};
50 3         45 $self->SUPER::DESTROY;
51             }
52              
53             sub make_compiler {
54 6     6 1 4304 my ( $self, %args ) = @_;
55 6         8 my $subordinate = delete $args{subordinate};
56 6         23 my $compiler
57             = LucyX::Search::FilterCompiler->new( %args, parent => $self );
58 6 50       87 $compiler->normalize unless $subordinate;
59 6         111 return $compiler;
60             }
61              
62             sub serialize {
63 1     1 0 49 my ( $self, $outstream ) = @_;
64 1         6 $self->SUPER::serialize($outstream);
65 1         6 my $frozen = nfreeze( $query{$$self} );
66 1         75 $outstream->write_cu32( bytes::length($frozen) );
67 1         786 $outstream->print($frozen);
68             }
69              
70             sub deserialize {
71 1     1 0 31 my ( $self, $instream ) = @_;
72 1         8 $self->SUPER::deserialize($instream);
73 1         7 my $len = $instream->read_cu32;
74 1         1 my $frozen;
75 1         4 $instream->read( $frozen, $len );
76 1         4 $query{$$self} = thaw($frozen);
77 1         37 return $self;
78             }
79              
80             sub equals {
81 2     2 1 6 my ( $self, $other ) = @_;
82 2 50       8 return 0 unless $other->isa(__PACKAGE__);
83 2 50       19 return 0 unless $query{$$self}->equals( $query{$$other} );
84 2 50       24 return 0 unless $self->get_boost == $other->get_boost;
85 2         9 return 1;
86             }
87              
88             sub to_string {
89 1     1 1 2 my $self = shift;
90 1         15 return 'Filter(' . $query{$$self}->to_string . ')';
91             }
92              
93             sub _bits {
94 12     12   436 my ( $self, $seg_reader ) = @_;
95              
96 12         16 my $cached_bits = $self->_fetch_cached_bits($seg_reader);
97              
98             # Fill the cache.
99 12 100       17 if ( !defined $cached_bits ) {
100 3         30 $cached_bits = Lucy::Object::BitVector->new(
101             capacity => $seg_reader->doc_max + 1 );
102 3         6 $self->_store_cached_bits( $seg_reader, $cached_bits );
103              
104 3         14 my $collector = Lucy::Search::Collector::BitCollector->new(
105             bit_vector => $cached_bits );
106              
107 3         74 my $polyreader = Lucy::Index::PolyReader->new(
108             schema => $seg_reader->get_schema,
109             folder => $seg_reader->get_folder,
110             snapshot => $seg_reader->get_snapshot,
111             sub_readers => [$seg_reader],
112             );
113 3         14 my $searcher
114             = Lucy::Search::IndexSearcher->new( index => $polyreader );
115              
116             # Perform the search.
117             $searcher->collect(
118 3         136 query => $query{$$self},
119             collector => $collector,
120             );
121             }
122              
123 12         22 return $cached_bits;
124             }
125              
126             # Store a cached BitVector associated with a particular SegReader. Store a
127             # weak reference to the SegReader as an indicator of cache validity.
128             sub _store_cached_bits {
129 3     3   4 my ( $self, $seg_reader, $bits ) = @_;
130 3         6 my $pair = { seg_reader => $seg_reader, bits => $bits };
131 3         7 weaken( $pair->{seg_reader} );
132 3         7 $cached_bits{$$self}{$$seg_reader} = $pair;
133             }
134              
135             # Retrieve a cached BitVector associated with a particular SegReader. As a
136             # side effect, clear away any BitVectors which are no longer valid because
137             # their SegReaders have gone away.
138             sub _fetch_cached_bits {
139 12     12   10 my ( $self, $seg_reader ) = @_;
140 12         15 my $cached_bits = $cached_bits{$$self};
141              
142             # Sweep.
143 12         32 while ( my ( $addr, $pair ) = each %$cached_bits ) {
144             # If weak ref has decomposed into undef, SegReader is gone... so
145             # delete.
146 15 50       42 next if defined $pair->{seg_reader};
147 0         0 delete $cached_bits->{$addr};
148             }
149              
150             # Fetch.
151 12         21 my $pair = $cached_bits->{$$seg_reader};
152 12 100       23 return $pair->{bits} if defined $pair;
153 3         4 return;
154             }
155              
156             # Kill any existing cached filters.
157             sub _init_cache {
158 2     2   3 my $self = shift;
159 2         8 $cached_bits{$$self} = {};
160             }
161              
162             # Testing only.
163             sub _cached_count {
164 4     4   1665 my $self = shift;
165 6         24 return scalar grep { defined $cached_bits{$$self}{$_}{seg_reader} }
166 4         4 keys %{ $cached_bits{$$self} };
  4         12  
167             }
168              
169             package LucyX::Search::FilterCompiler;
170             our $VERSION = '0.006000_002';
171             $VERSION = eval $VERSION;
172 2     2   1682 BEGIN { our @ISA = qw( Lucy::Search::Compiler ) }
173              
174             sub new {
175 6     6   12 my ( $class, %args ) = @_;
176 6   33     50 $args{similarity} ||= $args{searcher}->get_schema->get_similarity;
177 6         21 return $class->SUPER::new(%args);
178             }
179              
180             sub make_matcher {
181 6     6   11 my ( $self, %args ) = @_;
182 6         6 my $seg_reader = $args{reader};
183 6         22 my $bits = $self->get_parent->_bits($seg_reader);
184 6         378 return LucyX::Search::FilterMatcher->new(
185             bits => $bits,
186             doc_max => $seg_reader->doc_max,
187             );
188             }
189              
190             package LucyX::Search::FilterMatcher;
191             our $VERSION = '0.006000_002';
192             $VERSION = eval $VERSION;
193 2     2   56 BEGIN { our @ISA = qw( Lucy::Search::Matcher ) }
194              
195             1;
196              
197             __END__