Changeset 2538 for Search-Query/trunk/lib/Search/Query/Dialect/KSx.pm
- Timestamp:
- 02/23/10 09:40:38 (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
Search-Query/trunk/lib/Search/Query/Dialect/KSx.pm
r2537 r2538 5 5 use Carp; 6 6 use Data::Dump qw( dump ); 7 use Search::Query::Field::KSx; 7 8 8 9 our $VERSION = '0.08'; 10 11 __PACKAGE__->mk_accessors( 12 qw( 13 wildcard 14 fuzzify 15 ) 16 ); 9 17 10 18 =head1 NAME … … 23 31 =head1 METHODS 24 32 25 This class is a subclass of Search::Query::Dialect::Native. 26 Only new or overridden methods are documented here. 27 28 =cut 33 This class is a subclass of Search::Query::Dialect. Only new or overridden 34 methods are documented here. 35 36 =cut 37 38 =head2 init 39 40 Overrides base method and sets SWISH-appropriate defaults. 41 Can take the following params, also available as standard attribute 42 methods. 43 44 =over 45 46 =item wildcard 47 48 Default is '*'. 49 50 =item fuzzify 51 52 If true, a wildcard is automatically appended to each query term. 53 54 =back 55 56 =cut 57 58 sub init { 59 my $self = shift; 60 61 $self->SUPER::init(@_); 62 63 #carp dump $self; 64 $self->{wildcard} = '*'; 65 if ( $self->parser->fields ) { 66 $self->{default_field} ||= $self->parser->default_field 67 || [ sort keys %{ $self->parser->fields } ]; 68 } 69 else { 70 $self->{default_field} ||= $self->parser->default_field 71 || 'swishdefault'; 72 } 73 if ( $self->{default_field} and !ref( $self->{default_field} ) ) { 74 $self->{default_field} = [ $self->{default_field} ]; 75 } 76 77 return $self; 78 } 79 80 =head2 stringify 81 82 Returns the Query object as a normalized string. 83 84 =cut 85 86 my %op_map = ( 87 '+' => 'AND', 88 '' => 'OR', 89 '-' => 'NOT', 90 ); 91 92 sub stringify { 93 my $self = shift; 94 my $tree = shift || $self; 95 96 my @q; 97 foreach my $prefix ( '+', '', '-' ) { 98 my @clauses; 99 my $joiner = $op_map{$prefix}; 100 next unless exists $tree->{$prefix}; 101 for my $clause ( @{ $tree->{$prefix} } ) { 102 push( @clauses, $self->stringify_clause( $clause, $prefix ) ); 103 } 104 next if !@clauses; 105 106 push @q, join( " $joiner ", grep { defined and length } @clauses ); 107 } 108 109 return join " AND ", @q; 110 } 111 112 sub _doctor_value { 113 my ( $self, $clause ) = @_; 114 115 my $value = $clause->{value}; 116 117 if ( $self->fuzzify ) { 118 $value .= '*' unless $value =~ m/[\*\%]/; 119 } 120 121 # normalize wildcard 122 my $wildcard = $self->wildcard; 123 $value =~ s/[\*\%]/$wildcard/g; 124 125 return $value; 126 } 127 128 =head2 stringify_clause( I<leaf>, I<prefix> ) 129 130 Called by stringify() to handle each Clause in the Query tree. 131 132 =cut 133 134 sub stringify_clause { 135 my $self = shift; 136 my $clause = shift; 137 my $prefix = shift; 138 139 #warn dump $clause; 140 #warn "prefix = '$prefix'"; 141 142 if ( $clause->{op} eq '()' ) { 143 if ( $clause->has_children and $clause->has_children == 1 ) { 144 return $self->stringify( $clause->{value} ); 145 } 146 else { 147 return 148 ( $prefix eq '-' ? 'NOT ' : '' ) . "(" 149 . $self->stringify( $clause->{value} ) . ")"; 150 } 151 } 152 153 # make sure we have a field 154 my @fields 155 = $clause->{field} 156 ? ( $clause->{field} ) 157 : ( @{ $self->_get_default_field } ); 158 159 # what value 160 my $value 161 = ref $clause->{value} 162 ? $clause->{value} 163 : $self->_doctor_value($clause); 164 165 my $wildcard = $self->wildcard; 166 167 # normalize operator 168 my $op = $clause->{op} || ":"; 169 if ( $op eq '=' ) { 170 $op = ':'; 171 } 172 if ( $prefix eq '-' ) { 173 $op = '!' . $op; 174 } 175 if ( $value =~ m/\%/ ) { 176 $op = $prefix eq '-' ? '!~' : '~'; 177 } 178 179 my $quote = $clause->quote || ''; 180 181 my @buf; 182 NAME: for my $name (@fields) { 183 my $field = $self->_get_field($name); 184 185 if ( defined $field->callback ) { 186 push( @buf, $field->callback->( $field, $op, $value ) ); 187 next NAME; 188 } 189 190 #warn dump [ $name, $op, $quote, $value ]; 191 192 # invert fuzzy 193 if ( $op eq '!~' ) { 194 $value .= $wildcard unless $value =~ m/\Q$wildcard/; 195 push( @buf, 196 join( '', 'NOT ', $name, '=', qq/$quote$value$quote/ ) ); 197 } 198 199 # fuzzy 200 elsif ( $op eq '~' ) { 201 $value .= $wildcard unless $value =~ m/\Q$wildcard/; 202 push( @buf, join( '', $name, '=', qq/$quote$value$quote/ ) ); 203 } 204 205 # invert 206 elsif ( $op eq '!:' ) { 207 push( @buf, 208 join( '', 'NOT ', $name, ':', qq/$quote$value$quote/ ) ); 209 } 210 211 # range 212 elsif ( $op eq '..' ) { 213 if ( ref $value ne 'ARRAY' or @$value != 2 ) { 214 croak "range of values must be a 2-element ARRAY"; 215 } 216 217 # we support only numbers at this point 218 for my $v (@$value) { 219 if ( $v =~ m/\D/ ) { 220 croak "non-numeric range values are not supported: $v"; 221 } 222 } 223 224 my @range = ( $value->[0] .. $value->[1] ); 225 push( @buf, 226 join( '', $name, ':', '(', join( ' OR ', @range ), ')' ) ); 227 228 } 229 230 # invert range 231 elsif ( $op eq '!..' ) { 232 if ( ref $value ne 'ARRAY' or @$value != 2 ) { 233 croak "range of values must be a 2-element ARRAY"; 234 } 235 236 # we support only numbers at this point 237 for my $v (@$value) { 238 if ( $v =~ m/\D/ ) { 239 croak "non-numeric range values are not supported: $v"; 240 } 241 } 242 243 my @range = ( $value->[0] .. $value->[1] ); 244 push( @buf, 245 join( '', '-', $name, ':', '( ', join( ' ', @range ), ' )' ) 246 ); 247 } 248 249 # standard 250 else { 251 push( @buf, join( '', $name, ':', qq/$quote$value$quote/ ) ); 252 } 253 } 254 my $joiner = $prefix eq '-' ? ' AND ' : ' OR '; 255 return 256 ( scalar(@buf) > 1 ? '(' : '' ) 257 . join( $joiner, @buf ) 258 . ( scalar(@buf) > 1 ? ')' : '' ); 259 } 260 261 =head2 field_class 262 263 Returns "Search::Query::Field::KSx". 264 265 =cut 266 267 sub field_class {'Search::Query::Field::KSx'} 29 268 30 269 1;
Note: See TracChangeset
for help on using the changeset viewer.