use strict;
use warnings;
package KinoSearch::Search::PolyFilter;
use KinoSearch::Util::ToolSet;
use base qw( KinoSearch::Search::Filter );
our %instance_vars = (
# inherited
cached_bits => undef,
# members
filters => undef,
);
sub init_instance {
my $self = shift;
$self->{cached_bits} = {};
$self->{filters} = [];
}
sub bits {
my ( $self, $reader ) = @_;
return undef unless @{ $self->{filters} };
my $cached_bits = $self->fetch_cached_bits($reader);
# fill the cache
if ( !defined $cached_bits ) {
for my $filter ( @{ $self->{filters} } ) {
my $bits = $filter->{filter}->bits($reader);
my $logic = uc( $filter->{logic} ) || 'AND';
if ( !defined $cached_bits ) {
$cached_bits = $bits->clone;
$self->store_cached_bits( $reader, $cached_bits );
if ( $logic eq 'NOT' ) {
$cached_bits->flip_range( 0, $reader->max_doc );
}
}
elsif ( $logic eq 'XOR' ) {
$cached_bits->XOR($bits);
}
elsif ( $logic eq 'OR' ) {
$cached_bits->OR($bits);
}
elsif ( $logic eq 'NOT' ) {
$cached_bits->AND_NOT($bits);
}
else { # default: ($logic eq 'AND') {
$cached_bits->AND($bits);
}
}
}
return $cached_bits;
}
my %add_args = (
filter => undef,
logic => undef,
);
sub add {
my $self = shift;
confess kerror() unless verify_args( \%add_args, @_ );
my %args = ( %add_args, @_ );
confess("Missing required parameter filter") unless defined $args{filter};
push @{ $self->{filters} }, \%args;
$self->{cached_bits} = {}; # invalidate cache
return 1;
}
1;
__END__
=head1 NAME
KinoSearch::Search::PolyFilter - Combine filters for a search.
=head1 SYNOPSIS
my $polyfilter = KinoSearch::Search::PolyFilter->new;
$polyfilter->add( filter => $query_filter );
$polyfilter->add( filter => $range_filter, logic => 'AND' );
my $hits = $searcher->search( query => $query, filter => $polyfilter );
=head1 DESCRIPTION
A PolyFilter is a chain of L<Filter|KinoSearch::Search::Filter> objects which
may be combined using boolean logic, making it possible to do things like
filter by multiple ranges, or to apply both a RangeFilter and a QueryFilter
to the same query.
During search, the sub-filters are applied in the order that they were added.
=head1 METHODS
=head2 new
my $filter = KinoSearch::Search::PolyFilter->new;
Constructor. Takes no parameters.
=head2 add
$polyfilter->add(
filter => $query_filter, # required
logic => 'OR', # default: 'AND'
);
Adds a filter to the query.
=over
=item *
B<filter> - the L<Filter|KinoSearch::Search::Filter> object to add to the
PolyFilter, which might be a L<QueryFilter|KinoSearch::Search::QueryFilter>, a
L<RangeFilter|KinoSearch::Search::RangeFilter>, or another PolyFilter.
=item *
B<logic> - C<AND>, C<NOT>, C<OR>, or C<XOR>. Optional; default is C<AND>.
=back
=head1 COPYRIGHT
Copyright 2005-2007 Marvin Humphrey
=head1 LICENSE, DISCLAIMER, BUGS, etc.
See L<KinoSearch> version 0.20.
=cut