- Timestamp:
- 02/28/10 22:57:46 (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
Search-Query-Dialect-KSx/trunk/lib/Search/Query/Dialect/KSx.pm
r2552 r2556 6 6 use Data::Dump qw( dump ); 7 7 use Search::Query::Field::KSx; 8 use KinoSearch::Search::ANDQuery; 9 use KinoSearch::Search::NoMatchQuery; 10 use KinoSearch::Search::NOTQuery; 11 use KinoSearch::Search::ORQuery; 12 use KinoSearch::Search::PhraseQuery; 13 use KinoSearch::Search::RangeQuery; 14 use KinoSearch::Search::TermQuery; 15 use Search::Query::Dialect::KSx::NOTWildcardQuery; 16 use Search::Query::Dialect::KSx::WildcardQuery; 8 17 9 18 our $VERSION = '0.01'; … … 24 33 my $query = Search::Query->parser( dialect => 'KSx' )->parse('foo'); 25 34 print $query; 35 my $ks_query = $query->as_ks_query(); 36 my $hits = $ks_searcher->hits( query => $ks_query ); 26 37 27 38 =head1 DESCRIPTION 28 39 29 Search::Query::Dialect::KSx supports the KinoSearch::QueryParser syntax. 40 Search::Query::Dialect::KSx extends the KinoSearch::QueryParser syntax 41 to support wildcards, proximity and ranges, in addition to the standard 42 Search::Query features. 30 43 31 44 =head1 METHODS … … 149 162 = $clause->{field} 150 163 ? ( $clause->{field} ) 151 : ( @{ $default_field ? @$default_field : [] });164 : ( defined $default_field ? @$default_field : () ); 152 165 153 166 # what value … … 256 269 } 257 270 271 =head2 as_ks_query 272 273 Returns the Dialect object as a KinoSearch::Search::Query-based object. 274 The Dialect object is walked and converted to a 275 KinoSearch::Searcher-compatible tree. 276 277 =cut 278 279 sub as_ks_query { 280 my $self = shift; 281 my $tree = shift || $self; 282 283 my @q; 284 foreach my $prefix ( '+', '', '-' ) { 285 my @clauses; 286 my $joiner = $op_map{$prefix}; 287 next unless exists $tree->{$prefix}; 288 for my $clause ( @{ $tree->{$prefix} } ) { 289 push( @clauses, $self->_ks_clause( $clause, $prefix ) ); 290 } 291 next if !@clauses; 292 293 my $ks_class = 'KinoSearch::Search::' . $joiner . 'Query'; 294 295 push @q, @clauses == 1 296 ? $clauses[0] 297 : $ks_class->new( children => [ grep {defined} @clauses ] ); 298 } 299 300 return @q == 1 301 ? $q[0] 302 : KinoSearch::Search::ANDQuery->new( children => \@q ); 303 } 304 305 sub _ks_clause { 306 my $self = shift; 307 my $clause = shift; 308 my $prefix = shift; 309 310 #warn dump $clause; 311 #warn "prefix = '$prefix'"; 312 313 if ( $clause->{op} eq '()' ) { 314 return $self->as_ks_query( $clause->{value} ); 315 } 316 317 # make sure we have a field 318 my $default_field = $self->default_field || $self->parser->default_field; 319 my @fields 320 = $clause->{field} 321 ? ( $clause->{field} ) 322 : ( defined $default_field ? @$default_field : () ); 323 324 # what value 325 my $value 326 = ref $clause->{value} 327 ? $clause->{value} 328 : $self->_doctor_value($clause); 329 330 # if we have no fields, we can't proceed, because KS 331 # requires a field for every term. 332 if ( !@fields ) { 333 croak 334 "No field specified for term '$value' -- set a default_field in Parser or Dialect"; 335 } 336 337 my $wildcard = $self->wildcard; 338 339 # normalize operator 340 my $op = $clause->{op} || ":"; 341 if ( $op eq '=' ) { 342 $op = ':'; 343 } 344 if ( $prefix eq '-' ) { 345 $op = '!' . $op; 346 } 347 if ( $value =~ m/\%/ ) { 348 $op = $prefix eq '-' ? '!~' : '~'; 349 } 350 351 my $quote = $clause->quote || ''; 352 353 my @buf; 354 NAME: for my $name (@fields) { 355 my $field = $self->_get_field($name); 356 357 if ( defined $field->callback ) { 358 push( @buf, $field->callback->( $field, $op, $value ) ); 359 next NAME; 360 } 361 362 #warn dump [ $name, $op, $quote, $value ]; 363 364 # invert fuzzy 365 if ( $op eq '!~' || ( $op eq '!:' and $value =~ m/[$wildcard\*\?]/ ) ) 366 { 367 $value .= $wildcard unless $value =~ m/\Q$wildcard/; 368 369 push( 370 @buf, 371 Search::Query::Dialect::KSx::NOTWildcardQuery->new( 372 field => $name, 373 term => $value, 374 ) 375 ); 376 } 377 378 # fuzzy 379 elsif ( $op eq '~' 380 || ( $op eq ':' and $value =~ m/[$wildcard\*\?]/ ) ) 381 { 382 $value .= $wildcard unless $value =~ m/\Q$wildcard/; 383 384 push( 385 @buf, 386 Search::Query::Dialect::KSx::WildcardQuery->new( 387 field => $name, 388 term => $value, 389 ) 390 ); 391 } 392 393 # invert 394 elsif ( $op eq '!:' ) { 395 push( 396 @buf, 397 KinoSearch::Search::NOTQuery->new( 398 field => $name, 399 term => $value, 400 ) 401 ); 402 } 403 404 # range 405 elsif ( $op eq '..' ) { 406 if ( ref $value ne 'ARRAY' or @$value != 2 ) { 407 croak "range of values must be a 2-element ARRAY"; 408 } 409 410 my $range_query = KinoSearch::Search::RangeQuery->new( 411 field => $name, 412 lower_term => $value->[0], 413 upper_term => $value->[1], 414 include_lower => 1, 415 include_upper => 1, 416 ); 417 418 push( @buf, $range_query ); 419 420 } 421 422 # invert range 423 elsif ( $op eq '!..' ) { 424 if ( ref $value ne 'ARRAY' or @$value != 2 ) { 425 croak "range of values must be a 2-element ARRAY"; 426 } 427 428 croak "NOT Range query not yet supported"; 429 } 430 431 # standard 432 else { 433 push( 434 @buf, 435 KinoSearch::Search::TermQuery->new( 436 field => $name, 437 term => $value, 438 ) 439 ); 440 } 441 } 442 if ( @buf == 1 ) { 443 return $buf[0]; 444 } 445 my $joiner = $prefix eq '-' ? 'AND' : 'OR'; 446 my $ks_class = 'KinoSearch::Search::' . $joiner . 'Query'; 447 return $ks_class->new( children => \@buf ); 448 } 449 258 450 =head2 field_class 259 451
Note: See TracChangeset
for help on using the changeset viewer.