File Coverage

blib/lib/ElasticSearch/RequestParser.pm
Criterion Covered Total %
statement 18 473 3.8
branch 0 208 0.0
condition 0 69 0.0
subroutine 6 115 5.2
pod 71 71 100.0
total 95 936 10.1


line stmt bran cond sub pod time code
1             package ElasticSearch;
2             $ElasticSearch::VERSION = '0.68';
3 1     1   5 use strict;
  1         2  
  1         37  
4 1     1   5 use warnings FATAL => 'all';
  1         1  
  1         40  
5 1     1   5594 use Any::URI::Escape qw(uri_escape);
  1         12128  
  1         91  
6 1     1   12 use Carp;
  1         1  
  1         101  
7             use constant {
8 1         537 ONE_REQ => 1,
9             ONE_OPT => 2,
10             ONE_ALL => 3,
11             MULTI_ALL => 4,
12             MULTI_BLANK => 5,
13             MULTI_REQ => 6,
14 1     1   6 };
  1         3  
15              
16             use constant {
17 1         16310 CMD_NONE => [],
18             CMD_INDEX_TYPE_ID => [ index => ONE_REQ, type => ONE_REQ, id => ONE_REQ ],
19             CMD_INDEX_TYPE_id => [ index => ONE_REQ, type => ONE_REQ, id => ONE_OPT ],
20             CMD_INDEX_type_ID => [ index => ONE_REQ, type => ONE_ALL, id => ONE_REQ ],
21             CMD_Index => [ index => ONE_OPT ],
22             CMD_index => [ index => MULTI_BLANK ],
23             CMD_indices => [ index => MULTI_ALL ],
24             CMD_INDICES => [ index => MULTI_REQ ],
25             CMD_INDEX => [ index => ONE_REQ ],
26             CMD_INDEX_TYPE => [ index => ONE_REQ, type => ONE_REQ ],
27             CMD_INDEX_type => [ index => ONE_REQ, type => MULTI_BLANK ],
28             CMD_index_TYPE => [ index => MULTI_ALL, type => ONE_REQ ],
29             CMD_index_types => [ index => MULTI_ALL, type => MULTI_REQ ],
30             CMD_INDICES_TYPE => [ index => MULTI_REQ, type => ONE_REQ ],
31             CMD_index_type => [ index => MULTI_ALL, type => MULTI_BLANK ],
32             CMD_index_then_type => [ index => ONE_OPT, type => ONE_OPT ],
33             CMD_RIVER => [ river => ONE_REQ ],
34             CMD_nodes => [ node => MULTI_BLANK ],
35             CMD_NAME => [ name => ONE_REQ ],
36             CMD_INDEX_PERC => [ index => ONE_REQ, percolator => ONE_REQ ],
37              
38             CONSISTENCY => [ 'enum', [ 'one', 'quorum', 'all' ] ],
39             REPLICATION => [ 'enum', [ 'async', 'sync' ] ],
40             SEARCH_TYPE => [
41             'enum',
42             [ 'dfs_query_then_fetch', 'dfs_query_and_fetch',
43             'query_then_fetch', 'query_and_fetch',
44             'count', 'scan'
45             ]
46             ],
47             IGNORE_INDICES => [ 'enum', [ 'missing', 'none' ] ],
48              
49 1     1   7 };
  1         2  
50              
51             our %QS_Format = (
52             boolean => '1 | 0',
53             duration => "'5m' | '10s'",
54             optional => "'scalar value'",
55             flatten => "'scalar' or ['scalar_1', 'scalar_n']",
56             'int' => "integer",
57             string => sub {
58             my $k = shift;
59             return $k eq 'preference'
60             ? '_local | _primary | _primary_first | $string'
61             : $k eq 'percolate' || $k eq 'q' ? '$query_string'
62             : $k eq 'scroll_id' ? '$scroll_id'
63             : $k eq 'df' ? '$default_field'
64             : '$string';
65             },
66             float => 'float',
67             enum => sub { join " | ", @{ $_[1][1] } },
68             coderef => 'sub {..} | "IGNORE"',
69             );
70              
71             our %QS_Formatter = (
72             boolean => sub {
73             my $key = shift;
74             my $val = $_[0] ? $_[1] : $_[2];
75             return unless defined $val;
76             return ref $val ? $val : [ $key, $val ? 'true' : 'false' ];
77             },
78             duration => sub {
79             my ( $k, $t ) = @_;
80             return unless defined $t;
81             return [ $k, $t ] if $t =~ /^\d+([smh]|ms)$/i;
82             die "$k '$t' is not in the form $QS_Format{duration}\n";
83             },
84             flatten => sub {
85             my $key = shift;
86             my $array = shift or return;
87             return [ $key, ref $array ? join( ',', @$array ) : $array ];
88             },
89             'int' => sub {
90             my $key = shift;
91             my $int = shift;
92             return unless defined $int;
93             eval { $int += 0; 1 } or die "'$key' is not an integer";
94             return [ $key, $int ];
95             },
96             'float' => sub {
97             my $key = shift;
98             my $float = shift;
99             return unless defined $float;
100             $key = shift if @_;
101             eval { $float += 0; 1 } or die "'$key' is not a float";
102             return [ $key, $float ];
103             },
104             'string' => sub {
105             my $key = shift;
106             my $string = shift;
107             return unless defined $string;
108             return [ $key, $string ];
109             },
110             'coderef' => sub {
111             my $key = shift;
112             my $coderef = shift;
113             return unless defined $coderef;
114             unless ( ref $coderef ) {
115             die "'$key' is not a code ref or the string 'IGNORE'"
116             unless $coderef eq 'IGNORE';
117             $coderef = sub { };
118             }
119             return [ $key, $coderef ];
120             },
121             'enum' => sub {
122             my $key = shift;
123             my $val = shift;
124             return unless defined $val;
125             my $vals = $_[0];
126             for (@$vals) {
127             return [ $key, $val ] if $val eq $_;
128             }
129             die "Unrecognised value '$val'. Allowed values: "
130             . join( ', ', @$vals );
131             },
132              
133             );
134              
135             ##################################
136             ## DOCUMENT MANAGEMENT
137             ##################################
138              
139             #===================================
140             sub get {
141             #===================================
142 0     0 1   shift()->_do_action(
143             'get',
144             { cmd => CMD_INDEX_type_ID,
145             qs => {
146             fields => ['flatten'],
147             ignore_missing => [ 'boolean', 1 ],
148             preference => ['string'],
149             refresh => [ 'boolean', 1 ],
150             routing => ['string'],
151             parent => ['string'],
152             },
153             },
154             @_
155             );
156             }
157              
158             #===================================
159             sub exists : method {
160             #===================================
161             shift()->_do_action(
162             'exists',
163             { method => 'HEAD',
164             cmd => CMD_INDEX_TYPE_ID,
165             qs => {
166             preference => ['string'],
167             refresh => [ 'boolean', 1 ],
168             routing => ['string'],
169             parent => ['string'],
170             },
171 0     0     fixup => sub { $_[1]->{qs}{ignore_missing} = 1 }
172             },
173             @_
174 0     0 1   );
175             }
176              
177             #===================================
178             sub mget {
179             #===================================
180 0     0 1   my ( $self, $params ) = parse_params(@_);
181              
182 0   0       $params->{$_} ||= $self->{_default}{$_} for qw(index type);
183              
184 0 0         if ( $params->{index} ) {
185 0 0         if ( my $ids = delete $params->{ids} ) {
186 0 0         $self->throw( 'Param', 'mget',
187             'Cannot specify both ids and docs in mget()' )
188             if $params->{docs};
189 0           $params->{docs} = [ map { +{ _id => $_ } } @$ids ];
  0            
190             }
191             }
192             else {
193 0 0         $self->throw( 'Param',
194             'Cannot specify a type for mget() without specifying index' )
195             if $params->{type};
196 0 0         $self->throw( 'Param',
197             'Use of the ids param with mget() requires an index' )
198             if $params->{ids};
199             }
200              
201 0           my $filter;
202             $self->_do_action(
203             'mget',
204             { cmd => [ index => ONE_OPT, type => ONE_OPT ],
205             postfix => '_mget',
206             data => { docs => 'docs' },
207             qs => {
208             fields => ['flatten'],
209             filter_missing => [ 'boolean', 1 ],
210             },
211             fixup => sub {
212 0 0   0     $_[1]->{skip} = [] unless @{ $_[1]{data}{docs} };
  0            
213 0           $filter = delete $_[1]->{qs}{filter_missing};
214             },
215             post_process => sub {
216 0     0     my $result = shift;
217 0           my $docs = $result->{docs};
218 0 0         return $filter ? [ grep { $_->{exists} } @$docs ] : $docs;
  0            
219             }
220             },
221 0           $params
222             );
223             }
224              
225             my %Index_Defn = (
226             cmd => CMD_INDEX_TYPE_id,
227             qs => {
228             consistency => CONSISTENCY,
229             create => [ 'boolean', [ op_type => 'create' ] ],
230             parent => ['string'],
231             percolate => ['string'],
232             refresh => [ 'boolean', 1 ],
233             replication => REPLICATION,
234             routing => ['string'],
235             timeout => ['duration'],
236             timestamp => ['string'],
237             ttl => ['int'],
238             version => ['int'],
239             version_type => [ 'enum', [ 'internal', 'external' ] ],
240             },
241             data => { data => 'data' },
242             fixup => sub {
243             my $data = $_[1]{data}{data};
244             $_[1]{data} = ref $data eq 'HASH' ? $data : \$data;
245             }
246             );
247              
248             #===================================
249             sub index {
250             #===================================
251 0     0 1   my ( $self, $params ) = parse_params(@_);
252 0           $self->_index( 'index', \%Index_Defn, $params );
253             }
254              
255             #===================================
256             sub set {
257             #===================================
258 0     0 1   my ( $self, $params ) = parse_params(@_);
259 0           $self->_index( 'set', \%Index_Defn, $params );
260             }
261              
262             #===================================
263             sub create {
264             #===================================
265 0     0 1   my ( $self, $params ) = parse_params(@_);
266 0           $self->_index( 'create', \%Index_Defn, { %$params, create => 1 } );
267             }
268              
269             #===================================
270             sub _index {
271             #===================================
272 0     0     my $self = shift;
273 0 0         $_[1]->{method} = $_[2]->{id} ? 'PUT' : 'POST';
274 0           $self->_do_action(@_);
275             }
276              
277             #===================================
278             sub update {
279             #===================================
280 0     0 1   shift()->_do_action(
281             'update',
282             { method => 'POST',
283             cmd => CMD_INDEX_TYPE_ID,
284             postfix => '_update',
285             data => {
286             script => ['script'],
287             params => ['params'],
288             doc => ['doc'],
289             upsert => ['upsert'],
290             },
291             qs => {
292             consistency => CONSISTENCY,
293             fields => ['flatten'],
294             ignore_missing => [ 'boolean', 1 ],
295             parent => ['string'],
296             percolate => ['string'],
297             retry_on_conflict => ['int'],
298             routing => ['string'],
299             timeout => ['duration'],
300             replication => REPLICATION,
301             }
302             },
303             @_
304             );
305             }
306              
307             #===================================
308             sub delete {
309             #===================================
310 0     0 1   shift()->_do_action(
311             'delete',
312             { method => 'DELETE',
313             cmd => CMD_INDEX_TYPE_ID,
314             qs => {
315             consistency => CONSISTENCY,
316             ignore_missing => [ 'boolean', 1 ],
317             refresh => [ 'boolean', 1 ],
318             parent => ['string'],
319             routing => ['string'],
320             version => ['int'],
321             replication => REPLICATION,
322             }
323             },
324             @_
325             );
326             }
327              
328             #===================================
329             sub analyze {
330             #===================================
331 0     0 1   shift()->_do_action(
332             'analyze',
333             { method => 'GET',
334             cmd => CMD_Index,
335             postfix => '_analyze',
336             qs => {
337             text => ['string'],
338             analyzer => ['string'],
339             tokenizer => ['string'],
340             filters => ['flatten'],
341             field => ['string'],
342             format => [ 'enum', [ 'detailed', 'text' ] ],
343             prefer_local => [ 'boolean', undef, 0 ],
344             }
345             },
346             @_
347             );
348             }
349              
350             ##################################
351             ## BULK INTERFACE
352             ##################################
353              
354             #===================================
355             sub bulk {
356             #===================================
357 0     0 1   my $self = shift;
358 0           $self->_bulk( 'bulk', $self->_bulk_params( 'actions', @_ ) );
359             }
360              
361             #===================================
362             sub _bulk {
363             #===================================
364 0     0     my ( $self, $method, $params ) = @_;
365 0           my %callbacks;
366 0   0       my $actions = $params->{actions} || [];
367              
368             $self->_do_action(
369             $method,
370             { cmd => CMD_index_then_type,
371             method => 'POST',
372             postfix => '_bulk',
373             qs => {
374             consistency => CONSISTENCY,
375             replication => REPLICATION,
376             refresh => [ 'boolean', 1 ],
377             on_conflict => ['coderef'],
378             on_error => ['coderef'],
379             },
380             data => { actions => 'actions' },
381             fixup => sub {
382 0 0 0 0     die "Cannot specify type without index"
383             if $params->{type} && !$params->{index};
384 0           $_[1]->{data} = $self->_bulk_request($actions);
385 0           $_[1]->{skip} = { actions => [], results => [] }
386 0 0         unless ${ $_[1]->{data} };
387             $callbacks{$_} = delete $_[1]->{qs}{$_}
388 0           for qw(on_error on_conflict);
389             },
390             post_process => sub {
391 0     0     $self->_bulk_response( \%callbacks, $actions, @_ );
392             },
393             },
394 0           $params
395             );
396             }
397              
398             #===================================
399 0     0 1   sub bulk_index { shift->_bulk_action( 'index', @_ ) }
400 0     0 1   sub bulk_create { shift->_bulk_action( 'create', @_ ) }
401 0     0 1   sub bulk_delete { shift->_bulk_action( 'delete', @_ ) }
402             #===================================
403              
404             #===================================
405             sub _bulk_action {
406             #===================================
407 0     0     my $self = shift;
408 0           my $action = shift;
409 0           my $params = $self->_bulk_params( 'docs', @_ );
410 0           $params->{actions}
411 0           = [ map { +{ $action => $_ } } @{ delete $params->{docs} } ];
  0            
412 0           return $self->_bulk( "bulk_$action", $params );
413             }
414              
415             #===================================
416             sub _bulk_params {
417             #===================================
418 0     0     my $self = shift;
419 0           my $key = shift;
420              
421 0 0         return { $key => [], @_ } unless ref $_[0];
422             return
423 0 0         ref $_[0] eq 'ARRAY' ? { $key => $_[0] } : { $key => [], %{ $_[0] } }
  0 0          
424             unless @_ > 1;
425              
426 0           carp "The method signature for bulk methods has changed. "
427             . "Please check the docs.";
428              
429 0 0         if ( ref $_[0] eq 'ARRAY' ) {
430 0           my $first = shift;
431 0 0         my $params = ref $_[0] ? shift : {@_};
432 0           $params->{$key} = $first;
433 0           return $params;
434             }
435 0           return { $key => \@_ };
436             }
437              
438             my %Bulk_Actions = (
439             'delete' => {
440             index => ONE_OPT,
441             type => ONE_OPT,
442             id => ONE_REQ,
443             parent => ONE_OPT,
444             routing => ONE_OPT,
445             version => ONE_OPT,
446             version_type => ONE_OPT,
447             },
448             'index' => {
449             index => ONE_OPT,
450             type => ONE_OPT,
451             id => ONE_OPT,
452             data => ONE_REQ,
453             routing => ONE_OPT,
454             parent => ONE_OPT,
455             percolate => ONE_OPT,
456             timestamp => ONE_OPT,
457             ttl => ONE_OPT,
458             version => ONE_OPT,
459             version_type => ONE_OPT,
460             },
461             );
462             $Bulk_Actions{create} = $Bulk_Actions{index};
463              
464             #===================================
465             sub _bulk_request {
466             #===================================
467 0     0     my $self = shift;
468 0           my $actions = shift;
469              
470 0           my $json = $self->transport->JSON;
471 0           my $indenting = $json->get_indent;
472 0           $json->indent(0);
473              
474 0           my $json_docs = '';
475 0           my $error;
476 0 0 0       eval {
477 0           for my $data (@$actions)
478             {
479 0 0         die "'actions' must be an ARRAY ref of HASH refs"
480             unless ref $data eq 'HASH';
481              
482 0           my ( $action, $params ) = %$data;
483 0   0       $action ||= '';
484 0   0       my $defn = $Bulk_Actions{$action}
485             || die "Unknown action '$action'";
486              
487 0           my %metadata;
488 0           $params = {%$params};
489 0           delete @{$params}{qw(_score sort)};
  0            
490 0 0 0       $params->{data} ||= delete $params->{_source}
491             if $params->{_source};
492              
493 0           for my $key ( keys %$defn ) {
494 0           my $val = delete $params->{$key};
495 0 0         $val = delete $params->{"_$key"} unless defined $val;
496 0 0         unless ( defined $val ) {
497 0 0         next if $defn->{$key} == ONE_OPT;
498 0           die "Missing required param '$key' for action '$action'";
499             }
500 0           $metadata{"_$key"} = $val;
501             }
502 0 0         die "Unknown params for bulk action '$action': "
503             . join( ', ', keys %$params )
504             if keys %$params;
505              
506 0           my $data = delete $metadata{_data};
507 0           my $request = $json->encode( { $action => \%metadata } ) . "\n";
508 0 0         if ($data) {
509 0 0         $data = $json->encode($data) if ref $data eq 'HASH';
510 0           $request .= $data . "\n";
511             }
512 0           $json_docs .= $request;
513             }
514 0           1;
515             } or $error = $@ || 'Unknown error';
516              
517 0           $json->indent($indenting);
518 0 0         die $error if $error;
519              
520 0           return \$json_docs;
521             }
522              
523             #===================================
524             sub _bulk_response {
525             #===================================
526 0     0     my $self = shift;
527 0           my $callbacks = shift;
528 0           my $actions = shift;
529 0           my $results = shift;
530              
531 0   0       my $items = ref($results) eq 'HASH' && $results->{items}
532             || $self->throw( 'Request', 'Malformed response to bulk query',
533             $results );
534              
535 0           my ( @errors, %matches );
536 0           my ( $on_conflict, $on_error ) = @{$callbacks}{qw(on_conflict on_error)};
  0            
537              
538 0           for ( my $i = 0; $i < @$actions; $i++ ) {
539 0           my ( $action, $item ) = ( %{ $items->[$i] } );
  0            
540 0 0         if ( my $match = $item->{matches} ) {
541 0           push @{ $matches{$_} }, $item for @$match;
  0            
542             }
543              
544 0 0         my $error = $items->[$i]{$action}{error} or next;
545 0 0 0       if ( $on_conflict
    0          
546             and $error =~ /
547             VersionConflictEngineException
548             | DocumentAlreadyExistsException
549             /x
550             )
551             {
552 0           $on_conflict->( $action, $actions->[$i]{$action}, $error, $i );
553             }
554             elsif ($on_error) {
555 0           $on_error->( $action, $actions->[$i]{$action}, $error, $i );
556             }
557             else {
558 0           push @errors, { action => $actions->[$i], error => $error };
559             }
560             }
561              
562             return {
563 0 0         actions => $actions,
564             results => $items,
565             matches => \%matches,
566             took => $results->{took},
567             ( @errors ? ( errors => \@errors ) : () )
568             };
569             }
570              
571             ##################################
572             ## DSL FIXUP
573             ##################################
574              
575             #===================================
576             sub _to_dsl {
577             #===================================
578 0     0     my $self = shift;
579 0           my $ops = shift;
580 0           my $builder;
581 0           foreach my $clause (@_) {
582 0           while ( my ( $old, $new ) = each %$ops ) {
583 0 0         my $src = delete $clause->{$old} or next;
584 0 0         die "Cannot specify $old and $new parameters.\n"
585             if $clause->{$new};
586 0   0       $builder ||= $self->builder;
587 0 0         my $method = $new eq 'query' ? 'query' : 'filter';
588 0 0         my $sub_clause = $builder->$method($src) or next;
589 0           $clause->{$new} = $sub_clause->{$method};
590             }
591             }
592             }
593              
594             #===================================
595             sub _data_fixup {
596             #===================================
597 0     0     my $self = shift;
598 0           my $data = shift;
599 0           $self->_to_dsl( { queryb => 'query', filterb => 'filter' }, $data );
600              
601 0 0         my $facets = $data->{facets} or return;
602 0 0         die "(facets) must be a HASH ref" unless ref $facets eq 'HASH';
603 0           $facets = $data->{facets} = {%$facets};
604 0           for ( values %$facets ) {
605 0 0         die "All (facets) must be HASH refs" unless ref $_ eq 'HASH';
606 0           $_ = my $facet = {%$_};
607 0           $self->_to_dsl( {
608             queryb => 'query',
609             filterb => 'filter',
610             facet_filterb => 'facet_filter'
611             },
612             $facet
613             );
614             }
615             }
616              
617             #===================================
618             sub _query_fixup {
619             #===================================
620 0     0     my $self = shift;
621 0           my $args = shift;
622 0           $self->_to_dsl( { queryb => 'query' }, $args->{data} );
623 0 0         if ( my $query = delete $args->{data}{query} ) {
624 0           my ( $k, $v ) = %$query;
625 0           $args->{data}{$k} = $v;
626             }
627             }
628              
629             #===================================
630             sub _warmer_fixup {
631             #===================================
632 0     0     my ( $self, $args ) = @_;
633 0 0         my $warmers = $args->{data}{warmers} or return;
634 0           $warmers = $args->{data}{warmers} = {%$warmers};
635 0           for ( values %$warmers ) {
636 0           $_ = {%$_};
637 0 0         my $source = $_->{source} or next;
638 0           $_->{source} = $source = {%$source};
639 0           $self->_data_fixup($source);
640             }
641             }
642              
643             ##################################
644             ## QUERIES
645             ##################################
646              
647             my %Search_Data = (
648             explain => ['explain'],
649             facets => ['facets'],
650             fields => ['fields'],
651             filter => ['filter'],
652             filterb => ['filterb'],
653             from => ['from'],
654             highlight => ['highlight'],
655             indices_boost => ['indices_boost'],
656             min_score => ['min_score'],
657             script_fields => ['script_fields'],
658             size => ['size'],
659             'sort' => ['sort'],
660             track_scores => ['track_scores'],
661             );
662              
663             my %Search_Defn = (
664             cmd => CMD_index_type,
665             postfix => '_search',
666             data => {
667             %Search_Data,
668             query => ['query'],
669             queryb => ['queryb'],
670             partial_fields => ['partial_fields']
671             },
672             qs => {
673             search_type => SEARCH_TYPE,
674             ignore_indices => IGNORE_INDICES,
675             preference => ['string'],
676             routing => ['flatten'],
677             timeout => ['duration'],
678             scroll => ['duration'],
679             stats => ['flatten'],
680             version => [ 'boolean', 1 ]
681             },
682             fixup => sub { $_[0]->_data_fixup( $_[1]->{data} ) },
683             );
684              
685             my %SearchQS_Defn = (
686             cmd => CMD_index_type,
687             postfix => '_search',
688             qs => {
689             q => ['string'],
690             df => ['string'],
691             analyze_wildcard => [ 'boolean', 1 ],
692             analyzer => ['string'],
693             default_operator => [ 'enum', [ 'OR', 'AND' ] ],
694             explain => [ 'boolean', 1 ],
695             fields => ['flatten'],
696             from => ['int'],
697             ignore_indices => IGNORE_INDICES,
698             lenient => [ 'boolean', 1 ],
699             lowercase_expanded_terms => [ 'boolean', 1 ],
700             min_score => ['float'],
701             preference => ['string'],
702             quote_analyzer => ['string'],
703             quote_field_suffix => ['string'],
704             routing => ['flatten'],
705             scroll => ['duration'],
706             search_type => SEARCH_TYPE,
707             size => ['int'],
708             'sort' => ['flatten'],
709             stats => ['flatten'],
710             timeout => ['duration'],
711             version => [ 'boolean', 1 ],
712             },
713             );
714              
715             my %Query_Defn = (
716             data => {
717             query => ['query'],
718             queryb => ['queryb'],
719             },
720             deprecated => {
721             bool => ['bool'],
722             boosting => ['boosting'],
723             constant_score => ['constant_score'],
724             custom_score => ['custom_score'],
725             dis_max => ['dis_max'],
726             field => ['field'],
727             field_masking_span => ['field_masking_span'],
728             filtered => ['filtered'],
729             flt => [ 'flt', 'fuzzy_like_this' ],
730             flt_field => [ 'flt_field', 'fuzzy_like_this_field' ],
731             fuzzy => ['fuzzy'],
732             has_child => ['has_child'],
733             ids => ['ids'],
734             match_all => ['match_all'],
735             mlt => [ 'mlt', 'more_like_this' ],
736             mlt_field => [ 'mlt_field', 'more_like_this_field' ],
737             prefix => ['prefix'],
738             query_string => ['query_string'],
739             range => ['range'],
740             span_first => ['span_first'],
741             span_near => ['span_near'],
742             span_not => ['span_not'],
743             span_or => ['span_or'],
744             span_term => ['span_term'],
745             term => ['term'],
746             terms => [ 'terms', 'in' ],
747             text => ['text'],
748             text_phrase => ['text_phrase'],
749             text_phrase_prefix => ['text_phrase_prefix'],
750             top_children => ['top_children'],
751             wildcard => ['wildcard'],
752             }
753             );
754              
755             #===================================
756 0     0 1   sub search { shift()->_do_action( 'search', \%Search_Defn, @_ ) }
757 0     0 1   sub searchqs { shift()->_do_action( 'searchqs', \%SearchQS_Defn, @_ ) }
758             #===================================
759              
760             #===================================
761             sub msearch {
762             #===================================
763 0     0 1   my $self = shift;
764 0           my $params = $self->parse_params(@_);
765 0   0       my $queries = $params->{queries} || [];
766              
767 0           my $order;
768 0 0         if ( ref $queries eq 'HASH' ) {
769 0           $order = {};
770 0           my $i = 0;
771 0           my @queries;
772 0           for ( sort keys %$queries ) {
773 0           $order->{$_} = $i++;
774 0           push @queries, $queries->{$_};
775             }
776 0           $queries = \@queries;
777             }
778              
779             $self->_do_action(
780             'msearch',
781             { cmd => CMD_index_type,
782             method => 'GET',
783             postfix => '_msearch',
784             qs => { search_type => SEARCH_TYPE },
785             data => { queries => 'queries' },
786             fixup => sub {
787 0     0     my ( $self, $args ) = @_;
788 0           $args->{data} = $self->_msearch_queries($queries);
789 0 0         $args->{skip} = $order ? {} : [] unless ${ $args->{data} };
  0 0          
790             },
791             post_process => sub {
792 0     0     my $responses = shift->{responses};
793 0 0         return $responses unless $order;
794             return {
795 0           map { $_ => $responses->[ $order->{$_} ] }
  0            
796             keys %$order
797             };
798             },
799             },
800 0           $params
801             );
802             }
803              
804             my %MSearch = (
805             ( map { $_ => 'h' } 'index', 'type', keys %{ $Search_Defn{qs} } ),
806             ( map { $_ => 'b' } 'version', keys %{ $Search_Defn{data} } )
807             );
808             delete $MSearch{scroll};
809              
810             #===================================
811             sub _msearch_queries {
812             #===================================
813 0     0     my $self = shift;
814 0           my $queries = shift;
815              
816 0           my $json = $self->transport->JSON;
817 0           my $indenting = $json->get_indent;
818 0           $json->indent(0);
819              
820 0           my $json_docs = '';
821 0           my $error;
822 0 0 0       eval {
823 0           for my $query (@$queries)
824             {
825 0 0         die "'queries' must contain HASH refs\n"
826             unless ref $query eq 'HASH';
827              
828 0           my %request = ( h => {}, b => {} );
829 0           for ( keys %$query ) {
830 0 0         my $dest = $MSearch{$_}
831             or die "Unknown param for msearch: $_\n";
832 0           $request{$dest}{$_} = $query->{$_};
833             }
834              
835             # flatten arrays
836 0           for (qw(index type stats routing)) {
837 0 0         $request{h}{$_} = join ",", @{ $request{h}{$_} }
  0            
838             if ref $request{h}{$_} eq 'ARRAY';
839             }
840 0           $self->_data_fixup( $request{b} );
841 0           $json_docs .= $json->encode( $request{h} ) . "\n"
842             . $json->encode( $request{b} ) . "\n";
843             }
844 0           1;
845             } or $error = $@ || 'Unknown error';
846              
847 0           $json->indent($indenting);
848 0 0         die $error if $error;
849              
850 0           return \$json_docs;
851             }
852              
853             #===================================
854             sub validate_query {
855             #===================================
856             shift->_do_action(
857             'validate_query',
858             { cmd => CMD_index_type,
859             postfix => '_validate/query',
860             data => {
861             query => ['query'],
862             queryb => ['queryb'],
863             },
864             qs => {
865             q => ['string'],
866             explain => [ 'boolean', 1 ],
867             ignore_indices => IGNORE_INDICES,
868             },
869             fixup => sub {
870 0     0     my $args = $_[1];
871 0 0         if ( defined $args->{qs}{q} ) {
872 0           die "Cannot specify q and query/queryb parameters.\n"
873 0 0         if %{ $args->{data} };
874 0           delete $args->{data};
875             }
876             else {
877 0 0         eval { _query_fixup(@_); 1 } or do {
  0            
  0            
878 0 0         die $@ if $@ =~ /Cannot specify queryb and query/;
879             };
880             }
881             },
882             },
883             @_
884 0     0 1   );
885             }
886              
887             #===================================
888             sub explain {
889             #===================================
890             shift->_do_action(
891             'explain',
892             { cmd => CMD_INDEX_TYPE_ID,
893             postfix => '_explain',
894             data => {
895             query => ['query'],
896             queryb => ['queryb'],
897             },
898             qs => {
899             preference => ['string'],
900             routing => ['string'],
901             q => ['string'],
902             df => ['string'],
903             analyzer => ['string'],
904             analyze_wildcard => [ 'boolean', 1 ],
905             default_operator => [ 'enum', [ 'OR', 'AND' ] ],
906             fields => ['flatten'],
907             lowercase_expanded_terms => [ 'boolean', undef, 0 ],
908             lenient => [ 'boolean', 1 ],
909             },
910             fixup => sub {
911 0     0     my $args = $_[1];
912 0 0         if ( defined $args->{qs}{q} ) {
913 0           die "Cannot specify q and query/queryb parameters.\n"
914 0 0         if %{ $args->{data} };
915 0           delete $args->{data};
916             }
917             else {
918 0           $_[0]->_data_fixup( $args->{data} );
919             }
920             },
921             },
922             @_
923 0     0 1   );
924             }
925              
926             #===================================
927             sub scroll {
928             #===================================
929 0     0 1   shift()->_do_action(
930             'scroll',
931             { cmd => [],
932             prefix => '_search/scroll',
933             qs => {
934             scroll_id => ['string'],
935             scroll => ['duration'],
936             }
937             },
938             @_
939             );
940             }
941              
942             #===================================
943             sub scrolled_search {
944             #===================================
945 0     0 1   my $self = shift;
946 0           require ElasticSearch::ScrolledSearch;
947 0           return ElasticSearch::ScrolledSearch->new( $self, @_ );
948             }
949              
950             #===================================
951             sub delete_by_query {
952             #===================================
953             shift()->_do_action(
954             'delete_by_query',
955             { %Search_Defn,
956             method => 'DELETE',
957             postfix => '_query',
958             qs => {
959             consistency => CONSISTENCY,
960             replication => REPLICATION,
961             routing => ['flatten'],
962             },
963             %Query_Defn,
964             fixup => sub {
965 0     0     _query_fixup(@_);
966 0           die "Missing required param 'query' or 'queryb'\n"
967 0 0         unless %{ $_[1]->{data} };
968             },
969             },
970             @_
971 0     0 1   );
972             }
973              
974             #===================================
975             sub count {
976             #===================================
977             shift()->_do_action(
978             'count',
979             { %Search_Defn,
980             postfix => '_count',
981             %Query_Defn,
982             qs => {
983             routing => ['flatten'],
984             ignore_indices => IGNORE_INDICES,
985             },
986             fixup => sub {
987 0     0     _query_fixup(@_);
988 0 0         delete $_[1]{data} unless %{ $_[1]{data} };
  0            
989             },
990             },
991             @_
992 0     0 1   );
993             }
994              
995             #===================================
996             sub mlt {
997             #===================================
998             shift()->_do_action(
999             'mlt',
1000             { cmd => CMD_INDEX_TYPE_ID,
1001             method => 'GET',
1002             qs => {
1003             mlt_fields => ['flatten'],
1004             pct_terms_to_match => [ 'float', 'percent_terms_to_match' ],
1005             min_term_freq => ['int'],
1006             max_query_terms => ['int'],
1007             stop_words => ['flatten'],
1008             min_doc_freq => ['int'],
1009             max_doc_freq => ['int'],
1010             min_word_len => ['int'],
1011             max_word_len => ['int'],
1012             boost_terms => ['float'],
1013             routing => ['flatten'],
1014             search_indices => ['flatten'],
1015             search_from => ['int'],
1016             search_size => ['int'],
1017             search_type => SEARCH_TYPE,
1018             search_types => ['flatten'],
1019             search_scroll => ['string'],
1020             },
1021             postfix => '_mlt',
1022             data => {
1023             explain => ['explain'],
1024             facets => ['facets'],
1025             fields => ['fields'],
1026             filter => ['filter'],
1027             filterb => ['filterb'],
1028             highlight => ['highlight'],
1029             indices_boost => ['indices_boost'],
1030             min_score => ['min_score'],
1031             script_fields => ['script_fields'],
1032             'sort' => ['sort'],
1033             track_scores => ['track_scores'],
1034             },
1035             fixup => sub {
1036 0     0     shift()->_to_dsl( { filterb => 'filter' }, $_[0]->{data} );
1037             },
1038             },
1039             @_
1040 0     0 1   );
1041             }
1042              
1043             ##################################
1044             ## PERCOLATOR
1045             ##################################
1046             #===================================
1047             sub create_percolator {
1048             #===================================
1049             shift()->_do_action(
1050             'create_percolator',
1051             { cmd => CMD_INDEX_PERC,
1052             prefix => '_percolator',
1053             method => 'PUT',
1054             data => {
1055             query => ['query'],
1056             queryb => ['queryb'],
1057             data => ['data']
1058             },
1059             fixup => sub {
1060 0     0     my $self = shift;
1061 0           my $args = shift;
1062 0           $self->_to_dsl( { queryb => 'query' }, $args->{data} );
1063 0 0         die('create_percolator() requires either the query or queryb param'
1064             ) unless $args->{data}{query};
1065 0 0         die 'The "data" param cannot include a "query" key'
1066             if $args->{data}{data}{query};
1067 0           $args->{data} = {
1068             query => $args->{data}{query},
1069 0           %{ $args->{data}{data} }
1070             };
1071             },
1072             },
1073             @_
1074 0     0 1   );
1075             }
1076              
1077             #===================================
1078             sub delete_percolator {
1079             #===================================
1080 0     0 1   shift()->_do_action(
1081             'delete_percolator',
1082             { cmd => CMD_INDEX_PERC,
1083             prefix => '_percolator',
1084             method => 'DELETE',
1085             qs => { ignore_missing => [ 'boolean', 1 ], }
1086             },
1087             @_
1088             );
1089             }
1090              
1091             #===================================
1092             sub get_percolator {
1093             #===================================
1094             shift()->_do_action(
1095             'get_percolator',
1096             { cmd => CMD_INDEX_PERC,
1097             prefix => '_percolator',
1098             method => 'GET',
1099             qs => { ignore_missing => [ 'boolean', 1 ], },
1100             post_process => sub {
1101 0     0     my $result = shift;
1102 0 0         return $result
1103             unless ref $result eq 'HASH';
1104             return {
1105 0           index => $result->{_type},
1106             percolator => $result->{_id},
1107             query => delete $result->{_source}{query},
1108             data => $result->{_source},
1109             };
1110             },
1111             },
1112             @_
1113 0     0 1   );
1114             }
1115              
1116             #===================================
1117             sub percolate {
1118             #===================================
1119 0     0 1   shift()->_do_action(
1120             'percolate',
1121             { cmd => CMD_INDEX_TYPE,
1122             postfix => '_percolate',
1123             method => 'GET',
1124             qs => { prefer_local => [ 'boolean', undef, 0 ] },
1125             data => { doc => 'doc', query => ['query'] },
1126             },
1127             @_
1128             );
1129             }
1130              
1131             ##################################
1132             ## INDEX ADMIN
1133             ##################################
1134              
1135             #===================================
1136             sub index_status {
1137             #===================================
1138 0     0 1   shift()->_do_action(
1139             'index_status',
1140             { cmd => CMD_index,
1141             postfix => '_status',
1142             qs => {
1143             recovery => [ 'boolean', 1 ],
1144             snapshot => [ 'boolean', 1 ],
1145             ignore_indices => IGNORE_INDICES,
1146             },
1147             },
1148             @_
1149             );
1150             }
1151              
1152             #===================================
1153             sub index_stats {
1154             #===================================
1155 0     0 1   shift()->_do_action(
1156             'index_stats',
1157             { cmd => CMD_index,
1158             postfix => '_stats',
1159             qs => {
1160             docs => [ 'boolean', 1, 0 ],
1161             store => [ 'boolean', 1, 0 ],
1162             indexing => [ 'boolean', 1, 0 ],
1163             get => [ 'boolean', 1, 0 ],
1164             search => [ 'boolean', 1, 0 ],
1165             clear => [ 'boolean', 1 ],
1166             all => [ 'boolean', 1 ],
1167             merge => [ 'boolean', 1 ],
1168             flush => [ 'boolean', 1 ],
1169             refresh => [ 'boolean', 1 ],
1170             types => ['flatten'],
1171             groups => ['flatten'],
1172             level => [ 'enum', [qw(shards)] ],
1173             ignore_indices => IGNORE_INDICES,
1174             },
1175             },
1176             @_
1177             );
1178             }
1179              
1180             #===================================
1181             sub index_segments {
1182             #===================================
1183 0     0 1   shift()->_do_action(
1184             'index_segments',
1185             { cmd => CMD_index,
1186             postfix => '_segments',
1187             qs => { ignore_indices => IGNORE_INDICES, }
1188             },
1189             @_
1190             );
1191             }
1192              
1193             #===================================
1194             sub create_index {
1195             #===================================
1196 0     0 1   shift()->_do_action(
1197             'create_index',
1198             { method => 'PUT',
1199             cmd => CMD_INDEX,
1200             postfix => '',
1201             data => {
1202             settings => ['settings'],
1203             mappings => ['mappings'],
1204             warmers => ['warmers'],
1205             },
1206             fixup => \&_warmer_fixup
1207             },
1208             @_
1209             );
1210             }
1211              
1212             #===================================
1213             sub delete_index {
1214             #===================================
1215 0     0 1   shift()->_do_action(
1216             'delete_index',
1217             { method => 'DELETE',
1218             cmd => CMD_INDICES,
1219             qs => { ignore_missing => [ 'boolean', 1 ], },
1220             postfix => ''
1221             },
1222             @_
1223             );
1224             }
1225              
1226             #===================================
1227             sub index_exists {
1228             #===================================
1229             shift()->_do_action(
1230             'index_exists',
1231             { method => 'HEAD',
1232             cmd => CMD_index,
1233 0     0     fixup => sub { $_[1]->{qs}{ignore_missing} = 1 }
1234             },
1235             @_
1236 0     0 1   );
1237             }
1238              
1239             #===================================
1240             sub open_index {
1241             #===================================
1242 0     0 1   shift()->_do_action(
1243             'open_index',
1244             { method => 'POST',
1245             cmd => CMD_INDEX,
1246             postfix => '_open'
1247             },
1248             @_
1249             );
1250             }
1251              
1252             #===================================
1253             sub close_index {
1254             #===================================
1255 0     0 1   shift()->_do_action(
1256             'close_index',
1257             { method => 'POST',
1258             cmd => CMD_INDEX,
1259             postfix => '_close'
1260             },
1261             @_
1262             );
1263             }
1264              
1265             #===================================
1266             sub aliases {
1267             #===================================
1268 0     0 1   my ( $self, $params ) = parse_params(@_);
1269 0           my $actions = $params->{actions};
1270 0 0 0       if ( defined $actions && ref $actions ne 'ARRAY' ) {
1271 0           $params->{actions} = [$actions];
1272             }
1273              
1274             $self->_do_action(
1275             'aliases',
1276             { prefix => '_aliases',
1277             method => 'POST',
1278             cmd => [],
1279             data => { actions => 'actions' },
1280             fixup => sub {
1281 0     0     my $self = shift;
1282 0           my $args = shift;
1283 0           my @actions = @{ $args->{data}{actions} };
  0            
1284 0           for (@actions) {
1285 0           my ( $key, $value ) = %$_;
1286 0           $value = {%$value};
1287 0           $self->_to_dsl( { filterb => 'filter' }, $value );
1288 0           $_ = { $key => $value };
1289             }
1290 0           $args->{data}{actions} = \@actions;
1291             },
1292             },
1293 0           $params
1294             );
1295             }
1296              
1297             #===================================
1298             sub get_aliases {
1299             #===================================
1300             shift->_do_action(
1301 0     0 1   'aliases',
1302             { postfix => '_aliases',
1303             cmd => CMD_index,
1304             qs => { ignore_missing => [ 'boolean', 1 ] },
1305             },
1306             @_
1307             );
1308             }
1309              
1310             #===================================
1311             sub create_warmer {
1312             #===================================
1313             shift()->_do_action(
1314             'create_warmer',
1315             { method => 'PUT',
1316             cmd => CMD_index_type,
1317             postfix => '_warmer/',
1318             data => {
1319             warmer => 'warmer',
1320             facets => ['facets'],
1321             filter => ['filter'],
1322             filterb => ['filterb'],
1323             script_fields => ['script_fields'],
1324             'sort' => ['sort'],
1325             query => ['query'],
1326             queryb => ['queryb'],
1327             },
1328             fixup => sub {
1329 0     0     my ( $self, $args ) = @_;
1330 0           $args->{cmd} .= delete $args->{data}{warmer};
1331 0           $self->_data_fixup( $args->{data} );
1332             },
1333             },
1334             @_
1335 0     0 1   );
1336             }
1337              
1338             #===================================
1339             sub warmer {
1340             #===================================
1341 0     0 1   my ( $self, $params ) = parse_params(@_);
1342 0 0 0       $params->{warmer} = '*'
1343             unless defined $params->{warmer} and length $params->{warmer};
1344              
1345             $self->_do_action(
1346             'warmer',
1347             { method => 'GET',
1348             cmd => CMD_indices,
1349             postfix => '_warmer/',
1350             data => { warmer => ['warmer'] },
1351             qs => { ignore_missing => [ 'boolean', 1 ] },
1352             fixup => sub {
1353 0     0     my ( $self, $args ) = @_;
1354 0           $args->{cmd} .= delete $args->{data}{warmer};
1355             },
1356             },
1357 0           $params
1358             );
1359             }
1360              
1361             #===================================
1362             sub delete_warmer {
1363             #===================================
1364             shift()->_do_action(
1365             'delete_warmer',
1366             { method => 'DELETE',
1367             cmd => CMD_INDICES,
1368             postfix => '_warmer/',
1369             data => { warmer => 'warmer' },
1370             qs => { ignore_missing => [ 'boolean', 1 ] },
1371             fixup => sub {
1372 0     0     my ( $self, $args ) = @_;
1373 0           $args->{cmd} .= delete $args->{data}{warmer};
1374             },
1375             },
1376             @_
1377 0     0 1   );
1378             }
1379              
1380             #===================================
1381             sub create_index_template {
1382             #===================================
1383 0     0 1   shift()->_do_action(
1384             'create_index_template',
1385             { method => 'PUT',
1386             cmd => CMD_NAME,
1387             prefix => '_template',
1388             data => {
1389             template => 'template',
1390             settings => ['settings'],
1391             mappings => ['mappings'],
1392             warmers => ['warmers'],
1393             order => ['order'],
1394             },
1395             fixup => \&_warmer_fixup
1396             },
1397             @_
1398             );
1399             }
1400              
1401             #===================================
1402             sub delete_index_template {
1403             #===================================
1404 0     0 1   shift()->_do_action(
1405             'delete_index_template',
1406             { method => 'DELETE',
1407             cmd => CMD_NAME,
1408             prefix => '_template',
1409             qs => { ignore_missing => [ 'boolean', 1 ] },
1410             },
1411             @_
1412             );
1413             }
1414              
1415             #===================================
1416             sub index_template {
1417             #===================================
1418 0     0 1   shift()->_do_action(
1419             'index_template',
1420             { method => 'GET',
1421             cmd => CMD_NAME,
1422             prefix => '_template',
1423             },
1424             @_
1425             );
1426             }
1427              
1428             #===================================
1429             sub flush_index {
1430             #===================================
1431 0     0 1   shift()->_do_action(
1432             'flush_index',
1433             { method => 'POST',
1434             cmd => CMD_index,
1435             postfix => '_flush',
1436             qs => {
1437             refresh => [ 'boolean', 1 ],
1438             full => [ 'boolean', 1 ],
1439             ignore_indices => IGNORE_INDICES,
1440             },
1441             },
1442             @_
1443             );
1444             }
1445              
1446             #===================================
1447             sub refresh_index {
1448             #===================================
1449 0     0 1   shift()->_do_action(
1450             'refresh_index',
1451             { method => 'POST',
1452             cmd => CMD_index,
1453             postfix => '_refresh',
1454             qs => { ignore_indices => IGNORE_INDICES, }
1455             },
1456             @_
1457             );
1458             }
1459              
1460             #===================================
1461             sub optimize_index {
1462             #===================================
1463 0     0 1   shift()->_do_action(
1464             'optimize_index',
1465             { method => 'POST',
1466             cmd => CMD_index,
1467             postfix => '_optimize',
1468             qs => {
1469             only_deletes =>
1470             [ 'boolean', [ only_expunge_deletes => 'true' ] ],
1471             max_num_segments => ['int'],
1472             refresh => [ 'boolean', undef, 0 ],
1473             flush => [ 'boolean', undef, 0 ],
1474             wait_for_merge => [ 'boolean', undef, 0 ],
1475             ignore_indices => IGNORE_INDICES,
1476             },
1477             },
1478             @_
1479             );
1480             }
1481              
1482             #===================================
1483             sub snapshot_index {
1484             #===================================
1485 0     0 1   shift()->_do_action(
1486             'snapshot_index',
1487             { method => 'POST',
1488             cmd => CMD_index,
1489             postfix => '_gateway/snapshot',
1490             qs => { ignore_indices => IGNORE_INDICES, }
1491             },
1492             @_
1493             );
1494             }
1495              
1496             #===================================
1497             sub gateway_snapshot {
1498             #===================================
1499 0     0 1   shift()->_do_action(
1500             'gateway_snapshot',
1501             { method => 'POST',
1502             cmd => CMD_index,
1503             postfix => '_gateway/snapshot'
1504             },
1505             @_
1506             );
1507             }
1508              
1509             #===================================
1510             sub put_mapping {
1511             #===================================
1512 0     0 1   my ( $self, $params ) = parse_params(@_);
1513 0           my %defn = (
1514             data => { mapping => 'mapping' },
1515             deprecated => {
1516             dynamic => ['dynamic'],
1517             dynamic_templates => ['dynamic_templates'],
1518             properties => ['properties'],
1519             _all => ['_all'],
1520             _analyzer => ['_analyzer'],
1521             _boost => ['_boost'],
1522             _id => ['_id'],
1523             _index => ['_index'],
1524             _meta => ['_meta'],
1525             _parent => ['_parent'],
1526             _routing => ['_routing'],
1527             _source => ['_source'],
1528             },
1529             );
1530              
1531 0           $defn{deprecated}{mapping} = undef
1532 0           if !$params->{mapping} && grep { exists $params->{$_} }
1533 0 0 0       keys %{ $defn{deprecated} };
1534              
1535 0   0       my $type = $params->{type} || $self->{_default}{type};
1536             $self->_do_action(
1537             'put_mapping',
1538             { method => 'PUT',
1539             cmd => CMD_index_TYPE,
1540             postfix => '_mapping',
1541             qs => { ignore_conflicts => [ 'boolean', 1 ] },
1542             %defn,
1543             fixup => sub {
1544 0     0     my $args = $_[1];
1545 0   0       my $mapping = $args->{data}{mapping} || $args->{data};
1546 0           $args->{data} = { $type => $mapping };
1547             },
1548             },
1549 0           $params
1550             );
1551             }
1552              
1553             #===================================
1554             sub delete_mapping {
1555             #===================================
1556 0     0 1   my ( $self, $params ) = parse_params(@_);
1557              
1558 0           $self->_do_action(
1559             'delete_mapping',
1560             { method => 'DELETE',
1561             cmd => CMD_INDICES_TYPE,
1562             qs => { ignore_missing => [ 'boolean', 1 ], }
1563             },
1564             $params
1565             );
1566             }
1567              
1568             #===================================
1569             sub mapping {
1570             #===================================
1571 0     0 1   my ( $self, $params ) = parse_params(@_);
1572              
1573 0           $self->_do_action(
1574             'mapping',
1575             { method => 'GET',
1576             cmd => CMD_index_type,
1577             postfix => '_mapping',
1578             qs => { ignore_missing => [ 'boolean', 1 ], }
1579             },
1580             $params
1581             );
1582             }
1583              
1584             #===================================
1585             sub type_exists {
1586             #===================================
1587             shift()->_do_action(
1588             'type_exists',
1589             { method => 'HEAD',
1590             cmd => CMD_index_types,
1591             qs => { ignore_indices => IGNORE_INDICES, },
1592 0     0     fixup => sub { $_[1]->{qs}{ignore_missing} = 1 }
1593             },
1594             @_
1595 0     0 1   );
1596             }
1597              
1598             #===================================
1599             sub clear_cache {
1600             #===================================
1601 0     0 1   shift()->_do_action(
1602             'clear_cache',
1603             { method => 'POST',
1604             cmd => CMD_index,
1605             postfix => '_cache/clear',
1606             qs => {
1607             id => [ 'boolean', 1 ],
1608             filter => [ 'boolean', 1 ],
1609             field_data => [ 'boolean', 1 ],
1610             bloom => [ 'boolean', 1 ],
1611             fields => ['flatten'],
1612             ignore_indices => IGNORE_INDICES,
1613             }
1614             },
1615             @_
1616             );
1617             }
1618              
1619             #===================================
1620             sub index_settings {
1621             #===================================
1622 0     0 1   my ( $self, $params ) = parse_params(@_);
1623              
1624 0           $self->_do_action(
1625             'index_settings',
1626             { method => 'GET',
1627             cmd => CMD_index,
1628             postfix => '_settings'
1629             },
1630             $params
1631             );
1632             }
1633              
1634             #===================================
1635             sub update_index_settings {
1636             #===================================
1637 0     0 1   my ( $self, $params ) = parse_params(@_);
1638              
1639 0           $self->_do_action(
1640             'update_index_settings',
1641             { method => 'PUT',
1642             cmd => CMD_index,
1643             postfix => '_settings',
1644             data => { index => 'settings' }
1645             },
1646             $params
1647             );
1648             }
1649              
1650             ##################################
1651             ## RIVER MANAGEMENT
1652             ##################################
1653              
1654             #===================================
1655             sub create_river {
1656             #===================================
1657 0     0 1   my ( $self, $params ) = parse_params(@_);
1658 0 0         my $type = $params->{type}
1659             or $self->throw( 'Param', 'No river type specified', $params );
1660 0           my $data = { type => 'type', index => ['index'], $type => [$type] };
1661 0           $self->_do_action(
1662             'create_river',
1663             { method => 'PUT',
1664             prefix => '_river',
1665             cmd => CMD_RIVER,
1666             postfix => '_meta',
1667             data => $data
1668             },
1669             $params
1670             );
1671             }
1672              
1673             #===================================
1674             sub get_river {
1675             #===================================
1676 0     0 1   my ( $self, $params ) = parse_params(@_);
1677 0           $self->_do_action(
1678             'get_river',
1679             { method => 'GET',
1680             prefix => '_river',
1681             cmd => CMD_RIVER,
1682             postfix => '_meta',
1683             qs => { ignore_missing => [ 'boolean', 1 ] }
1684             },
1685             $params
1686             );
1687             }
1688              
1689             #===================================
1690             sub delete_river {
1691             #===================================
1692 0     0 1   my ( $self, $params ) = parse_params(@_);
1693 0           $self->_do_action(
1694             'delete_river',
1695             { method => 'DELETE',
1696             prefix => '_river',
1697             cmd => CMD_RIVER,
1698             },
1699             $params
1700             );
1701             }
1702              
1703             #===================================
1704             sub river_status {
1705             #===================================
1706 0     0 1   my ( $self, $params ) = parse_params(@_);
1707 0           $self->_do_action(
1708             'river_status',
1709             { method => 'GET',
1710             prefix => '_river',
1711             cmd => CMD_RIVER,
1712             postfix => '_status',
1713             qs => { ignore_missing => [ 'boolean', 1 ] }
1714             },
1715             $params
1716             );
1717             }
1718              
1719             ##################################
1720             ## CLUSTER MANAGEMENT
1721             ##################################
1722              
1723             #===================================
1724             sub cluster_state {
1725             #===================================
1726 0     0 1   shift()->_do_action(
1727             'cluster_state',
1728             { prefix => '_cluster/state',
1729             qs => {
1730             filter_blocks => [ 'boolean', 1 ],
1731             filter_nodes => [ 'boolean', 1 ],
1732             filter_metadata => [ 'boolean', 1 ],
1733             filter_routing_table => [ 'boolean', 1 ],
1734             filter_indices => ['flatten'],
1735             }
1736              
1737             },
1738             @_
1739             );
1740             }
1741              
1742             #===================================
1743             sub current_server_version {
1744             #===================================
1745             shift()->_do_action(
1746             'current_server_version',
1747             { cmd => CMD_NONE,
1748             prefix => '',
1749             post_process => sub {
1750 0     0     return shift->{version};
1751             },
1752             }
1753 0     0 1   );
1754             }
1755              
1756             #===================================
1757             sub nodes {
1758             #===================================
1759 0     0 1   shift()->_do_action(
1760             'nodes',
1761             { prefix => '_cluster/nodes',
1762             cmd => CMD_nodes,
1763             qs => {
1764             settings => [ 'boolean', 1 ],
1765             http => [ 'boolean', 1 ],
1766             jvm => [ 'boolean', 1 ],
1767             network => [ 'boolean', 1 ],
1768             os => [ 'boolean', 1 ],
1769             process => [ 'boolean', 1 ],
1770             thread_pool => [ 'boolean', 1 ],
1771             transport => [ 'boolean', 1 ],
1772             },
1773             },
1774             @_
1775             );
1776             }
1777              
1778             #===================================
1779             sub nodes_stats {
1780             #===================================
1781 0     0 1   shift()->_do_action(
1782             'nodes',
1783             { prefix => '_cluster/nodes',
1784             postfix => 'stats',
1785             cmd => CMD_nodes,
1786             qs => {
1787             indices => [ 'boolean', 1, 0 ],
1788             clear => [ 'boolean', 1 ],
1789             all => [ 'boolean', 1 ],
1790             fs => [ 'boolean', 1 ],
1791             http => [ 'boolean', 1 ],
1792             jvm => [ 'boolean', 1 ],
1793             network => [ 'boolean', 1 ],
1794             os => [ 'boolean', 1 ],
1795             process => [ 'boolean', 1 ],
1796             thread_pool => [ 'boolean', 1 ],
1797             transport => [ 'boolean', 1 ],
1798             },
1799             },
1800             @_
1801             );
1802             }
1803              
1804             #===================================
1805             sub shutdown {
1806             #===================================
1807 0     0 1   shift()->_do_action(
1808             'shutdown',
1809             { method => 'POST',
1810             prefix => '_cluster/nodes',
1811             cmd => CMD_nodes,
1812             postfix => '_shutdown',
1813             qs => { delay => ['duration'] }
1814             },
1815             @_
1816             );
1817             }
1818              
1819             #===================================
1820             sub restart {
1821             #===================================
1822 0     0 1   shift()->_do_action(
1823             'shutdown',
1824             { method => 'POST',
1825             prefix => '_cluster/nodes',
1826             cmd => CMD_nodes,
1827             postfix => '_restart',
1828             qs => { delay => ['duration'] }
1829             },
1830             @_
1831             );
1832             }
1833              
1834             #===================================
1835             sub cluster_health {
1836             #===================================
1837 0     0 1   shift()->_do_action(
1838             'cluster_health',
1839             { prefix => '_cluster/health',
1840             cmd => CMD_index,
1841             qs => {
1842             level => [ 'enum', [qw(cluster indices shards)] ],
1843             wait_for_status => [ 'enum', [qw(green yellow red)] ],
1844             wait_for_relocating_shards => ['int'],
1845             wait_for_nodes => ['string'],
1846             timeout => ['duration']
1847             }
1848             },
1849             @_
1850             );
1851             }
1852              
1853             #===================================
1854             sub cluster_settings {
1855             #===================================
1856 0     0 1   my ( $self, $params ) = parse_params(@_);
1857              
1858 0           $self->_do_action(
1859             'cluster_settings',
1860             { method => 'GET',
1861             cmd => CMD_NONE,
1862             postfix => '_cluster/settings'
1863             },
1864             $params
1865             );
1866             }
1867              
1868             #===================================
1869             sub update_cluster_settings {
1870             #===================================
1871 0     0 1   my ( $self, $params ) = parse_params(@_);
1872              
1873 0           $self->_do_action(
1874             'update_cluster_settings',
1875             { method => 'PUT',
1876             cmd => CMD_NONE,
1877             postfix => '_cluster/settings',
1878             data => {
1879             persistent => ['persistent'],
1880             transient => ['transient']
1881             }
1882             },
1883             $params
1884             );
1885             }
1886              
1887             #===================================
1888             sub cluster_reroute {
1889             #===================================
1890 0     0 1   my ( $self, $params ) = parse_params(@_);
1891 0 0 0       $params->{commands} = [ $params->{commands} ]
1892             if $params->{commands} and ref( $params->{commands} ) ne 'ARRAY';
1893              
1894 0           $self->_do_action(
1895             'cluster_reroute',
1896             { prefix => '_cluster/reroute',
1897             cmd => [],
1898             method => 'POST',
1899             data => { commands => ['commands'] },
1900             qs => { dry_run => [ 'boolean', 1 ], },
1901             },
1902             $params
1903             );
1904             }
1905              
1906             ##################################
1907             ## FLAGS
1908             ##################################
1909              
1910             #===================================
1911             sub camel_case {
1912             #===================================
1913 0     0 1   my $self = shift;
1914 0 0         if (@_) {
1915 0 0         if ( shift() ) {
1916 0           $self->{_base_qs}{case} = 'camelCase';
1917             }
1918             else {
1919 0           delete $self->{_base_qs}{case};
1920             }
1921             }
1922 0 0         return $self->{_base_qs}{case} ? 1 : 0;
1923             }
1924              
1925             #===================================
1926             sub error_trace {
1927             #===================================
1928 0     0 1   my $self = shift;
1929 0 0         if (@_) {
1930 0 0         if ( shift() ) {
1931 0           $self->{_base_qs}{error_trace} = 'true';
1932             }
1933             else {
1934 0           delete $self->{_base_qs}{error_trace};
1935             }
1936             }
1937 0 0         return $self->{_base_qs}{error_trace} ? 1 : 0;
1938             }
1939              
1940             ##################################
1941             ## INTERNAL
1942             ##################################
1943              
1944             #===================================
1945             sub _do_action {
1946             #===================================
1947 0     0     my $self = shift;
1948 0   0       my $action = shift || '';
1949 0   0       my $defn = shift || {};
1950 0           my $original_params = $self->parse_params(@_);
1951              
1952 0           my $error;
1953              
1954 0           my $params = {%$original_params};
1955 0   0       my %args = ( method => $defn->{method} || 'GET' );
1956 0           $args{as_json} = delete $params->{as_json};
1957              
1958 0 0 0       eval {
1959 0           $args{cmd}
1960 0           = $self->_build_cmd( $params, @{$defn}{qw(prefix cmd postfix)} );
1961 0           $args{qs} = $self->_build_qs( $params, $defn->{qs} );
1962 0           $args{data}
1963 0           = $self->_build_data( $params, @{$defn}{ 'data', 'deprecated' } );
1964 0 0         if ( my $fixup = $defn->{fixup} ) {
1965 0           $fixup->( $self, \%args );
1966             }
1967 0 0         die "Unknown parameters: " . join( ', ', keys %$params ) . "\n"
1968             if keys %$params;
1969 0           1;
1970             } or $error = $@ || 'Unknown error';
1971              
1972 0           $args{post_process} = $defn->{post_process};
1973 0 0         if ($error) {
1974 0 0         die $error if ref $error;
1975 0           $self->throw(
1976             'Param',
1977             $error . $self->_usage( $action, $defn ),
1978             { params => $original_params }
1979             );
1980             }
1981 0 0         if ( my $skip = $args{skip} ) {
1982 0           return $self->transport->skip_request( $args{as_json}, $skip );
1983             }
1984 0           return $self->request( \%args );
1985             }
1986              
1987             #===================================
1988             sub _usage {
1989             #===================================
1990 0     0     my $self = shift;
1991 0           my $action = shift;
1992 0           my $defn = shift;
1993              
1994 0           my $usage = "Usage for '$action()':\n";
1995 0 0         my @cmd = @{ $defn->{cmd} || [] };
  0            
1996 0           while ( my $key = shift @cmd ) {
1997 0           my $type = shift @cmd;
1998 0 0         my $arg_format
    0          
1999             = $type == ONE_REQ ? "\$$key"
2000             : $type == ONE_OPT ? "\$$key"
2001             : "\$$key | [\$${key}_1,\$${key}_n]";
2002              
2003 0 0 0       my $required
2004             = ( $type == ONE_REQ or $type == MULTI_REQ )
2005             ? 'required'
2006             : 'optional';
2007 0           $usage .= sprintf( " - %-26s => %-45s # %s\n",
2008             $key, $arg_format, $required );
2009             }
2010              
2011 0 0         if ( my $data = $defn->{data} ) {
2012 0 0         my @keys = sort { $a->[0] cmp $b->[0] }
  0            
2013 0           map { ref $_ ? [ $_->[0], 'optional' ] : [ $_, 'required' ] }
2014             values %$data;
2015              
2016 0           for (@keys) {
2017 0           $usage .= sprintf(
2018             " - %-26s => %-45s # %s\n",
2019             $_->[0], '{' . $_->[0] . '}',
2020             $_->[1]
2021             );
2022             }
2023             }
2024              
2025 0 0         if ( my $qs = $defn->{qs} ) {
2026 0           for ( sort keys %$qs ) {
2027 0           my $arg_format = $QS_Format{ $qs->{$_}[0] };
2028 0           my @extra;
2029 0 0         $arg_format = $arg_format->( $_, $qs->{$_} )
2030             if ref $arg_format;
2031 0 0         if ( length($arg_format) > 45 ) {
2032 0           ( $arg_format, @extra ) = split / [|] /, $arg_format;
2033             }
2034 0           $usage .= sprintf( " - %-26s => %-45s # optional\n", $_,
2035             $arg_format );
2036 0           $usage .= ( ' ' x 34 ) . " | $_\n" for @extra;
2037             }
2038             }
2039              
2040 0           return $usage;
2041             }
2042              
2043             #===================================
2044             sub _build_qs {
2045             #===================================
2046 0     0     my $self = shift;
2047 0           my $params = shift;
2048 0   0       my $defn = shift || {};
2049 0           my %qs = %{ $self->{_base_qs} };
  0            
2050 0           foreach my $key ( keys %$defn ) {
2051 0 0         my ( $format_name, @args ) = @{ $defn->{$key} || [] };
  0            
2052 0   0       $format_name ||= '';
2053              
2054 0 0         next unless exists $params->{$key};
2055              
2056 0 0         my $formatter = $QS_Formatter{$format_name}
2057             or die "Unknown QS formatter '$format_name'";
2058              
2059 0 0         my $val = $formatter->( $key, delete $params->{$key}, @args )
2060             or next;
2061 0           $qs{ $val->[0] } = $val->[1];
2062             }
2063 0           return \%qs;
2064             }
2065              
2066             #===================================
2067             sub _build_data {
2068             #===================================
2069 0     0     my $self = shift;
2070 0           my $params = shift;
2071 0 0         my $defn = shift or return;
2072              
2073 0 0         if ( my $deprecated = shift ) {
2074 0           $defn = { %$defn, %$deprecated };
2075             }
2076              
2077 0           my %data;
2078 0           KEY: while ( my ( $key, $source ) = each %$defn ) {
2079 0 0         next unless defined $source;
2080 0 0         if ( ref $source eq 'ARRAY' ) {
2081 0           foreach (@$source) {
2082 0           my $val = delete $params->{$_};
2083 0 0         next unless defined $val;
2084 0           $data{$key} = $val;
2085 0           next KEY;
2086             }
2087             }
2088             else {
2089 0 0         $data{$key} = delete $params->{$source}
2090             or die "Missing required param '$source'\n";
2091             }
2092             }
2093 0           return \%data;
2094             }
2095              
2096             #===================================
2097             sub _build_cmd {
2098             #===================================
2099 0     0     my $self = shift;
2100 0           my $params = shift;
2101 0           my ( $prefix, $defn, $postfix ) = @_;
2102              
2103 0 0         my @defn = ( @{ $defn || [] } );
  0            
2104 0           my @cmd;
2105 0           while (@defn) {
2106 0           my $key = shift @defn;
2107 0           my $type = shift @defn;
2108              
2109 0 0         my $val
2110             = exists $params->{$key}
2111             ? delete $params->{$key}
2112             : $self->{_default}{$key};
2113              
2114 0 0         $val = '' unless defined $val;
2115              
2116 0 0         if ( ref $val eq 'ARRAY' ) {
2117 0 0         die "'$key' must be a single value\n"
2118             if $type <= ONE_ALL;
2119 0           $val = join ',', @$val;
2120             }
2121 0 0         unless ( length $val ) {
2122 0 0 0       next if $type == ONE_OPT || $type == MULTI_BLANK;
2123 0 0 0       die "Param '$key' is required\n"
2124             if $type == ONE_REQ || $type == MULTI_REQ;
2125 0           $val = '_all';
2126             }
2127 0           push @cmd, uri_escape($val);
2128             }
2129              
2130 0           return join '/', '', grep {defined} ( $prefix, @cmd, $postfix );
  0            
2131             }
2132             1;