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   1512 use strict;
  2         4  
  2         51  
17 2     2   6 use warnings;
  2         2  
  2         91  
18              
19             package LucyX::Search::Filter;
20 2     2   92 BEGIN { our @ISA = qw( Lucy::Search::Query ) }
21             our $VERSION = '0.006000_001';
22             $VERSION = eval $VERSION;
23 2     2   9 use Carp;
  2         3  
  2         110  
24 2     2   8 use Storable qw( nfreeze thaw );
  2         2  
  2         92  
25 2     2   7 use Scalar::Util qw( blessed weaken );
  2         2  
  2         71  
26 2     2   6 use bytes;
  2         3  
  2         39  
27 2     2   36 no bytes;
  2         3  
  2         6  
28              
29             # Inside-out member vars.
30             our %query;
31             our %cached_bits;
32              
33             sub new {
34 2     2 1 300 my ( $either, %args ) = @_;
35 2         4 my $query = delete $args{query};
36 2 50 33     36 confess("required parameter query is not a Lucy::Search::Query")
37             unless ( blessed($query)
38             && $query->isa('Lucy::Search::Query') );
39 2         18 my $self = $either->SUPER::new(%args);
40 2         289 $self->_init_cache;
41 2         3 $query{$$self} = $query;
42 2         12 $self->set_boost(0);
43 2         5 return $self;
44             }
45              
46             sub DESTROY {
47 3     3   387 my $self = shift;
48 3         10 delete $query{$$self};
49 3         10 delete $cached_bits{$$self};
50 3         34 $self->SUPER::DESTROY;
51             }
52              
53             sub make_compiler {
54 6     6 1 5465 my ( $self, %args ) = @_;
55 6         10 my $subordinate = delete $args{subordinate};
56 6         23 my $compiler
57             = LucyX::Search::FilterCompiler->new( %args, parent => $self );
58 6 50       104 $compiler->normalize unless $subordinate;
59 6         157 return $compiler;
60             }
61              
62             sub serialize {
63 1     1 0 55 my ( $self, $outstream ) = @_;
64 1         10 $self->SUPER::serialize($outstream);
65 1         6 my $frozen = nfreeze( $query{$$self} );
66 1         97 $outstream->write_cu32( bytes::length($frozen) );
67 1         963 $outstream->print($frozen);
68             }
69              
70             sub deserialize {
71 1     1 0 41 my ( $self, $instream ) = @_;
72 1         11 $self->SUPER::deserialize($instream);
73 1         8 my $len = $instream->read_cu32;
74 1         2 my $frozen;
75 1         5 $instream->read( $frozen, $len );
76 1         4 $query{$$self} = thaw($frozen);
77 1         61 return $self;
78             }
79              
80             sub equals {
81 2     2 1 10 my ( $self, $other ) = @_;
82 2 50       9 return 0 unless $other->isa(__PACKAGE__);
83 2 50       42 return 0 unless $query{$$self}->equals( $query{$$other} );
84 2 50       20 return 0 unless $self->get_boost == $other->get_boost;
85 2         9 return 1;
86             }
87              
88             sub to_string {
89 1     1 1 1 my $self = shift;
90 1         15 return 'Filter(' . $query{$$self}->to_string . ')';
91             }
92              
93             sub _bits {
94 12     12   533 my ( $self, $seg_reader ) = @_;
95              
96 12         22 my $cached_bits = $self->_fetch_cached_bits($seg_reader);
97              
98             # Fill the cache.
99 12 100       24 if ( !defined $cached_bits ) {
100 3         33 $cached_bits = Lucy::Object::BitVector->new(
101             capacity => $seg_reader->doc_max + 1 );
102 3         9 $self->_store_cached_bits( $seg_reader, $cached_bits );
103              
104 3         21 my $collector = Lucy::Search::Collector::BitCollector->new(
105             bit_vector => $cached_bits );
106              
107 3         97 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         20 my $searcher
114             = Lucy::Search::IndexSearcher->new( index => $polyreader );
115              
116             # Perform the search.
117             $searcher->collect(
118 3         157 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         8 my $pair = { seg_reader => $seg_reader, bits => $bits };
131 3         10 weaken( $pair->{seg_reader} );
132 3         9 $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   12 my ( $self, $seg_reader ) = @_;
140 12         18 my $cached_bits = $cached_bits{$$self};
141              
142             # Sweep.
143 12         39 while ( my ( $addr, $pair ) = each %$cached_bits ) {
144             # If weak ref has decomposed into undef, SegReader is gone... so
145             # delete.
146 15 50       56 next if defined $pair->{seg_reader};
147 0         0 delete $cached_bits->{$addr};
148             }
149              
150             # Fetch.
151 12         26 my $pair = $cached_bits->{$$seg_reader};
152 12 100       27 return $pair->{bits} if defined $pair;
153 3         5 return;
154             }
155              
156             # Kill any existing cached filters.
157             sub _init_cache {
158 2     2   3 my $self = shift;
159 2         10 $cached_bits{$$self} = {};
160             }
161              
162             # Testing only.
163             sub _cached_count {
164 4     4   2035 my $self = shift;
165 6         28 return scalar grep { defined $cached_bits{$$self}{$_}{seg_reader} }
166 4         6 keys %{ $cached_bits{$$self} };
  4         13  
167             }
168              
169             package LucyX::Search::FilterCompiler;
170             our $VERSION = '0.006000_001';
171             $VERSION = eval $VERSION;
172 2     2   1585 BEGIN { our @ISA = qw( Lucy::Search::Compiler ) }
173              
174             sub new {
175 6     6   13 my ( $class, %args ) = @_;
176 6   33     57 $args{similarity} ||= $args{searcher}->get_schema->get_similarity;
177 6         28 return $class->SUPER::new(%args);
178             }
179              
180             sub make_matcher {
181 6     6   16 my ( $self, %args ) = @_;
182 6         9 my $seg_reader = $args{reader};
183 6         27 my $bits = $self->get_parent->_bits($seg_reader);
184 6         477 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_001';
192             $VERSION = eval $VERSION;
193 2     2   64 BEGIN { our @ISA = qw( Lucy::Search::Matcher ) }
194              
195             1;
196              
197             __END__