File Coverage

lib/Net/API/CPAN/Filter.pm
Criterion Covered Total %
statement 41 170 24.1
branch 13 100 13.0
condition 3 51 5.8
subroutine 11 27 40.7
pod 17 18 94.4
total 85 366 23.2


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Meta CPAN API - ~/lib/Net/API/CPAN/Filter.pm
3             ## Version v0.1.0
4             ## Copyright(c) 2023 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2023/08/03
7             ## Modified 2023/08/03
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Net::API::CPAN::Filter;
15             BEGIN
16             {
17 3     3   111891 use strict;
  3         14  
  3         99  
18 3     3   24 use warnings;
  3         6  
  3         93  
19 3     3   549 use parent qw( Module::Generic );
  3         303  
  3         18  
20 3     3   122001 use vars qw( $VERSION );
  3         16  
  3         161  
21 3     3   62 our $VERSION = 'v0.1.0';
22             };
23              
24 3     3   21 use strict;
  3         5  
  3         58  
25 3     3   15 use warnings;
  3         8  
  3         6867  
26              
27             sub init
28             {
29 2     2 1 1410 my $self = shift( @_ );
30 2 50       120 $self->{aggs} = undef unless( CORE::exists( $self->{aggs} ) );
31 2 50       13 $self->{es} = undef unless( CORE::exists( $self->{es} ) );
32 2 50       8 $self->{fields} = undef unless( CORE::exists( $self->{fields} ) );
33 2 50       10 $self->{filter} = undef unless( CORE::exists( $self->{filter} ) );
34 2 50       7 $self->{from} = undef unless( CORE::exists( $self->{from} ) );
35 2 50       8 $self->{match_all} = 0 unless( CORE::exists( $self->{match_all} ) );
36 2 50       12 $self->{name} = undef unless( CORE::exists( $self->{name} ) );
37 2 50       10 $self->{query} = undef unless( CORE::exists( $self->{query} ) );
38 2 50       9 $self->{size} = undef unless( CORE::exists( $self->{size} ) );
39 2 50       8 $self->{sort} = undef unless( CORE::exists( $self->{sort} ) );
40 2 50       8 $self->{source} = undef unless( CORE::exists( $self->{source} ) );
41 2         6 $self->{_init_strict_use_sub} = 1;
42 2 50       15 $self->SUPER::init( @_ ) || return( $self->pass_error );
43 2         37844 $self->{_data} = {};
44 2         17 return( $self );
45             }
46              
47 0     0 1 0 sub aggregations { return( shift->aggs( @_ ) ); }
48              
49 0     0 1 0 sub aggs { return( shift->reset(@_)->_set_get_hash_as_mix_object( 'aggs', @_ ) ); }
50              
51             sub apply
52             {
53 0     0 1 0 my $self = shift( @_ );
54 0         0 my $hash = $self->_get_args_as_hash( @_ );
55 0 0       0 return( $self ) if( !scalar( keys( %$hash ) ) );
56            
57 0         0 foreach my $k ( keys( %$hash ) )
58             {
59 0         0 my $code;
60             # if( !CORE::exists( $dict->{ $k } ) )
61 0 0       0 if( !( $code = $self->can( $k ) ) )
62             {
63 0 0 0     0 warn( "No method \"$k\" found in class ", ( ref( $self ) || $self ), " when applying data to this object. Skipping it." ) if( $self->_is_warnings_enabled );
64 0         0 next;
65             }
66 0         0 $code->( $self, $hash->{ $_ } );
67             }
68 0         0 return( $self );
69             }
70              
71             sub as_hash
72             {
73 0     0 1 0 my $self = shift( @_ );
74 0 0 0     0 return( $self->{_data_cache} ) if( $self->{_data_cache} && !CORE::length( $self->{_reset} ) );
75 0         0 my $data = {};
76 0         0 my $es = $self->es;
77 0 0 0     0 if( defined( $es ) && $es->length )
78             {
79 0         0 $data = $es->as_hash;
80             }
81             else
82             {
83 0         0 $data->{query} = $self->query;
84 0 0       0 if( my $aggs = $self->aggs )
85             {
86 0         0 $data->{aggs} = $aggs;
87             }
88 0 0       0 if( my $fields = $self->fields )
89             {
90 0 0       0 $data->{fields} = [@$fields] if( !$fields->is_empty );
91             }
92 0 0       0 if( my $filter = $self->filter )
93             {
94 0         0 $data->{filter} = $filter;
95             }
96 0 0       0 if( my $sort = $self->sort )
97             {
98 0 0       0 $data->{sort} = [@$sort] if( !$sort->is_empty );
99             }
100 0 0       0 if( my $source = $self->source )
101             {
102 0         0 $data->{_source} = $source;
103             }
104 0 0       0 if( my $name = $self->name )
105             {
106 0 0 0     0 if( exists( $data->{filter} ) &&
      0        
107             exists( $data->{filter}->{terms} ) &&
108             ref( $data->{filter}->{terms} ) eq 'HASH' )
109             {
110 0         0 $data->{filter}->{terms}->{_name} = $name;
111             }
112             }
113 0 0       0 if( defined( my $from = $self->from ) )
114             {
115 0         0 $data->{from} = $from;
116             }
117 0 0       0 if( defined( my $size = $self->size ) )
118             {
119 0         0 $data->{size} = $size;
120             }
121             }
122 0         0 $self->{_data_cache} = $data;
123 0         0 CORE::delete( $self->{_reset} );
124 0         0 return( $data );
125             }
126              
127             sub as_json
128             {
129 0     0 1 0 my $self = shift( @_ );
130 0         0 my $opts = $self->_get_args_as_hash( @_ );
131 0         0 my $data = $self->as_hash;
132 0         0 my $j = $self->new_json;
133 0 0       0 $j = $j->pretty if( $opts->{pretty} );
134 0 0       0 $j = $j->canonical if( $opts->{sort} );
135 0         0 my $json;
136 0         0 local $@;
137             # try-catch
138             eval
139 0         0 {
140 0 0 0     0 if( exists( $opts->{encoding} ) &&
      0        
      0        
141             defined( $opts->{encoding} ) &&
142             ( lc( $opts->{encoding} ) eq 'utf-8' || lc( $opts->{encoding} ) eq 'utf8' ) )
143             {
144 0         0 $json = $j->utf8->encode( $data );
145             }
146             else
147             {
148 0         0 $json = $j->encode( $data );
149             }
150             };
151 0 0       0 if( $@ )
152             {
153 0         0 return( $self->error( "Error encoding to JSON: $@" ) );
154             }
155 0         0 return( $json );
156             }
157              
158 0     0 1 0 sub es { return( shift->reset(@_)->_set_get_hash_as_mix_object( 'es', @_ ) ); }
159              
160 0     0 1 0 sub fields { return( shift->reset(@_)->_set_get_array_as_object( 'fields', @_ ) ); }
161              
162 0     0 1 0 sub filter { return( shift->reset(@_)->_set_get_hash_as_mix_object( 'filter', @_ ) ); }
163              
164 0     0 1 0 sub from { return( shift->reset(@_)->_set_get_number( 'from', @_ ) ); }
165              
166 1     1 1 189 sub match_all { return( shift->reset(@_)->_set_get_number( 'match_all', @_ ) ); }
167              
168 0     0 1 0 sub name { return( shift->reset(@_)->_set_get_scalar_as_object( 'name', @_ ) ); }
169              
170             sub query
171             {
172 0     0 1 0 my $self = shift( @_ );
173 0 0       0 if( @_ )
174             {
175 0         0 my $query;
176 0         0 my $this = shift( @_ );
177 0         0 my $opts = $self->_get_args_as_hash( @_ );
178 0 0       0 return( $self->error( "Argument to query must be an hash reference." ) ) if( ref( $this ) ne 'HASH' );
179 0     0   0 $self->message( 5, "Query provided is -> ", sub{ $self->Module::Generic::dump( $this ) } );
  0         0  
180 0         0 $self->reset(1);
181 0         0 my @es_ops = qw( bool should );
182 0         0 my $es_ops = {};
183 0         0 @$es_ops{ @es_ops } = (1) x scalar( @es_ops );
184 0         0 my @operators = ( 'all', 'bool', 'either', 'not', 'must', 'shall', 'shall not', 'should' );
185 0         0 my $op_re = join( '|', @operators );
186 0         0 my $equi =
187             {
188             all => 'must',
189             either => 'should',
190             not => 'must_not',
191             };
192 0         0 my $build;
193             $build = sub
194             {
195 0     0   0 my $ref = shift( @_ );
196 0         0 $self->message( 5, "Top properties are: ", sub{ join( ', ', sort( keys( %$ref ) ) ) } );
  0         0  
197 0         0 my $q = {};
198 0 0       0 if( scalar( grep( /^($op_re)$/, keys( %$ref ) ) ) )
199             {
200 0         0 foreach my $op ( @operators )
201             {
202 0 0       0 next unless( exists( $ref->{ $op } ) );
203 0         0 my $def = delete( $ref->{ $op } );
204             # No need for transformation
205 0 0       0 if( exists( $es_ops->{ $op } ) )
206             {
207 0         0 $q->{ $op } = $def;
208 0         0 next;
209             }
210 0 0       0 $def = [$def] if( ref( $def ) eq 'HASH' );
211 0 0       0 if( !$self->_is_array( $def ) )
212             {
213 0         0 return( $self->error( "Invalid parameter \"$op\" value provided (", overload::StrVal( $def ), "). I was expecting an hash or an array reference." ) );
214             }
215            
216 0         0 my $sub_q = [];
217 0         0 foreach my $sub_def ( @$def )
218             {
219 0   0     0 my $this_q = $build->( $sub_def ) || return( $self->pass_error );
220 0         0 push( @$sub_q, $this_q );
221             }
222            
223 0         0 $q->{bool} = {};
224 0   0     0 $q->{bool}->{ $equi->{ $op } // $op } = $sub_q;
225             }
226             }
227             else
228             {
229 0         0 $self->message( 5, "Top properties do not contain either of ", sub{ join( ', ', @operators ) } );
  0         0  
230 0         0 my $n = scalar( keys( %$ref ) );
231 0 0       0 return( $self->error( "Wrong number of keys (${n}). Query element should have only 1 key. Insted I got: ", sub{ $self->Module::Generic::dump( $ref ) } ) ) if( $n > 1 );
  0         0  
232 0         0 my $key = [keys( %$ref )]->[0];
233 0         0 my $val = $ref->{ $key };
234             # Example:
235             # $ref =
236             # {
237             # status =>
238             # {
239             # value => "latest",
240             # boost => 2.0
241             # }
242             # }
243 0 0 0     0 if( ref( $val ) eq 'HASH' )
    0 0        
244             {
245 0         0 $q->{term} = $ref;
246             }
247             # or, just:
248             # $ref = { status => "latest" }
249             # or, maybe:
250             # $ref = { name => "Some*" }
251             elsif( ( !ref( $val ) || $self->_can_overload( $val => '""' ) ) && "$val" =~ /[\w\*]/ )
252             {
253 0 0       0 my $type = ( "$val" =~ /[*?]/ ? 'wildcard' : 'term' );
254 0         0 $q->{ $type } = $ref;
255             }
256             else
257             {
258 0         0 return( $self->error( "Wrong value for \"$key\". I do not know what to do with '", overload::StrVal( $val ), "'" ) );
259             }
260             }
261 0         0 return( $q );
262 0         0 };
263 0   0     0 $query = $build->( $this ) || return( $self->pass_error );
264 0         0 $self->{query} = $query;
265             }
266            
267 0 0       0 unless( $self->{query} )
268             {
269 0         0 my $match_all = $self->match_all;
270 0 0       0 if( $match_all )
271             {
272 0 0       0 if( $match_all == 1 )
273             {
274 0         0 $self->{query} = { match_all => {} };
275             }
276             else
277             {
278 0         0 $self->{query} = { match_all => { boost => $match_all } };
279             }
280             }
281             }
282 0         0 return( $self->{query} );
283             }
284              
285             sub reset
286             {
287 5     5 1 18 my $self = shift( @_ );
288 5 50 33     74 if( (
      100        
289             !exists( $self->{_reset} ) ||
290             !defined( $self->{_reset} ) ||
291             !CORE::length( $self->{_reset} )
292             ) && scalar( @_ ) )
293             {
294 1         4 $self->{_reset} = scalar( @_ );
295             }
296 5         34 return( $self );
297             }
298              
299 4     4 1 23 sub size { return( shift->reset(@_)->_set_get_number( 'size', @_ ) ); }
300              
301 0     0 1   sub sort { return( shift->reset(@_)->_set_get_array_as_object( 'sort', @_ ) ); }
302              
303             sub source
304             {
305 0     0 1   my $self = shift( @_ );
306 0 0         if( @_ )
307             {
308 0 0 0       if( !defined( $_[0] ) )
    0 0        
      0        
309             {
310 0           $self->{source} = undef;
311 0           $self->reset(1);
312             }
313             elsif( $self->_is_array( $_[0] ) ||
314             !ref( $_[0] ) ||
315             ( ref( $_[0] ) && $self->_can_overload( $_[0] => '""' ) ) )
316             {
317 0           $self->{source} = shift( @_ );
318 0           $self->reset(1);
319             }
320             else
321             {
322 0           return( $self->error( "A source can only be either a string or an array reference." ) );
323             }
324             }
325 0           return( $self->{source} );
326             }
327              
328 0     0 0   sub TO_JSON { return( shift->as_hash ); }
329              
330             1;
331             # NOTE: POD
332             __END__
333              
334             =encoding utf-8
335              
336             =head1 NAME
337              
338             Net::API::CPAN::Filter - Meta CPAN API
339              
340             =head1 SYNOPSIS
341              
342             use Net::API::CPAN::Filter;
343             my $this = Net::API::CPAN::Filter->new(
344             query => {
345             regexp => { name => 'HTTP.*' },
346             },
347             ) || die( Net::API::CPAN::Filter->error, "\n" );
348              
349             =head1 VERSION
350              
351             v0.1.0
352              
353             =head1 DESCRIPTION
354              
355             This class is designed to facilitate the forming of an Elastic Search query and store its various components as an object of this class, so it can possibly be re-used or shared.
356              
357             You can pass arguments to the methods L</aggs>, L</fields>, L</filter>, L</from>, L</match_all>, L</query>, L</size>, L</sort>, L</source> to affect the production of the query.
358              
359             Alternatively, you can pass an hash reference of a fully formed Elastic Search query directly to L</es> to take precedence over all the other methods.
360              
361             Calling L</as_hash> will collate all the components and cache the result. If any information is changed using any of the methods in this class, it will remove the cached hash produced by L</as_hash>
362              
363             You can get a resulting C<JSON> by calling L</as_json>, which in turn, calls L</as_hash>
364              
365             As far as it is documented in the API documentation, Meta CPAN uses version C<2.4> of Elastic Search, and the methods documentation herein reflect that.
366              
367             =head1 METHODS
368              
369             =head2 aggregations
370              
371             This is an alias for L</aggs>
372              
373             =head2 aggs
374              
375             Sets or gets an hash reference of query L<aggregations (post filter)|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-post-filter.html>. It returns an L<hash object|Module::Generic::Hash>, or C<undef>, if nothing was set.
376              
377             Example from L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-post-filter.html>
378              
379             {
380             aggs => {
381             models => {
382             terms => { field => "model" },
383             },
384             },
385             query => {
386             bool => {
387             filter => [
388             {
389             term => { color => "red" },
390             },
391             {
392             term => { brand => "gucci" },
393             },
394             ],
395             },
396             },
397             }
398              
399             See also L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-post-filter.html>, and L<here|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-aggregations-bucket-terms-aggregation.html>
400              
401             =head2 apply
402              
403             Provided with an hash or hash reference of parameters and this will apply each of the value to the method matching its corresponding key if that method exists.
404              
405             It returns the current object for chaining.
406              
407             =head2 as_hash
408              
409             Read-only. Returns the various components of the query as an hash reference.
410              
411             The resulting hash of data is cached so you can call it multiple time without additional overhead. Any change passed to any methods here will reset that cache.
412              
413             =head2 as_json
414              
415             my $json = $filter->as_json;
416             my $json_in_utf8 = $filter->as_json( encoding => 'utf-8' );
417              
418             Read-only. Returns the various components of the query as C<JSON> data encoded in L<Perl internal utf-8 encoding|perlunicode>.
419              
420             If an hash or hash reference of options is provided with a property encoding set to C<utf-8> or C<utf8>, then the JSON data returned will be encoded in C<utf-8>
421              
422             =head2 es
423              
424             This takes an hash reference of L<Elastic Search query parameters|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-filter-context.html>.
425              
426             See L</"ELASTIC SEARCH QUERY"> for a brief overview of valid parameters.
427              
428             Otherwise you are encouraged to call L</query> which will format the Elastic Search query for you.
429              
430             Returns an L<hash object|Module::Generic::Hash>
431              
432             =head2 fields
433              
434             Sets or gets an array of fields onto which the query will be applied.
435              
436             It returns an L<array object|Module::Generic::Array>
437              
438             {
439             query => {
440             terms => { name => "Japan Folklore" }
441             },
442             fields => [qw( name abstract distribution )],
443             }
444              
445             Field names can also contain wildcard:
446              
447             {
448             query => {
449             terms => { name => "Japan Folklore" }
450             },
451             fields => [qw( name abstract dist* )],
452             }
453              
454             Importance of some fields can also be boosted using the caret notation C<^>
455              
456             {
457             query => {
458             terms => { name => "Japan Folklore" }
459             },
460             fields => [qw( name^3 abstract dist* )],
461             }
462              
463             Here, the field C<name> is treated as 3 times important as the others.
464              
465             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-fields.html> for more information.
466              
467             =head2 filter
468              
469             Sets or gets an hash of filter to affect the Elastic Search query result.
470              
471             {
472             query => {
473             bool => {
474             must => [
475             { match => { name => "Folklore-Japan-v1.2.3" }},
476             { match => { abstract => "Japan Folklore Object Class" }}
477             ],
478             filter => [
479             { term => { status => "latest" }},
480             { range => { date => { gte => "2023-07-01" }}}
481             ]
482             }
483             }
484             }
485              
486             It returns an L<hash object|Module::Generic::Hash>
487              
488             =head2 from
489              
490             Sets or gets a positive integer to return the desired results page. It returns the current value, if any, as a L<number object|Module::Generic::Number>, or C<undef> if there is no value set.
491              
492             {
493             from => 0,
494             query => {
495             term => { user => "kimchy" },
496             },
497             size => 10,
498             }
499              
500             As per the Elastic Search documentation, "[p]agination of results can be done by using the C<from> and C<size> parameters. The C<from> parameter defines the offset from the first result you want to fetch. The C<size> parameter allows you to configure the maximum amount of hits to be returned".
501              
502             For example, on a size of C<10> elements per page, the first page would start at offset a.k.a C<from> C<0> and end at offset C<9> and page 2 at C<from> C<10> till C<19>, thus to get the second page you would set the value for C<from> to C<10>
503              
504             See also the more efficient L<scroll approach|Net::API::CPAN::Scroll> to pagination of query results.
505              
506             Keep in mind this is different from the C<from> option supported in some endpoints of the MetaCPAN API, which would typically starts at 1 instead of 0.
507              
508             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-from-size.html> for more information.
509              
510             =head2 match_all
511              
512             # Enabled
513             $filter->match_all(1);
514             # Disabled (default)
515             $filter->match_all(0);
516             # or
517             $filter->match_all(undef);
518             # or with explicit score
519             $filter->match_all(1.12);
520              
521             Boolean. If true, this will match all documents by Elastic Search with an identical score of C<1.0>
522              
523             If the value provided is a number other than C<1> or C<0>, then it will be interpreted as an explicit score to use instead of the default C<1.0>
524              
525             For example:
526              
527             $filter->match_all(1.12)
528              
529             would produce:
530              
531             { match_all => { boost => 1.2 }}
532              
533             See L<Elastic Search|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-all-query.html> for more information.
534              
535             =head2 name
536              
537             Sets or gets the L<optional query name|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-named-queries-and-filters.html>. It always returns a L<scalar object|Module::Generic::Scalar>
538              
539             If set, it will be added to the C<filter>
540              
541             {
542             bool => {
543             filter => {
544             terms => { _name => "test", "name.last" => [qw( banon kimchy )] },
545             },
546             should => [
547             {
548             match => { "name.first" => { _name => "first", query => "shay" } },
549             },
550             {
551             match => { "name.last" => { _name => "last", query => "banon" } },
552             },
553             ],
554             },
555             }
556              
557             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-named-queries-and-filters.html> for more information.
558              
559             =head2 query
560              
561             This takes an hash reference of parameters and format the query in compliance with Elastic Search. You can provide directly the Elastic Search structure by calling L</es> and providing it the proper hash reference of parameters.
562              
563             Queries can be straightforward such as:
564              
565             { name => 'Taro Momo' }
566              
567             or
568              
569             { pauseid => 'MOMOTARO' }
570              
571             or using simple regular expression:
572              
573             { name => 'Taro *' }
574              
575             This would find all the people whose name start with C<Taro>
576              
577             To produce more complex search queries, you can use some special keywords: C<all>, C<either> and C<not>, which correspond respectively to Elastic Search C<must>, C<should>, and C<must_not> and you can use the Elastic Search keywords interchangeably if you prefer. Thus:
578              
579             {
580             either => [
581             { name => 'John *' },
582             { name => 'Peter *' },
583             ]
584             }
585              
586             is the same as:
587              
588             {
589             should => [
590             { name => 'John *' },
591             { name => 'Peter *' },
592             ]
593             }
594              
595             and
596              
597             {
598             all => [
599             { name => 'John *' },
600             { email => '*gmail.com' },
601             ]
602             }
603              
604             is the same as:
605              
606             {
607             must => [
608             { name => 'John *' },
609             { email => '*gmail.com' },
610             ]
611             }
612              
613             Likewise
614              
615             {
616             either => [
617             { name => 'John *' },
618             { name => 'Peter *' },
619             ],
620             not => [
621             { email => '*gmail.com' },
622             ],
623             }
624              
625             can also be expressed as:
626              
627             {
628             should => [
629             { name => 'John *' },
630             { name => 'Peter *' },
631             ],
632             must_not => [
633             { email => '*gmail.com' },
634             ],
635             }
636              
637             =head2 reset
638              
639             When called with some arguments, no matter their value, this will reset the cached hash reference computed by L</as_hash>
640              
641             It returns the current object for chaining.
642              
643             =head2 size
644              
645             Sets or gets a positive integer to set the maximum number of hits of query results. It returns the current value, if any, as a L<number object|Module::Generic::Number>, or C<undef> if there is no value set.
646              
647             See L</from> for more information.
648              
649             {
650             from => 0,
651             query => {
652             term => { user => "kimchy" },
653             },
654             size => 10,
655             }
656              
657             See also the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-from-size.html>
658              
659             =head2 sort
660              
661             Sets or gets an array reference of C<sort> parameter to affect the order of the query results.
662              
663             It always returns an L<array object|Module::Generic::Array>, which might be empty if nothing was specified.
664              
665             {
666             query => {
667             term => { user => "kimchy" },
668             },
669             sort => [
670             {
671             post_date => { order => "asc" },
672             },
673             "user",
674             { name => "desc" },
675             { age => "desc" },
676             "_score",
677             ],
678             }
679              
680             The order option can have the following values:
681              
682             =over 4
683              
684             =item * C<asc>
685              
686             Sort in ascending order
687              
688             =item * C<desc>
689              
690             Sort in descending order
691              
692             =back
693              
694             Elastic Search supports sorting by array or multi-valued fields. The mode option controls what array value is picked for sorting the document it belongs to. The mode option can have the following values:
695              
696             {
697             query => {
698             term => { user => "kimchy" },
699             },
700             sort => [
701             {
702             price => {
703             order => "asc",
704             mode => "avg"
705             }
706             }
707             ]
708             }
709              
710             =over 4
711              
712             =item * C<min>
713              
714             Pick the lowest value.
715              
716             =item * C<max>
717              
718             Pick the highest value.
719              
720             =item * C<sum>
721              
722             Use the sum of all values as sort value. Only applicable for number based array fields.
723              
724             =item * C<avg>
725              
726             Use the average of all values as sort value. Only applicable for number based array fields.
727              
728             =item * C<median>
729              
730             Use the median of all values as sort value. Only applicable for number based array fields.
731              
732             =back
733              
734             You can also allow to sort by geo distance with C<_geo_distance>, such as:
735              
736             {
737             query => {
738             term => { user => "kimchy" },
739             },
740             sort => [
741             {
742             _geo_distance => {
743             distance_type => "sloppy_arc",
744             mode => "min",
745             order => "asc",
746             "pin.location" => [-70, 40],
747             # or, as lat/long
748             # "pin.location" => {
749             # lat => 40,
750             # lon => -70
751             # },
752             # or, as string
753             # "pin.location" => "40,-70",
754             # or, as GeoHash
755             # "pin.location" => "drm3btev3e86",
756             unit => "km",
757             },
758             },
759             ],
760             }
761              
762             See also L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-sort.html>
763              
764             =head2 source
765              
766             This sets or gets a string or an array reference of query L<source filtering|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-source-filtering.html>.
767              
768             It returns the current value, which may be C<undef> if nothing was specified.
769              
770             By default Elastic Search returns the contents of the C<_source> field unless you have used the L<fields|/fields> parameter or if the C<_source> field is disabled.
771              
772             You can set it to false to disable it. A false value can be C<0>, or an empty string C<"">, but not C<undef>, which will disable this option entirely.
773              
774             $filter->query({
775             user => 'kimchy'
776             });
777             $filter->source(0);
778              
779             would produce the following hash returned by L</as_hash>:
780              
781             {
782             _source => \0,
783             query => {
784             term => { user => "kimchy" },
785             },
786             }
787              
788             For complete control, you can specify both C<include> and C<exclude> patterns:
789              
790             $filter->query({
791             user => 'kimchy'
792             });
793             $filter->source({
794             exclude => ["*.description"],
795             include => ["obj1.*", "obj2.*"],
796             });
797              
798             would produce the following hash returned by L</as_hash>:
799              
800             {
801             _source => { exclude => ["*.description"], include => ["obj1.*", "obj2.*"] },
802             query => {
803             term => { user => "kimchy" },
804             },
805             }
806              
807             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-source-filtering.html> for more information.
808              
809             =head1 ELASTIC SEARCH QUERY
810              
811             =head2 Query and Filter
812              
813             Example:
814              
815             The following will instruct Meta CPAN Elastic Search to find module release where all the following conditions are met:
816              
817             =over 4
818              
819             =item * The C<name> field contains the word C<Folklore-Japan-v1.2.3>.
820              
821             =item * The C<abstract> field contains C<Japan Folklore Object Class>.
822              
823             =item * The C<status> field contains the exact word C<latest>.
824              
825             =item * The C<date> field contains a date from 1 July 2023 onwards.
826              
827             =back
828              
829             {
830             query => {
831             bool => {
832             must => [
833             { match => { name => "Folklore-Japan-v1.2.3" }},
834             { match => { abstract => "Japan Folklore Object Class" }}
835             ],
836             filter => [
837             { term => { status => "latest" }},
838             { range => { date => { gte => "2023-07-01" }}}
839             ]
840             }
841             }
842             }
843              
844             =head2 Match all
845              
846             { match_all => {} }
847              
848             or with an explicit score of C<1.12>
849              
850             { match_all => { boost => 1.12 } }
851              
852             =head2 Match Query
853              
854             {
855             match => { name => "Folklore-Japan-v1.2.3" }
856             }
857              
858             or
859              
860             {
861             match => {
862             name => {
863             query => "Folklore-Japan-v1.2.3",
864             # Defaults to 'or'
865             operator => 'and',
866             # The minimum number of optional 'should' clauses to match
867             minimum_should_match => 1,
868             # Set to true (\1 is translated as 'true' in JSON) to ignore exceptions caused by data-type mismatches
869             lenient => \1,
870             # Set the fuzziness value: 0, 1, 2 or AUTO
871             fuzziness => 'AUTO',
872             # True by default
873             fuzzy_transpositions => 1,
874             # 'none' or 'all'; defaults to 'none'
875             zero_terms_query => 'all',
876             cutoff_frequency => 0.001,
877             }
878             }
879             }
880              
881             =over 4
882              
883             =item * C<minimum_should_match>
884              
885             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-minimum-should-match.html> for valid value for C<minimum_should_match>
886              
887             =item * C<fuzziness>
888              
889             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/common-options.html#fuzziness> for valid values
890              
891             =item * C<zero_terms_query>
892              
893             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-query.html#query-dsl-match-query-zero> for valid values
894              
895             =item * C<cutoff_frequency>
896              
897             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-query.html#query-dsl-match-query-cutoff> for valid values
898              
899             =back
900              
901             See also the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-query.html> on C<match> query for more information on its valid parameters.
902              
903             =head2 Match Phrase
904              
905             {
906             match_phrase => {
907             abstract => "Japan Folklore Object Class",
908             }
909             }
910              
911             which is the same as:
912              
913             {
914             match => {
915             abstract => {
916             query => "Japan Folklore Object Class",
917             type => 'phrase',
918             }
919             }
920             }
921              
922             =head2 Match Phrase Prefix
923              
924             As per L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-query.html#query-dsl-match-query-phrase-prefix>, this is a poor-man’s autocomplete.
925              
926             {
927             match_phrase_prefix => {
928             abstract => "Japan Folklore O"
929             }
930             }
931              
932             It is designed to allow expansion on the last term of the query. The maximum number of expansion is controlled with the parameter C<max_expansions>
933              
934             {
935             match_phrase_prefix => {
936             abstract => {
937             query => "Japan Folklore O",
938             max_expansions => 10,
939             }
940             }
941             }
942              
943             The L<documentation recommends|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-suggesters-completion.html> the use of the completion suggester instead.
944              
945             =head2 Multi Match Query
946              
947             This performs a query on multiple fields:
948              
949             {
950             multi_match => {
951             query => 'Japan Folklore',
952             fields => [qw( name abstract distribution )],
953             }
954             }
955              
956             Field names can contain wildcard:
957              
958             {
959             multi_match => {
960             query => 'Japan Folklore',
961             fields => [qw( name abstract dist* )],
962             }
963             }
964              
965             Importance of some fields can also be boosted using the caret notation C<^>
966              
967             {
968             multi_match => {
969             query => 'Japan Folklore',
970             fields => [qw( name^3 abstract dist* )],
971             }
972             }
973              
974             Here, the field C<name> is treated as 3 times important as the others.
975              
976             To affect the way the multiple match query is performed, you can set the C<type> value to C<best_fields>, C<most_fields>, C<cross_fields>, C<phrase> or C<phrase_prefix>
977              
978             {
979             multi_match => {
980             query => 'Japan Folklore',
981             fields => [qw( name^3 abstract dist* )],
982             type => 'best_fields',
983             }
984             }
985              
986             It accepts the other same parameters as in the L<match query/"Query and Filter">
987              
988             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-multi-match-query.html#multi-match-types> for more details.
989              
990             =head2 Common Terms Query
991              
992             As per L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-common-terms-query.html#query-dsl-common-terms-query>, the "C<common> terms query is a modern alternative to stopwords which improves the precision and recall of search results (by taking stopwords into account), without sacrificing performance."
993              
994             {
995             common => {
996             abstract => {
997             query => 'Japan Folklore',
998             cutoff_frequency => 0.001,
999             }
1000             }
1001             }
1002              
1003             The number of terms which should match can be controlled with the C<minimum_should_match>
1004              
1005             See the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-common-terms-query.html> for more information.
1006              
1007             =head2 Query String Query
1008              
1009             This leverages the parser in order to parse the content of the query.
1010              
1011             {
1012             query_string => {
1013             default_field => "abstract",
1014             query => "this AND that OR thus",
1015             fields => [qw( abstract name )],
1016             # Default is 'OR'
1017             default_operator => 'AND',
1018             # \1 (true) or \0 (false)
1019             allow_leading_wildcard => \1,
1020             # Default to true
1021             lowercase_expanded_terms => \1,
1022             # Default to true
1023             enable_position_increments => \1,
1024             # Defaults to 50
1025             fuzzy_max_expansions => 10,
1026             # Defaults to 'AUTO'
1027             fuzziness => 'AUTO',
1028             # Defaults to 0
1029             fuzzy_prefix_length => 0,
1030             # Defaults to 0
1031             phrase_slop => 0,
1032             # Defaults to 1.0
1033             boost => 0,
1034             # Defaults to true
1035             analyze_wildcard => \1,
1036             # Defaults to false
1037             auto_generate_phrase_queries => \0,
1038             # Defaults to 10000
1039             max_determinized_states => 10000,
1040             minimum_should_match => 2,
1041             # Defaults to true,
1042             lenient => \1,
1043             locale => 'ROOT',
1044             time_zone => 'Asia/Tokyo',
1045             }
1046             }
1047              
1048             L<Wildcard searches|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#_wildcards> can be run on individual terms, using C<?> to replace a single character, and C<*> to replace zero or more characters:
1049              
1050             qu?ck bro*
1051              
1052             L<Regular expression|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-regexp-query.html#regexp-syntax> can also be used:
1053              
1054             As per the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#_regular_expressions>, "regular expression patterns can be embedded in the query string by wrapping them in forward-slashes ("/")":
1055              
1056             name:/joh?n(ath[oa]n)/
1057              
1058             Fuzziness, i.e., terms that are similar to, but not exactly like our search terms, can be expressed with the fuzziness operator:
1059              
1060             quikc~ brwn~ foks~
1061              
1062             An edit distance can be specified:
1063              
1064             quikc~1
1065             "fox quick"~5
1066              
1067             A range can be specified for date, numeric or string fields. Inclusive ranges are specified with square brackets C<[min TO max]> and exclusive ranges with curly brackets C<{min TO max}>.
1068              
1069             All days in 2023:
1070              
1071             date:[2023-01-01 TO 2023-12-31]
1072              
1073             Numbers 1..5
1074              
1075             count:[1 TO 5]
1076              
1077             Tags between C<alpha> and C<omega>, excluding C<alpha> and C<omega>:
1078              
1079             tag:{alpha TO omega}
1080              
1081             Numbers from 10 upwards
1082              
1083             count:[10 TO *]
1084              
1085             Dates before 2023
1086              
1087             date:{* TO 2023-01-01}
1088              
1089             Numbers from 1 up to but not including 5
1090              
1091             count:[1 TO 5}
1092              
1093             Ranges with one side unbounded can use the following syntax:
1094              
1095             age:>10
1096             age:>=10
1097             age:<10
1098             age:<=10
1099              
1100             age:(>=10 AND <20)
1101             age:(+>=10 +<20)
1102              
1103             But better to use a L<range query|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-range-query.html>:
1104              
1105             {
1106             range => {
1107             age => {
1108             gte => 10,
1109             lte => 20,
1110             boost => 2.0
1111             }
1112             }
1113             }
1114              
1115             L<Boolean operators|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#_boolean_operators>:
1116              
1117             quick brown +fox -news
1118              
1119             =over
1120              
1121             =item * fox must be present
1122              
1123             =item * news must not be present
1124              
1125             =item * quick and brown are optional — their presence increases the relevance
1126              
1127             =back
1128              
1129             L<Grouping|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#_grouping>
1130              
1131             (quick OR brown) AND fox
1132              
1133             status:(active OR pending) title:(full text search)^2
1134              
1135             See the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#query-dsl-query-string-query> and the L<query string syntax|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#query-string-syntax> for more information.
1136              
1137             L<Multi field|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-query-string-query.html#_multi_field>
1138              
1139             {
1140             query_string => {
1141             fields => [qw( abstract name )],
1142             query => "this AND that"
1143             }
1144             }
1145              
1146             is equivalent to:
1147              
1148             {
1149             query_string => {
1150             query => "(abstract:this OR name:this) AND (abstract:that OR name:that)"
1151             }
1152             }
1153              
1154             "Simple wildcard can also be used to search "within" specific inner elements of the document":
1155              
1156             {
1157             query_string => {
1158             fields => ["metadata.*"],
1159             # or, even, to give 5 times more importance of sub elements of metadata
1160             fields => [qw( abstract metadata.*^5 )],
1161             query => "this AND that OR thus",
1162             use_dis_max => \1,
1163             }
1164             }
1165              
1166             =head2 Field names
1167              
1168             Field names can contain query syntax, such as:
1169              
1170             where the C<status> field contains C<latest>
1171              
1172             status:latest
1173              
1174             where the C<abstract> field contains quick or brown. If you omit the OR operator the default operator will be used
1175              
1176             abstract:(quick OR brown)
1177             abstract:(quick brown)
1178              
1179             where the C<author> field contains the exact phrase C<john smith>
1180              
1181             author:"John Smith"
1182              
1183             where any of the fields C<metadata.abstract>, C<metadata.name> or C<metadata.date> contains C<quick> or C<brown> (note how we need to escape the C<*> with a backslash):
1184              
1185             metadata.\*:(quick brown)
1186              
1187             where the field C<resources.bugtracker> has no value (or is missing):
1188              
1189             _missing_:resources.bugtracker
1190              
1191             where the field C<resources.repository> has any non-null value:
1192              
1193             _exists_:resources.repository
1194              
1195             =head2 Simple Query String Query
1196              
1197             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-simple-query-string-query.html#query-dsl-simple-query-string-query> for more information.
1198              
1199             Those queries will never throw an exception and discard invalid parts.
1200              
1201             {
1202             simple_query_string => {
1203             query => "\"fried eggs\" +(eggplant | potato) -frittata",
1204             analyzer => "snowball",
1205             fields => [qw( body^5 _all )],
1206             default_operator => "and",
1207             }
1208             }
1209              
1210             Supported special characters:
1211              
1212             =over 4
1213              
1214             =item * C<+> signifies AND operation
1215              
1216             =item * C<|> signifies OR operation
1217              
1218             =item * C<-> negates a single token
1219              
1220             =item * C<"> wraps a number of tokens to signify a phrase for searching
1221              
1222             =item * C<*> at the end of a term signifies a prefix query
1223              
1224             =item * C<(> and C<)> signify precedence
1225              
1226             =item * C<~N> after a word signifies edit distance (fuzziness)
1227              
1228             =item * C<~N> after a phrase signifies slop amount
1229              
1230             =back
1231              
1232             L<Flags|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-simple-query-string-query.html#_flags> can be specified to indicate which features to enable when parsing:
1233              
1234             {
1235             simple_query_string => {
1236             query => "foo | bar + baz*",
1237             flags => "OR|AND|PREFIX",
1238             }
1239             }
1240              
1241             The available flags are: C<ALL>, C<NONE>, C<AND>, C<OR>, C<NOT>, C<PREFIX>, C<PHRASE>, C<PRECEDENCE>, C<ESCAPE>, C<WHITESPACE>, C<FUZZY>, C<NEAR>, and C<SLOP>
1242              
1243             =head2 Term Queries
1244              
1245             {
1246             term => { author => "John Doe" }
1247             }
1248              
1249             A C<boost> parameter can also be used to give a term more importance:
1250              
1251             {
1252             query => {
1253             bool => {
1254             should => [
1255             {
1256             term => {
1257             status => {
1258             value => "latest",
1259             boost => 2.0
1260             }
1261             }
1262             },
1263             {
1264             term => {
1265             status => "deprecated"
1266             }
1267             }]
1268             }
1269             }
1270             }
1271              
1272             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-term-query.html#query-dsl-term-query> for more information.
1273              
1274             =head2 Terms Query
1275              
1276             {
1277             constant_score => {
1278             filter => {
1279             terms => { pauseid => [qw( momotaro kintaro )]}
1280             }
1281             }
1282             }
1283              
1284             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-terms-query.html#query-dsl-terms-query> for more information.
1285              
1286             =head2 Range Query
1287              
1288             {
1289             range => {
1290             age => {
1291             gte => 10,
1292             lte => 20,
1293             boost => 2.0,
1294             }
1295             }
1296             }
1297              
1298             The C<range> query accepts the following parameters:
1299              
1300             =over 4
1301              
1302             =item * C<gte>
1303              
1304             Greater-than or equal to
1305              
1306             =item * C<gt>
1307              
1308             Greater-than
1309              
1310             =item * C<lte>
1311              
1312             Less-than or equal to
1313              
1314             =item * C<lt>
1315              
1316             Less-than
1317              
1318             =item * C<boost>
1319              
1320             Sets the boost value of the query, defaults to C<1.0>
1321              
1322             =back
1323              
1324             When using range on a date, ranges can be specified using L<Date Math|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/common-options.html#date-math>:
1325              
1326             =over 4
1327              
1328             =item * C<+1h>
1329              
1330             Add one hour
1331              
1332             =item * C<-1d>
1333              
1334             Subtract one day
1335              
1336             =item * C</d>
1337              
1338             Round down to the nearest day
1339              
1340             =back
1341              
1342             Supported time units are: C<y> (year), C<M> (month), C<w> (week), C<d> (day), C<h> (hour), C<m> (minute), and C<s> (second).
1343              
1344             For example:
1345              
1346             =over 4
1347              
1348             =item * C<now+1h>
1349              
1350             The current time plus one hour, with ms resolution.
1351              
1352             =item * C<now+1h+1m>
1353              
1354             The current time plus one hour plus one minute, with ms resolution.
1355              
1356             =item * C<now+1h/d>
1357              
1358             The current time plus one hour, rounded down to the nearest day.
1359              
1360             =item * C<2023-01-01||+1M/d>
1361              
1362             C<2023-01-01> plus one month, rounded down to the nearest day.
1363              
1364             =back
1365              
1366             L<Date formats|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-range-query.html#_date_format_in_range_queries> in range queries can be specified with the C<format> argument:
1367              
1368             {
1369             range => {
1370             born => {
1371             gte => "01/01/2022",
1372             lte => "2023",
1373             format => "dd/MM/yyyy||yyyy"
1374             # With a time zone
1375             # alternatively: Asia/Tokyo
1376             time_zone => "+09:00",
1377             }
1378             }
1379             }
1380              
1381             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-range-query.html#query-dsl-range-query> for more information.
1382              
1383             =head2 Exists Query
1384              
1385             Search for values that are non-null.
1386              
1387             {
1388             exists => { field => "author" }
1389             }
1390              
1391             You can change the definition of what is C<null> with the L<null_value parameter|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-exists-query.html#null-value-mapping>
1392              
1393             Equivalent to the L<missing query|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-exists-query.html#missing-query>:
1394              
1395             bool => {
1396             must_not => {
1397             exists => {
1398             field => "author"
1399             }
1400             }
1401             }
1402              
1403             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-exists-query.html#query-dsl-exists-query> for more information.
1404              
1405             =head2 Prefix Query
1406              
1407             Search for documents that have fields containing terms with a specified C<prefix>.
1408              
1409             For example, the C<author> field that contains a term starting with C<ta>:
1410              
1411             {
1412             prefix => { author => "ta" }
1413             }
1414              
1415             or, using the C<boost> parameter:
1416              
1417             {
1418             prefix => {
1419             author => {
1420             value => "ta",
1421             boost => 2.0,
1422             }
1423             }
1424             }
1425              
1426             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-prefix-query.html#query-dsl-prefix-query> for more information.
1427              
1428             =head2 Wildcard Query
1429              
1430             {
1431             wildcard => { pauseid => "momo*o" }
1432             }
1433              
1434             or
1435              
1436             {
1437             wildcard => {
1438             pauseid => {
1439             value => "momo*o",
1440             boost => 2.0,
1441             }
1442             }
1443             }
1444              
1445             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-wildcard-query.html#query-dsl-wildcard-query> for more information.
1446              
1447             =head2 Regexp Query
1448              
1449             This enables the use of L<regular expressions syntax|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-regexp-query.html#regexp-syntax>
1450              
1451             {
1452             regexp => {
1453             metadata.author => "Ta.*o"
1454             }
1455             }
1456              
1457             or
1458              
1459             {
1460             regexp => {
1461             metadata.author => {
1462             value => "Ta.*o",
1463             boost => 1.2,
1464             flags => "INTERSECTION|COMPLEMENT|EMPTY",
1465             }
1466             }
1467             }
1468              
1469             Possible flags values are: ALL (default), ANYSTRING, COMPLEMENT, EMPTY, INTERSECTION, INTERVAL, or NONE
1470              
1471             Check the L<regular expression syntax|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-regexp-query.html#regexp-syntax>
1472              
1473             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-regexp-query.html#query-dsl-regexp-query> for more information.
1474              
1475             =head2 Fuzzy Query
1476              
1477             {
1478             fuzzy => { pauseid => "momo" }
1479             }
1480              
1481             With more advanced parameters:
1482              
1483             {
1484             fuzzy => {
1485             user => {
1486             value => "momo",
1487             boost => 1.0,
1488             fuzziness => 2,
1489             prefix_length => 0,
1490             max_expansions => 100
1491             }
1492             }
1493             }
1494              
1495             With number fields:
1496              
1497             {
1498             fuzzy => {
1499             price => {
1500             value => 12,
1501             fuzziness => 2,
1502             }
1503             }
1504             }
1505              
1506             With date fields:
1507              
1508             {
1509             fuzzy => {
1510             created => {
1511             value => "2023-07-29T12:05:07",
1512             fuzziness => "1d"
1513             }
1514             }
1515             }
1516              
1517             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-fuzzy-query.html#query-dsl-fuzzy-query> for more information.
1518              
1519             =head2 Constant Score Query
1520              
1521             As per the Elastic Search documentation, this is a "query that wraps another query and simply returns a constant score equal to the query boost for every document in the filter".
1522              
1523             {
1524             constant_score => {
1525             filter => {
1526             term => { pauseid => "momotaro"}
1527             },
1528             boost => 1.2,
1529             }
1530             }
1531              
1532             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-constant-score-query.html#query-dsl-constant-score-query> for more information.
1533              
1534             =head2 Bool Query
1535              
1536             As per the Elastic Search documentation, this is a "query that matches documents matching boolean combinations of other queries."
1537              
1538             The occurrence types are:
1539              
1540             =over 4
1541              
1542             =item * C<must>
1543              
1544             The clause (query) must appear in matching documents and will contribute to the score.
1545              
1546             =item * C<filter>
1547              
1548             The clause (query) must appear in matching documents. However unlike must the score of the query will be ignored.
1549              
1550             =item * C<should>
1551              
1552             The clause (query) should appear in the matching document. In a boolean query with no must or filter clauses, one or more should clauses must match a document. The minimum number of should clauses to match can be set using the L<minimum_should_match|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-minimum-should-match.html> parameter.
1553              
1554             =item * C<must_not>
1555              
1556             The clause (query) must not appear in the matching documents.
1557              
1558             =back
1559              
1560             {
1561             bool => {
1562             must => {
1563             term => { author => "momotaro" }
1564             },
1565             filter => {
1566             term => { tag => "tech" }
1567             },
1568             must_not => {
1569             range => {
1570             age => { from => 10, to => 20 }
1571             }
1572             },
1573             should => [
1574             {
1575             term => { tag => "wow" }
1576             },
1577             {
1578             term => { tag => "elasticsearch" }
1579             }
1580             ],
1581             minimum_should_match => 1,
1582             boost => 1.0,
1583             }
1584             }
1585              
1586             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-bool-query.html#query-dsl-bool-query> for more information.
1587              
1588             =head2 Dis Max Query
1589              
1590             As per the Elastic Search documentation, this is a "query that generates the union of documents produced by its subqueries".
1591              
1592             {
1593             dis_max => {
1594             tie_breaker => 0.7,
1595             boost => 1.2,
1596             queries => [
1597             {
1598             term => { "age" : 34 }
1599             },
1600             {
1601             term => { "age" : 35 }
1602             }
1603             ]
1604             }
1605             }
1606              
1607             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-dis-max-query.html#query-dsl-dis-max-query> for more information.
1608              
1609             =head2 Function Score Query
1610              
1611             As per the Elastic Search documentation, the "C<function_score> allows you to modify the score of documents that are retrieved by a query. This can be useful if, for example, a score function is computationally expensive and it is sufficient to compute the score on a filtered set of documents.
1612              
1613             To use C<function_score>, the user has to define a query and one or more functions, that compute a new score for each document returned by the query."
1614              
1615             function_score => {
1616             query => {},
1617             boost => "boost for the whole query",
1618             FUNCTION => {},
1619             boost_mode => "(multiply|replace|...)"
1620             }
1621              
1622             Multiple functions can also be provided:
1623              
1624             function_score => {
1625             query => {},
1626             boost => "boost for the whole query",
1627             functions => [
1628             {
1629             filter => {},
1630             FUNCTION => {},
1631             weight => $number,
1632             },
1633             {
1634             FUNCTION => {},
1635             },
1636             {
1637             filter => {},
1638             weight => $number,
1639             }
1640             ],
1641             max_boost => $number,
1642             score_mode => "(multiply|max|...)",
1643             boost_mode => "(multiply|replace|...)",
1644             min_score => $number
1645             }
1646              
1647             C<score_mode> can have the following values:
1648              
1649             =over 4
1650              
1651             =item * C<multiply>
1652              
1653             Scores are multiplied (default)
1654              
1655             =item * C<sum>
1656              
1657             Scores are summed
1658              
1659             =item * C<avg>
1660              
1661             Scores are averaged
1662              
1663             =item * C<first>
1664              
1665             The first function that has a matching filter is applied
1666              
1667             =item * C<max>
1668              
1669             Maximum score is used
1670              
1671             =item * C<min>
1672              
1673             Minimum score is used
1674              
1675             =back
1676              
1677             C<boost_mode> can have the following values:
1678              
1679             =over 4
1680              
1681             =item * C<multiply>
1682              
1683             Query score and function score is multiplied (default)
1684              
1685             =item * C<replace>
1686              
1687             Only function score is used, the query score is ignored
1688              
1689             =item * C<sum>
1690              
1691             Query score and function score are added
1692              
1693             =item * C<avg>
1694              
1695             Average
1696              
1697             =item * C<max>
1698              
1699             Max of query score and function score
1700              
1701             =item * C<min>
1702              
1703             Min of query score and function score
1704              
1705             =back
1706              
1707             To exclude documents that do not meet a certain score threshold the C<min_score> parameter can be set to the desired score threshold.
1708              
1709             See the L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-function-score-query.html> for the list of functions that can be used.
1710              
1711             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-function-score-query.html#query-dsl-function-score-query> for more information.
1712              
1713             =head2 Boosting Query
1714              
1715             As per the Elastic Search documentation, the "C<boosting> query can be used to effectively demote results that match a given query. Unlike the "NOT" clause in C<bool> query, this still selects documents that contain undesirable terms, but reduces their overall score".
1716              
1717             {
1718             boosting => {
1719             positive => {
1720             term => {
1721             field1 => "value1",
1722             },
1723             },
1724             negative => {
1725             term => {
1726             field2 => "value2",
1727             },
1728             },
1729             negative_boost => 0.2,
1730             }
1731             }
1732              
1733             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-boosting-query.html#query-dsl-boosting-query> for more information.
1734              
1735             =head2 Indices Query
1736              
1737             {
1738             indices => {
1739             indices => [qw( index1 index2 )],
1740             query => {
1741             term => { tag => "wow" }
1742             },
1743             no_match_query => {
1744             term => { tag => "kow" }
1745             }
1746             }
1747             }
1748              
1749             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-indices-query.html#query-dsl-indices-query> for more information.
1750              
1751             =head2 Joining Queries
1752              
1753             Elastic Search provides 2 types of joins that are "designed to scale horizontally": C<nested> and L<has_child|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-has-child-query.html#query-dsl-has-child-query> / L<has_parent|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-has-parent-query.html#query-dsl-has-parent-query>
1754              
1755             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/joining-queries.html#joining-queries> for more information.
1756              
1757             =head2 Nested Query
1758              
1759             As per the Elastic Search documentation, the "C<nested> query allows to query nested objects / docs".
1760              
1761             {
1762             nested => {
1763             path => "obj1",
1764             score_mode => "avg",
1765             query => {
1766             bool => {
1767             must => [
1768             {
1769             match => { "obj1.name" => "blue" }
1770             },
1771             {
1772             range => { "obj1.count" => { gt => 5 } }
1773             },
1774             ]
1775             }
1776             }
1777             }
1778             }
1779              
1780             The C<score_mode> allows to set how inner children matching affects scoring of parent. It defaults to C<avg>, but can be C<sum>, C<min>, C<max> and C<none>.
1781              
1782             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-nested-query.html#query-dsl-nested-query> for more information.
1783              
1784             =head2 Geo Queries
1785              
1786             Elastic Search supports two types of geo data: C<geo_point> and L<geo_shape|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-shape-query.html>
1787              
1788             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/geo-queries.html#geo-queries> for more information.
1789              
1790             =head2 Geo Bounding Box Query
1791              
1792             A query allowing to filter hits based on a point location using a bounding box.
1793              
1794             {
1795             bool => {
1796             must => {
1797             match_all => {},
1798             },
1799             filter => {
1800             geo_bounding_box => {
1801             "author.location" => {
1802             top_left => {
1803             lat => 40.73,
1804             lon => -74.1,
1805             },
1806             # or, using an array reference [long, lat]
1807             # top_left => [qw( -74.1 40.73 )],
1808             # or, using a string "lat, long"
1809             # top_left => "40.73, -74.1"
1810             # or, using GeoHash:
1811             # top_left => "dr5r9ydj2y73",
1812             bottom_right => {
1813             lat => 40.01,
1814             lon => -71.12,
1815             },
1816             # or, using an array reference [long, lat]
1817             # bottom_right => [qw( -71.12 40.01 )],
1818             # or, using a string "lat, long"
1819             # bottom_right => "40.01, -71.12",
1820             # or, using GeoHash:
1821             # bottom_right => "drj7teegpus6",
1822             },
1823             # Set to true to accept invalid latitude or longitude (default to false)
1824             ignore_malformed => \1,
1825             }
1826             }
1827             }
1828             }
1829              
1830             or, using L<vertices|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-bounding-box-query.html#_vertices>
1831              
1832             {
1833             bool => {
1834             must => {
1835             match_all => {},
1836             },
1837             filter => {
1838             geo_bounding_box => {
1839             "author.location" => {
1840             top => -74.1,
1841             left => 40.73,
1842             bottom => -71.12,
1843             right => 40.01,
1844             },
1845             # Set to true to accept invalid latitude or longitude (default to false)
1846             ignore_malformed => \1,
1847             }
1848             }
1849             }
1850             }
1851              
1852             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-bounding-box-query.html> for more information.
1853              
1854             =head2 Geo Distance Query
1855              
1856             As per the Elastic Search documentation, this "filters documents that include only hits that exists within a specific distance from a geo point."
1857              
1858             {
1859             bool => {
1860             must => {
1861             match_all => {},
1862             },
1863             filter => {
1864             geo_distance => {
1865             distance => "200km",
1866             "author.location" => {
1867             lat => 40,
1868             lon => -70,
1869             }
1870             # or, using an array reference [long, lat]
1871             # "author.location" => [qw( -70 40 )],
1872             # or, using a string "lat, long"
1873             # "author.location" => "40, -70",
1874             # or, using GeoHash
1875             # "author.location" => "drm3btev3e86",
1876             }
1877             }
1878             }
1879             }
1880              
1881             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-distance-query.html#query-dsl-geo-distance-query> for more information.
1882              
1883             =head2 Geo Distance Range Query
1884              
1885             As per the Elastic Search documentation, this "filters documents that exists within a range from a specific point".
1886              
1887             {
1888             bool => {
1889             must => {
1890             match_all => {}
1891             },
1892             filter => {
1893             geo_distance_range => {
1894             from => "200km",
1895             to => "400km",
1896             2pin.location" : {
1897             lat => 40,
1898             lon => -70,
1899             }
1900             }
1901             }
1902             }
1903             }
1904              
1905             This supports the same geo point options as L</"Geo Distance Query">
1906              
1907             It also "support the common parameters for range (C<lt>, C<lte>, C<gt>, C<gte>, C<from>, C<to>, C<include_upper> and C<include_lower>)."
1908              
1909             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-distance-range-query.html#query-dsl-geo-distance-range-query> for more information.
1910              
1911             =head2 Geo Polygon Query
1912              
1913             This allows "to include hits that only fall within a polygon of points".
1914              
1915             {
1916             bool => {
1917             query => {
1918             match_all => {}
1919             },
1920             filter => {
1921             geo_polygon => {
1922             "person.location" => {
1923             points => [
1924             { lat => 40, lon => -70 },
1925             { lat => 30, lon => -80 },
1926             { lat => 20, lon => -90 }
1927             # or, as an array [long, lat]
1928             # [-70, 40],
1929             # [-80, 30],
1930             # [-90, 20],
1931             # or, as a string "lat, long"
1932             # "40, -70",
1933             # "30, -80",
1934             # "20, -90"
1935             # or, as GeoHash
1936             # "drn5x1g8cu2y",
1937             # "30, -80",
1938             # "20, -90"
1939             ]
1940             },
1941             # Set to true to ignore invalid geo points (defaults to false)
1942             ignore_malformed => \1,
1943             }
1944             }
1945             }
1946             }
1947              
1948             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geo-polygon-query.html#query-dsl-geo-polygon-query> for more information.
1949              
1950             =head2 GeoHash Cell Query
1951              
1952             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-geohash-cell-query.html#query-dsl-geohash-cell-query> for more information.
1953              
1954             =head2 More Like This Query
1955              
1956             As per the Elastic Search documentation, the "More Like This Query (MLT Query) finds documents that are "like" a given set of documents".
1957              
1958             "The simplest use case consists of asking for documents that are similar to a provided piece of text".
1959              
1960             For example, querying for all module releases that have some text similar to "Application Programming Interface" in their "abstract" and in their "description" fields, limiting the number of selected terms to 12.
1961              
1962             {
1963             more_like_this => {
1964             fields => [qw( abstract description )],
1965             like => "Application Programming Interface",
1966             min_term_freq => 1,
1967             max_query_terms => 12,
1968             # optional
1969             # unlike => "Python",
1970             # Defaults to 30%
1971             # minimum_should_match => 2,
1972             # boost_terms => 1,
1973             # Defaults to false
1974             # include => \1,
1975             # Defaults to 1.0
1976             # boost => 1.12
1977             }
1978             }
1979              
1980             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-mlt-query.html#query-dsl-mlt-query> for more information.
1981              
1982             =head2 Template Query
1983              
1984             As per the Elastic Search documentation, this "accepts a query template and a map of key/value pairs to fill in template parameters".
1985              
1986             {
1987             query => {
1988             template => {
1989             inline => { match => { text => "{{query_string}}" }},
1990             params => {
1991             query_string => "all about search",
1992             }
1993             }
1994             }
1995             }
1996              
1997             would be translated to:
1998              
1999             {
2000             query => {
2001             match => {
2002             text => "all about search",
2003             }
2004             }
2005             }
2006              
2007             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-template-query.html#query-dsl-template-query> for more information.
2008              
2009             =head2 Script Query
2010              
2011             As per the Elastic Search documentation, this is used "to define scripts as queries. They are typically used in a filter context". for example:
2012              
2013             bool => {
2014             must => {
2015             # query details goes here
2016             # ...
2017             },
2018             filter => {
2019             script => {
2020             script => "doc['num1'].value > 1"
2021             }
2022             }
2023             }
2024              
2025             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-script-query.html#query-dsl-script-query> for more information.
2026              
2027             =head2 Span Term Query
2028              
2029             As per the Elastic Search documentation, this matches "spans containing a term".
2030              
2031             {
2032             span_term => { pauseid => "momotaro" }
2033             }
2034              
2035             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-term-query.html#query-dsl-span-term-query> for more information.
2036              
2037             =head2 Span Multi Terms Query
2038              
2039             The C<span_multi> query allows you to wrap a multi term query (one of C<wildcard>, C<fuzzy>, C<prefix>, C<term>, C<range> or C<regexp> query) as a C<span> query, so it can be nested.
2040              
2041             {
2042             span_multi => {
2043             match => {
2044             prefix => { pauseid => { value => "momo" } }
2045             }
2046             }
2047             }
2048              
2049             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-multi-term-query.html#query-dsl-span-multi-term-query> for more information.
2050              
2051             =head2 Span First Query
2052              
2053             As per the Elastic Search documentation, this matches "spans near the beginning of a field".
2054              
2055             {
2056             span_first => {
2057             match => {
2058             span_term => { pauseid => "momotaro" }
2059             },
2060             end => 3,
2061             }
2062             }
2063              
2064             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-first-query.html#query-dsl-span-first-query> for more information.
2065              
2066             =head2 Span Near Query
2067              
2068             As per the Elastic Search documentation, this matches "spans which are near one another. One can specify slop, the maximum number of intervening unmatched positions, as well as whether matches are required to be in-order".
2069              
2070             {
2071             span_near => {
2072             clauses => [
2073             { span_term => { field => "value1" } },
2074             { span_term => { field => "value2" } },
2075             { span_term => { field => "value3" } },
2076             ],
2077             collect_payloads => \0,
2078             in_order => \0,
2079             slop => 12,
2080             },
2081             }
2082              
2083             The C<clauses> element is a list of one or more other span type queries and the C<slop> controls the maximum number of intervening unmatched positions permitted.
2084              
2085             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-near-query.html#query-dsl-span-near-query> for more information.
2086              
2087             =head2 Span Or Query
2088              
2089             As per the Elastic Search documentation, this matches "the union of its span clauses".
2090              
2091             {
2092             span_or => {
2093             clauses => [
2094             { span_term => { field => "value1" } },
2095             { span_term => { field => "value2" } },
2096             { span_term => { field => "value3" } },
2097             ],
2098             },
2099             }
2100              
2101             The C<clauses> element is a list of one or more other span type queries
2102              
2103             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-or-query.html#query-dsl-span-or-query> for more information.
2104              
2105             =head2 Span Not Query
2106              
2107             As per the Elastic Search documentation, this removes "matches which overlap with another span query".
2108              
2109             {
2110             span_not => {
2111             exclude => {
2112             span_near => {
2113             clauses => [
2114             { span_term => { field1 => "la" } },
2115             { span_term => { field1 => "hoya" } },
2116             ],
2117             in_order => \1,
2118             slop => 0,
2119             },
2120             },
2121             include => { span_term => { field1 => "hoya" } },
2122             },
2123             }
2124              
2125             The C<include> and C<exclude> clauses can be any span type query.
2126              
2127             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-not-query.html#query-dsl-span-not-query> for more information.
2128              
2129             =head2 Span Containing Query
2130              
2131             As per the Elastic Search documentation, this returns "matches which enclose another span query".
2132              
2133             {
2134             span_containing => {
2135             big => {
2136             span_near => {
2137             clauses => [
2138             { span_term => { field1 => "bar" } },
2139             { span_term => { field1 => "baz" } },
2140             ],
2141             in_order => \1,
2142             slop => 5,
2143             },
2144             },
2145             little => { span_term => { field1 => "foo" } },
2146             },
2147             }
2148              
2149             The C<big> and C<little> clauses can be any C<span> type query. Matching spans from C<big> that contain matches from C<little> are returned.
2150              
2151             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-containing-query.html#query-dsl-span-containing-query> for more information.
2152              
2153             =head2 Span Within a Query
2154              
2155             As per the Elastic Search documentation, this returns "matches which are enclosed inside another span query".
2156              
2157             {
2158             span_within => {
2159             big => {
2160             span_near => {
2161             clauses => [
2162             { span_term => { field1 => "bar" } },
2163             { span_term => { field1 => "baz" } },
2164             ],
2165             in_order => \1,
2166             slop => 5,
2167             },
2168             },
2169             little => { span_term => { field1 => "foo" } },
2170             },
2171             }
2172              
2173             The C<big> and C<little> clauses can be any C<span> type query. Matching spans from C<little> that are enclosed within C<big> are returned.
2174              
2175             See L<Elastic Search documentation|https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-span-within-query.html#query-dsl-span-within-query> for more information.
2176              
2177             =head1 AUTHOR
2178              
2179             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
2180              
2181             =head1 SEE ALSO
2182              
2183             L<Net::API::CPAN::Scroll>, L<Net::API::CPAN::List>
2184              
2185             =head1 COPYRIGHT & LICENSE
2186              
2187             Copyright(c) 2023 DEGUEST Pte. Ltd.
2188              
2189             All rights reserved
2190              
2191             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2192              
2193             =cut