File Coverage

blib/lib/Plucene/Search/PhraseQuery.pm
Criterion Covered Total %
statement 65 70 92.8
branch 13 20 65.0
condition n/a
subroutine 15 15 100.0
pod 5 5 100.0
total 98 110 89.0


line stmt bran cond sub pod time code
1             package Plucene::Search::PhraseQuery;
2              
3             =head1 NAME
4              
5             Plucene::Search::PhraseQuery - A query that matchs a phrase
6              
7             =head1 SYNOPSIS
8              
9             # isa Plucene::Search::Query
10              
11             =head1 DESCRIPTION
12              
13             A Query that matches documents containing a particular sequence of terms.
14              
15             A phrase query represents a query that is matched against a consecutive
16             sequence of terms in the field. For example, the phrase query 'winding road'
17             should match 'winding road' but not 'road winding' (with the exception of
18             more relaxed slop factors).
19              
20             Phrase queries are represented in Plucene's API by instances of the
21             PharseQuery class. These instances contain an ordered list of Term objects
22             that represent the terms to match. For obvious reasons, all terms in a
23             PhraseQuery must refer to the same field.
24              
25             A phrase query may have an optional boost factor and an optional slop
26             parameter (default = 0). The slop parameter can be used to relax the phrase
27             matching by accepting somewhat out of order sequences of the terms.
28              
29             =head1 METHODS
30              
31             =cut
32              
33 4     4   22 use strict;
  4         9  
  4         154  
34 4     4   21 use warnings;
  4         7  
  4         124  
35              
36 4     4   21 use Carp;
  4         7  
  4         303  
37              
38 4     4   22 use Plucene::Search::Similarity;
  4         11  
  4         96  
39 4     4   1102 use Plucene::Search::TermQuery;
  4         10  
  4         50  
40 4     4   117 use Plucene::Search::TermScorer;
  4         10  
  4         71  
41 4     4   2542 use Plucene::Search::PhraseScorer::Exact;
  4         13  
  4         53  
42 4     4   1997 use Plucene::Search::PhraseScorer::Sloppy;
  4         11  
  4         52  
43              
44 4     4   119 use base 'Plucene::Search::Query';
  4         11  
  4         2798  
45              
46             __PACKAGE__->mk_accessors(qw(slop terms field idf weight));
47              
48             sub new {
49 10     10 1 77 my $self = shift->SUPER::new(@_);
50 10         188 $self->slop(0);
51 10         131 $self->terms([]);
52 10         71 $self;
53             }
54              
55             =head2 add
56              
57             Adds a term to the end of the query phrase.
58              
59             =cut
60              
61             sub add {
62 21     21 1 37 my ($self, $term) = @_;
63 21 100       28 if (@{ $self->terms } == 0) {
  21 50       58  
64 10         93 $self->field($term->field);
65             } elsif ($self->field ne $term->field) {
66 0         0 carp "All terms in this phrase should be in the same field: "
67             . $self->field;
68             }
69 21         264 push @{ $self->terms }, $term;
  21         64  
70             }
71              
72             =head2 sum_squared_weights
73              
74             The sum squared weights of this query.
75              
76             =cut
77              
78             sub sum_squared_weights {
79 6     6 1 31 my ($self, $searcher) = @_;
80 6         28 $self->{idf} += Plucene::Search::Similarity->idf($_, $searcher)
81 6         14 for @{ $self->terms };
82 6         51 $self->{weight} = $self->idf * $self->boost;
83 6         116 $self->boost * $self->boost;
84             }
85              
86             =head2 normalize
87              
88             Normalize the query.
89              
90             =cut
91              
92             sub normalize {
93 6     6 1 52 my ($self, $norm) = @_;
94 6         37 $self->{weight} *= $norm * $self->idf;
95             }
96              
97             sub _scorer {
98 6     6   21 my ($self, $reader) = @_;
99 6 50       15 return unless @{ $self->{terms} };
  6         38  
100 6 50       12 if (@{ $self->{terms} } == 1) {
  6         32  
101 0         0 my $term = $self->{terms}->[0];
102 0         0 my $docs = $reader->term_docs($term);
103 0 0       0 return unless $docs;
104 0         0 return Plucene::Search::TermScorer->new({
105             term_docs => $docs,
106             norms => $reader->norms($term->field),
107             weight => $self->weight
108             });
109             }
110              
111 6         15 my @tps;
112 6         15 for my $term (@{ $self->terms }) {
  6         36  
113 13         88 my $tp = $reader->term_positions($term);
114 13 50       43 return unless $tp;
115 13         41 push @tps, $tp;
116             }
117              
118 6 100       41 my $class =
119             "Plucene::Search::PhraseScorer::"
120             . (($self->slop == 0) ? "Exact" : "Sloppy");
121 6         83 $class->new({
122             tps => \@tps,
123             norms => $reader->norms($self->field),
124             weight => $self->weight,
125             slop => $self->slop
126             });
127             }
128              
129             =head2 to_string
130              
131             Prints a user-readable version of this query.
132              
133             =cut
134              
135             sub to_string {
136 6     6 1 3223 my ($self, $field) = @_;
137 6         13 my $buffer = "";
138 6 100       19 $buffer = $self->field . ":" if $field ne $self->field;
139 6         57 $buffer .= sprintf('"%s"', join(" ", map $_->text, @{ $self->terms }));
  6         17  
140 6 100       99 $buffer .= "~" . $self->slop if $self->slop;
141 6 50       54 $buffer .= "^" . $self->boost if $self->boost != 1;
142 6         48 $buffer;
143             }
144              
145             1;