File Coverage

blib/lib/Elastic/Model/View.pm
Criterion Covered Total %
statement 28 128 21.8
branch 1 64 1.5
condition 0 14 0.0
subroutine 8 34 23.5
pod 10 11 90.9
total 47 251 18.7


line stmt bran cond sub pod time code
1             package Elastic::Model::View;
2             $Elastic::Model::View::VERSION = '0.51';
3 1     1   822 use Moose;
  1         2  
  1         8  
4              
5 1     1   6427 use Carp;
  1         2  
  1         69  
6 1         11 use Elastic::Model::Types qw(
7             IndexNames ArrayRefOfStr SortArgs
8 1     1   7 HighlightArgs Consistency Replication);
  1         2  
9 1     1   7869 use MooseX::Types::Moose qw(Str Int HashRef ArrayRef Bool Num Object);
  1         4  
  1         8  
10 1     1   6366 use Elastic::Model::SearchBuilder();
  1         3  
  1         19  
11 1     1   6 use namespace::autoclean;
  1         2  
  1         9  
12              
13             #===================================
14             has 'domain' => (
15             #===================================
16                 isa => IndexNames,
17                 is => 'rw',
18                 lazy => 1,
19                 builder => '_build_domains',
20                 coerce => 1,
21             );
22              
23             #===================================
24             has 'type' => (
25             #===================================
26                 is => 'rw',
27                 isa => ArrayRefOfStr,
28                 default => sub { [] },
29                 coerce => 1,
30             );
31              
32             #===================================
33             has 'query' => (
34             #===================================
35                 isa => HashRef,
36                 is => 'rw',
37             );
38              
39             #===================================
40             has 'filter' => (
41             #===================================
42                 isa => HashRef,
43                 is => 'rw',
44             );
45              
46             #===================================
47             has 'post_filter' => (
48             #===================================
49                 isa => HashRef,
50                 is => 'rw',
51             );
52              
53             #===================================
54             has 'aggs' => (
55             #===================================
56                 traits => ['Hash'],
57                 isa => HashRef [HashRef],
58                 is => 'rw',
59                 handles => {
60                     add_agg => 'set',
61                     remove_agg => 'delete',
62                     get_agg => 'get'
63                 }
64             );
65              
66             #===================================
67             has 'facets' => (
68             #===================================
69                 traits => ['Hash'],
70                 isa => HashRef [HashRef],
71                 is => 'rw',
72                 handles => {
73                     add_facet => 'set',
74                     remove_facet => 'delete',
75                     get_facet => 'get'
76                 }
77             );
78              
79             #===================================
80             has 'fields' => (
81             #===================================
82                 isa => ArrayRefOfStr,
83                 coerce => 1,
84                 is => 'rw',
85                 default => sub { [] },
86             );
87              
88             #===================================
89             has 'from' => (
90             #===================================
91                 isa => Int,
92                 is => 'rw',
93                 default => 0,
94             );
95              
96             #===================================
97             has 'size' => (
98             #===================================
99                 isa => Int,
100                 is => 'rw',
101                 lazy => 1,
102                 default => 10,
103                 predicate => '_has_size',
104             );
105              
106             #===================================
107             has 'sort' => (
108             #===================================
109                 isa => SortArgs,
110                 is => 'rw',
111                 coerce => 1,
112             );
113              
114             #===================================
115             has 'highlighting' => (
116             #===================================
117                 isa => HashRef,
118                 is => 'rw',
119                 trigger => \&_check_no_fields,
120             );
121              
122             #===================================
123             has 'highlight' => (
124             #===================================
125                 is => 'rw',
126                 isa => HighlightArgs,
127                 coerce => 1,
128             );
129              
130             #===================================
131             has 'index_boosts' => (
132             #===================================
133                 isa => HashRef [Num],
134                 is => 'rw',
135                 traits => ['Hash'],
136                 handles => {
137                     add_index_boost => 'set',
138                     remove_index_boost => 'delete',
139                     get_index_boost => 'get'
140                 }
141             );
142              
143             #===================================
144             has 'min_score' => (
145             #===================================
146                 isa => Num,
147                 is => 'rw',
148             );
149              
150             #===================================
151             has 'preference' => (
152             #===================================
153                 isa => Str,
154                 is => 'rw',
155             );
156              
157             #===================================
158             has 'routing' => (
159             #===================================
160                 isa => ArrayRefOfStr,
161                 coerce => 1,
162                 is => 'rw',
163             );
164              
165             #===================================
166             has 'include_paths' => (
167             #===================================
168                 is => 'rw',
169                 isa => ArrayRef [Str],
170                 predicate => '_has_include_paths'
171             );
172              
173             #===================================
174             has 'exclude_paths' => (
175             #===================================
176                 is => 'rw',
177                 isa => ArrayRef [Str],
178                 predicate => '_has_exclude_paths'
179             );
180              
181             #===================================
182             has 'script_fields' => (
183             #===================================
184                 isa => HashRef,
185                 is => 'rw',
186                 traits => ['Hash'],
187                 handles => {
188                     add_script_field => 'set',
189                     remove_script_field => 'delete',
190                     get_script_field => 'get'
191                 }
192             );
193              
194             #===================================
195             has 'timeout' => (
196             #===================================
197                 isa => Str,
198                 is => 'rw',
199             );
200              
201             #===================================
202             has 'explain' => (
203             #===================================
204                 is => 'rw',
205                 isa => Bool,
206             );
207              
208             #===================================
209             has 'stats' => (
210             #===================================
211                 is => 'rw',
212                 isa => ArrayRefOfStr,
213                 coerce => 1,
214             );
215              
216             #===================================
217             has 'track_scores' => (
218             #===================================
219                 isa => Bool,
220                 is => 'rw',
221             );
222              
223             #===================================
224             has 'consistency' => (
225             #===================================
226                 is => 'rw',
227                 isa => Consistency,
228             );
229              
230             #===================================
231             has 'replication' => (
232             #===================================
233                 is => 'rw',
234                 isa => Replication
235             );
236              
237             #===================================
238             has 'search_builder' => (
239             #===================================
240                 isa => Object,
241                 is => 'rw',
242                 lazy => 1,
243                 builder => '_build_search_builder',
244             );
245              
246             #===================================
247             has 'cache' => (
248             #===================================
249                 is => 'rw',
250                 isa => Object,
251             );
252              
253             #===================================
254             has 'cache_opts' => (
255             #===================================
256                 is => 'rw',
257                 isa => HashRef,
258             );
259              
260             #===================================
261 0     0   0 sub _build_search_builder { Elastic::Model::SearchBuilder->new }
262             #===================================
263              
264             #===================================
265             sub queryb {
266             #===================================
267 0     0 1 0     my $self = shift;
268 0 0       0     my @args = @_ > 1 ? {@_} : shift();
269 0 0       0     my $query = $self->search_builder->query(@args)
270                     or return $self->_clone_self;
271 0         0     $self->query( $query->{query} );
272             }
273              
274             #===================================
275             sub filterb {
276             #===================================
277 0     0 1 0     my $self = shift;
278 0 0       0     my @args = @_ > 1 ? {@_} : shift();
279 0 0       0     my $filter = $self->search_builder->filter(@args)
280                     or return $self->_clone_self;
281 0         0     $self->filter( $filter->{filter} );
282             }
283              
284             #===================================
285             sub post_filterb {
286             #===================================
287 0     0 1 0     my $self = shift;
288 0 0       0     my @args = @_ > 1 ? {@_} : shift();
289 0 0       0     my $filter = $self->search_builder->filter(@args)
290                     or return $self->_clone_self;
291 0         0     $self->post_filter( $filter->{filter} );
292             }
293              
294             #===================================
295             # clone views when setting attributes
296             #===================================
297              
298             around [
299             #===================================
300                 'from', 'size', 'timeout', 'track_scores',
301                 'search_builder', 'preference', 'min_score', 'explain',
302                 'consistency', 'replication', 'cache'
303             #===================================
304             ] => sub { _clone_args( \&_scalar_args, @_ ) };
305              
306             around [
307             #===================================
308                 'domain', 'type', 'fields', 'sort', 'routing', 'stats',
309                 'include_paths', 'exclude_paths'
310             #===================================
311             ] => sub { _clone_args( \&_array_args, @_ ) };
312              
313             around [
314             #===================================
315                 'aggs', 'facets', 'index_boosts', 'script_fields', 'highlighting',
316                 'query', 'filter', 'post_filter', 'cache_opts'
317             #===================================
318             ] => sub { _clone_args( \&_hash_args, @_ ) };
319              
320             #===================================
321             around 'highlight'
322             #===================================
323                 => sub { _clone_args( \&_highlight_args, @_ ) };
324              
325             for my $name ( 'agg', 'facet', 'index_boost', 'script_field' ) {
326                 my $attr = $name . 's';
327                 for my $method ( "add_$name", "remove_$name" ) {
328                     around $method => sub {
329                         my $orig = shift;
330                         my $self = shift;
331                         my %hash = %{ $self->$attr || {} };
332                         $self = $self->$attr( \%hash );
333                         $self->$orig(@_);
334                         return $self;
335                     };
336                 }
337             }
338              
339             #===================================
340 0     0   0 sub _scalar_args {@_}
341 0 0   0   0 sub _hash_args { @_ > 1 ? {@_} : @_ }
342 0 0   0   0 sub _highlight_args { ref $_[0] ? shift : \@_ }
343 0 0   0   0 sub _array_args { ref $_[0] eq 'ARRAY' ? shift() : \@_ }
344             #===================================
345              
346             #===================================
347             sub _clone_args {
348             #===================================
349 0     0   0     my $args = shift;
350 0         0     my $orig = shift;
351 0         0     my $self = shift;
352 0 0       0     if (@_) {
353 0         0         $self = bless {%$self}, ref $self;
354 0         0         $self->$orig( $args->(@_) );
355 0         0         return $self;
356                 }
357 0         0     $self->$orig();
358             }
359              
360             #===================================
361             sub _clone_self {
362             #===================================
363 0     0   0     my $self = shift;
364 0         0     return bless {%$self}, ref $self;
365             }
366              
367             #===================================
368             sub _check_no_fields {
369             #===================================
370 0     0   0     my ( $self, $val ) = @_;
371 0 0       0     croak "Use the (highlight) attribute to set the fields to highlight"
372                     if $val->{fields};
373             }
374              
375 1     1   1305 no Moose;
  1         2  
  1         8  
376              
377             #===================================
378             sub BUILD {
379             #===================================
380 1     1 0 1     my ( $orig_self, $args ) = @_;
381 1         2     my $self = $orig_self;
382 1         2     for (qw(queryb filterb post_filterb)) {
383 3 50       7         $self = $self->$_( $args->{$_} )
384                         if defined $args->{$_};
385                 }
386              
387 1         1     %{$orig_self} = %{$self};
  1         51  
  1         4  
388             }
389              
390             #===================================
391             sub _build_domains {
392             #===================================
393 0     0         my $self = shift;
394 0               my $namespaces = $self->model->namespaces;
395 0               [ map { $_, @{ $namespaces->{$_}->fixed_domains } }
  0            
  0            
396                     sort keys %$namespaces
397                 ];
398             }
399              
400             #===================================
401             sub search {
402             #===================================
403 0     0 1       my $self = shift;
404 0               $self->model->results_class->new( search => $self->_build_search )
405                     ->as_results;
406             }
407              
408             #===================================
409             sub cached_search {
410             #===================================
411 0     0 1       my $self = shift;
412 0 0             my $cache = $self->cache
413                     or return $self->search;
414              
415 0 0             my %cache_opts
416 0 0                 = ( %{ $self->cache_opts || {} }, @_ == 1 ? %{ $_[0] } : @_ );
  0            
417              
418 0               $self->model->cached_results_class->new(
419                     search => $self->_build_search,
420                     cache => $cache,
421                     cache_opts => \%cache_opts
422                 )->as_results;
423             }
424              
425             #===================================
426 0     0 1   sub scroll { shift->_scroll(@_)->as_results }
427             #===================================
428              
429             #===================================
430             sub scan {
431             #===================================
432 0     0 1       my $self = shift;
433 0 0             croak "A scan cannot be combined with sorting"
434 0 0                 if @{ $self->sort || [] };
435 0               return $self->_scroll( shift, search_type => 'scan', @_ )->as_objects;
436             }
437              
438             #===================================
439             sub _scroll {
440             #===================================
441 0     0         my $self = shift;
442 0   0           my $search = $self->_build_search( scroll => shift() || '1m', @_ );
443 0               return $self->model->scrolled_results_class->new( search => $search );
444             }
445              
446             #===================================
447             sub delete {
448             #===================================
449 0     0 1       my $self = shift;
450 0               $self->model->store->delete_by_query( $self->_build_delete(@_) );
451             }
452              
453             #===================================
454 0     0 1   sub first { shift->size(1)->search(@_)->first }
455 0     0 1   sub total { shift->size(0)->search(@_)->total }
456             #===================================
457              
458             #===================================
459             sub _build_search {
460             #===================================
461 0     0         my $self = shift;
462              
463 0               my ( $highlight, $hfields );
464 0 0 0           if ( $hfields = $self->highlight and keys %$hfields ) {
465 0 0                 $highlight = { %{ $self->highlighting || {} }, fields => $hfields };
  0            
466                 }
467              
468 0               my $fields = $self->fields;
469              
470 0               my $source;
471 0 0             $source->{include} = $self->include_paths
472                     if $self->_has_include_paths;
473 0 0             $source->{exclude} = $self->exclude_paths
474                     if $self->_has_exclude_paths;
475              
476 0 0 0           $fields = ['_source'] unless $source || @$fields;
477              
478 0               my %args = _strip_undef(
479 0                   ( map { $_ => $self->$_ }
480                             qw(
481             type sort from size aggs
482             min_score post_filter preference routing stats
483             script_fields timeout track_scores explain
484             )
485                     ),
486                     facets => $self->_build_facets,
487                     index => $self->domain,
488                     query => $self->_build_query,
489                     highlight => $highlight,
490                     indices_boost => $self->index_boosts,
491                     @_,
492                     version => 1,
493                     fields => [ '_parent', '_routing', @$fields ]
494                 );
495 0 0             $args{_source} = $source
496                     if defined $source;
497 0               return \%args;
498             }
499              
500             #===================================
501             sub _build_facets {
502             #===================================
503 0     0         my $self = shift;
504 0 0             return undef unless $self->facets;
505              
506 0               my $facets = { %{ $self->facets } };
  0            
507              
508 0               for ( values %$facets ) {
509 0 0                 die "All (facets) must be HASH refs" unless ref $_ eq 'HASH';
510 0                   $_ = my $facet = {%$_};
511 0                   $self->_to_dsl(
512                         { queryb => 'query',
513                             filterb => 'filter',
514                             facet_filterb => 'facet_filter'
515                         },
516                         $facet
517                     );
518                 }
519              
520 0               $facets;
521             }
522              
523             #===================================
524             sub _to_dsl {
525             #===================================
526 0     0         my ( $self, $ops ) = ( shift, shift );
527              
528 0               my $builder;
529 0               for my $clause (@_) {
530 0                   while ( my ( $old, $new ) = each %$ops ) {
531 0 0                     my $src = delete $clause->{$old} or next;
532 0 0                     die "Cannot specify $old and $new parameters.\n" if $clause->{$new};
533              
534 0   0                   $builder ||= $self->search_builder;
535 0 0                     my $method = $new eq 'query' ? 'query' : 'filter';
536 0 0                     my $sub_clause = $builder->$method($src) or next;
537 0                       $clause->{$new} = $sub_clause->{$method};
538                     }
539                 }
540             }
541              
542             #===================================
543             sub _build_query {
544             #===================================
545 0     0         my $self = shift;
546 0               my $q = $self->query;
547 0               my $f = $self->filter;
548 0 0 0           return { match_all => {} } unless $q || $f;
549              
550                 return
551 0 0                  !$q ? { constant_score => { filter => $f } }
    0          
552                     : $f ? { filtered => { query => $q, filter => $f } }
553                     : $q;
554             }
555              
556             #===================================
557             sub _build_delete {
558             #===================================
559 0     0         my $self = shift;
560 0               my %args = _strip_undef(
561                     index => $self->domain,
562 0                   ( map { $_ => $self->$_ } qw(type routing consistency replication) ),
563                     @_,
564                     query => $self->_build_query,
565                 );
566 0               return \%args;
567             }
568              
569             #===================================
570             sub _strip_undef {
571             #===================================
572 0     0         my %args = @_;
573 0               return map { $_ => $args{$_} } grep { defined $args{$_} } keys %args;
  0            
  0            
574             }
575             1;
576              
577             =pod
578            
579             =encoding UTF-8
580            
581             =head1 NAME
582            
583             Elastic::Model::View - Views to query your docs in Elasticsearch
584            
585             =head1 VERSION
586            
587             version 0.51
588            
589             =head1 SYNOPSIS
590            
591             $view = $model->view(); # all domains and types known to the model
592             $view = $domain->view(); # just $domain->name, and its types
593             $posts = $view->type( 'post' ); # just type post
594            
595             10 most relevant posts containing C<'perl'> or C<'moose'>
596            
597             $results = $posts->queryb( content => 'perl moose' )->search;
598            
599             10 most relevant posts containing C<'perl'> or C<'moose'> published since
600             1 Jan 2012, sorted by C<timestamp>, with highlighted snippets from the
601             C<content> field:
602            
603             $results = $posts
604             ->queryb ( 'content' => 'perl moose' )
605             ->filterb ( 'created' => { gte => '2012-01-01' } )
606             ->sort ( 'timestamp' )
607             ->highlight ( 'content' )
608             ->search;
609            
610             The same as the above, but in one step:
611            
612             $results = $domain->view(
613             type => 'post',
614             sort => 'timestamp',
615             queryb => { content => 'perl moose' },
616             filterb => { created => { gte => '2012-01-01' } },
617             highlight => 'content',
618             )->search;
619            
620             Efficiently retrieve all posts, unsorted:
621            
622             $results = $posts->size(100)->scan;
623            
624             while (my $result = $results->shift_result) {
625             do_something_with($result);
626             );
627            
628             Cached results:
629            
630             $cache = CHI->new(....);
631             $view = $view->cache( $cache )->cache_opts( expires_in => '2 min');
632            
633             $results = $view->queryb( 'perl' )->cached_search();
634             $results = $view->queryb( 'perl' )->cached_search( expires => '30 sec');
635            
636             =head1 DESCRIPTION
637            
638             L<Elastic::Model::View> is used to query your docs in Elasticsearch.
639            
640             Views are "chainable". In other words, you get a clone of the
641             current view every time you set an attribute. For instance, you could do:
642            
643             $all_types = $domain->view;
644             $users = $all_types->type('user');
645             $posts = $all_types->('post');
646             $recent_posts = $posts->filterb({ published => { gt => '2012-05-01' }});
647            
648             Alternatively, you can set all or some of the attributes when you create
649             a view:
650            
651             $recent_posts = $domain->view(
652             type => 'post',
653             filterb => { published => { gt => '2012-05-01 '}}
654             );
655            
656             Views are also reusable. They only hit the database when you call one
657             of the L<methods|/METHODS>, eg:
658            
659             $results = $recent_posts->search; # retrieve $size results
660             $scroll = $recent_posts->scroll; # keep pulling results
661            
662             =head1 METHODS
663            
664             Calling one of the methods listed below executes your query and returns
665             the results. Your C<view> is unchanged and can be reused later.
666            
667             See L<Elastic::Manual::Searching> for a discussion about when
668             and how to use L</search()>, L</scroll()> or L</scan()>.
669            
670             =head2 search()
671            
672             $results = $view->search();
673            
674             Executes a search and returns an L<Elastic::Model::Results> object
675             with at most L</size> results.
676            
677             This is useful for returning finite results, ie where you know how many
678             results you want. For instance: I<"give me the 10 best results">.
679            
680             =head2 cached_search()
681            
682             B<NOTE: Think carefully before you cache data outside of Elasticsearch.
683             Elasticsearch already has smart filter caches, which are updated as your data
684             changes. Most of the time, you will be better off using those directly,
685             instead of an external cache.>
686            
687             $results = $view->cache( $cache )->cached_search( %opts );
688            
689             If a L</cache> attribute has been specified for the current view, then
690             L</cached_search()> tries to retrieve the search results from the L</cache>.
691             If it fails, then a L</search()> is executed, and the results are stored in
692             the L</cache>. An L<Elastic::Model::Results::Cached> object is returned.
693            
694             Any C<%opts> that are passed in override any default L</cache_opts>, and are
695             passed to L<CHI's get() or set()|'https://metacpan.org/module/CHI#Getting-and-setting>
696             methods.
697            
698             $view = $view->cache_opts( expires_in => '30 sec' );
699            
700             $results = $view->cached_search; # 30 seconds
701             $results = $view->cached_search( expires_in => '2 min' ); # 2 minutes
702            
703             Given the near-real-time nature of Elasticsearch, you sometimes want to
704             invalidate a cached result in the near future. For instance, if you have
705             cached a list of comments on a blog post, but then you add a new comment,
706             you want to invalidate the cached comments list. However, the new
707             comment will only become visible to search sometime within the next second, so
708             invalidating the cache immediately may or may not be useful.
709            
710             Use the special argument C<force_set> to bypass the cache C<get()> and to force
711             the cached version to be updated, along with a new expiry time:
712            
713             $results = $view->cached_search( force_set => 1, expires_in => '2 sec');
714            
715             =head2 scroll()
716            
717             $scroll_timeout = '1m';
718             $scrolled_results = $view->scroll( $scroll_timeout );
719            
720             Executes a search and returns an L<Elastic::Model::Results::Scrolled>
721             object which will pull L</size> results from Elasticsearch as required until
722             either (1) no more results are available or (2) more than C<$scroll_timeout>
723             (default 1 minute) elapses between requests to Elasticsearch.
724            
725             Scrolling allows you to return an unbound result set. Useful if you're not
726             sure whether to expect 2 results or 2000.
727            
728             =head2 scan()
729            
730             $timeout = '1m';
731             $scrolled_results = $view->scan($timeout);
732            
733             L</scan()> is a special type of L</scroll()> request, intended for efficient
734             handling of large numbers of unsorted docs (eg when you want to reindex
735             all of your data).
736            
737             =head2 first()
738            
739             $result = $view->first();
740             $object = $view->first->object;
741            
742             Executes the search and returns just the first result. All other
743             metadata is thrown away.
744            
745             =head2 total()
746            
747             $total = $view->total();
748            
749             Executes the search and returns the total number of matching docs.
750             All other metadta is thrown away.
751            
752             =head2 delete()
753            
754             $results = $view->delete();
755            
756             Deletes all docs matching the query and returns a hashref indicating
757             success. Any docs that are stored in a live L<scope|Elastic::Model::Scope>
758             or are cached somewhere are not removed. Any
759             L<unique keys|Elastic::Manual::Attributes::Unique> are not removed.
760            
761             This should really only be used once you are sure that the matching docs
762             are out of circulation. Also, it is more efficient to just delete a whole index
763             (if possible), rather than deleting large numbers of docs.
764            
765             B<Note:> The only attributes relevant to L</delete()> are L</domain>,
766             L</type>, L</query>, L</routing>, L</consistency> and L</replication>.
767            
768             =head1 CORE ATTRIBUTES
769            
770             =head2 domain
771            
772             $new_view = $view->domain('my_index');
773             $new_view = $view->domain('index_one','alias_two');
774            
775             \@domains = $view->domain;
776            
777             Specify one or more domains (indices or aliases) to query. By default, a C<view>
778             created from a L<domain|Elastic::Model::Domain> will query just that domain's
779             L<name|Elastic::Model::Domain/name>.
780             A C<view> created from the L<model|Elastic::Model::Role::Model> will query all
781             the main domains (ie the L<Elastic::Model::Namespace/name>) and
782             L<fixed domains|Elastic::Model::Namesapace/fixed domains> known to the model.
783            
784             =head2 type
785            
786             $new_view = $view->type('user');
787             $new_view = $view->type('user','post');
788            
789             \@types = $view->type;
790            
791             By default, a C<view> will query all L<types|Elastic::Manual::Terminology/Type>
792             known to all the L<domains|"domain"> specified in the view. You can specify
793             one or more types.
794            
795             =head2 query
796            
797             =head2 queryb
798            
799             # native query DSL
800             $new_view = $view->query( text => { title => 'interesting words' } );
801            
802             # SearchBuilder DSL
803             $new_view = $view->queryb( title => 'interesting words' );
804            
805             \%query = $view->query
806            
807             Specify the query to run in the native
808             L<Elasticsearch query DSL|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html>
809             or use C<queryb()> to specify your query with the more Perlish
810             L<Elastic::Model::SearchBuilder> query syntax.
811            
812             By default, the query will
813             L<match all docs|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-match-all-query.html>.
814            
815             =head2 filter
816            
817             =head2 filterb
818            
819             # native query DSL
820             $new_view = $view->filter( term => { tag => 'perl' } );
821            
822             # SearchBuilder DSL
823             $new_view = $view->filterb( tag => 'perl' );
824            
825             \%filter = $view->filter;
826            
827             You can specify a filter to apply to the query results using either
828             the native Elasticsearch query DSL or, use C<filterb()> to specify your
829             filter with the more Perlish L<Elastic::Model::SearchBuilder> DSL.
830             If a filter is specified, it will be combined with the L</query>
831             as a L<filtered query|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-filtered-query.html>,
832             or (if no query is specified) as a
833             L<constant score|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html>
834             query.
835            
836             =head2 post_filter
837            
838             =head2 post_filterb
839            
840             # native query DSL
841             $new_view = $view->post_filter( term => { tag => 'perl' } );
842            
843             # SearchBuilder DSL
844             $new_view = $view->post_filterb( tag => 'perl' );
845            
846             \%filter = $view->post_filter;
847            
848             L<Post-filters|http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-post-filter.html>
849             filter the results AFTER any L</aggs> have been calculated. In the above
850             example, the aggregations would be calculated on all values of C<tag>, but the
851             results would then be limited to just those docs where C<tag == perl>.
852            
853             You can specify a post_filter using either the native Elasticsearch query DSL or,
854             use C<post_filterb()> to specify it with the more Perlish
855             L<Elastic::Model::SearchBuilder> DSL.
856            
857             =head2 sort
858            
859             $new_view = $view->sort( '_score' ); # _score desc
860             $new_view = $view->sort( 'timestamp' ); # timestamp asc
861             $new_view = $view->sort( { timestamp => 'asc' } ); # timestamp asc
862             $new_view = $view->sort( { timestamp => 'desc' } ); # timestamp desc
863            
864             $new_view = $view->sort(
865             '_score', # _score desc
866             { timestamp => 'desc' } # then timestamp desc
867             );
868            
869             \@sort = $view->sort
870            
871             By default, results are sorted by "relevance" (C<< _score => 'desc' >>).
872             You can specify multiple sort arguments, which are applied in order, and
873             can include scripts or geo-distance.
874             See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-sort.html> for
875             more information.
876            
877             B<Note:> Sorting cannot be combined with L</scan()>.
878            
879             =head2 from
880            
881             $new_view = $view->from( 10 );
882            
883             $from = $view->from;
884            
885             By default, results are returned from the first result. Think of it as
886             I<"the number of docs to skip">, so setting C<from> to C<0> would start from
887             the first result. Setting C<from> to C<10> would skip the first 10 results
888             and return docs from result number 11 onwards.
889            
890             =head2 size
891            
892             $new_view = $view->size( 100 );
893            
894             $size = $view->size;
895            
896             The number of results returned in a single L</search()>, which defaults to 10.
897            
898             B<Note:> See L</scan()> for a slightly different application of the L</size>
899             value.
900            
901             =head2 aggs
902            
903             $new_view = $view->aggs(
904             active_docs => {
905             filter => {
906             term => { status => 'active' }
907             },
908             aggs => {
909             popular_tags => {
910             terms => {
911             field => 'path.to.tags',
912             size => 10
913             }
914             }
915             }
916             },
917             agg_two => {....}
918             );
919            
920             $new_view = $view->add_agg( agg_three => {...} )
921             $new_view = $view->remove_agg('agg_three');
922            
923             \%aggs = $view->aggs;
924             \%agg = $view->get_agg('active_docs');
925            
926             Aggregations allow you to aggregate data from a query, for instance: most popular
927             terms, number of blog posts per day, average price etc. Aggs are calculated
928             from the query generated from L</query> and L</filter>. If you want to filter
929             your query results down further after calculating your aggs, you can
930             use L</post_filter>.
931            
932             B<NOTE:> There is no support in aggs for L<Elastic::Model::SearchBuilder>.
933            
934             See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html> for
935             an explanation of what aggregations are available.
936            
937             =head2 facets
938            
939             B<IMPORTANT:> Facets are deprecated in favour of L<aggregations|/aggs>.
940             They will be removed in a future version of Elasticsearch.
941            
942             $new_view = $view->facets(
943             facet_one => {
944             terms => {
945             field => 'field.to.facet',
946             size => 10
947             },
948             facet_filterb => { status => 'active' },
949             },
950             facet_two => {....}
951             );
952            
953             $new_view = $view->add_facet( facet_three => {...} )
954             $new_view = $view->remove_facet('facet_three');
955            
956             \%facets = $view->facets;
957             \%facet = $view->get_facet('facet_one');
958            
959             Facets allow you to aggregate data from a query, for instance: most popular
960             terms, number of blog posts per day, average price etc. Facets are calculated
961             from the query generated from L</query> and L</filter>. If you want to filter
962             your query results down further after calculating your facets, you can
963             use L</post_filter>.
964            
965             See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-facets.html> for
966             an explanation of what facets are available.
967            
968             =head2 highlight
969            
970             $new_view = $view->highlight(
971             'field_1',
972             'field_2' => \%field_2_settings,
973             'field_3'
974             );
975            
976             Specify which fields should be used for
977             L<highlighted snippets|http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-highlighting.html>.
978             to your search results. You can pass just a list of fields, or fields with
979             their field-specific settings. These values are used to set the C<fields>
980             parameter in L</highlighting>.
981            
982             =head2 highlighting
983            
984             $new_view = $view->highlighting(
985             pre_tags => [ '<em>', '<b>' ],
986             post_tags => [ '</em>', '</b>' ],
987             encoder => 'html'
988             ...
989             );
990            
991             The L</highlighting> attribute is used to pass any highlighting parameters
992             which should be applied to all of the fields set in L</highlight> (although
993             you can override these settings for individual fields by passing field settings
994             to L</highlight>).
995            
996             See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-highlighting.html>.
997             for more about how highlighting works, and L<Elastic::Model::Result/highlight>
998             for how to retrieve the highlighted snippets.
999            
1000             =head1 OTHER ATTRIBUTES
1001            
1002             =head2 fields
1003            
1004             $new_view = $view->fields('title','content');
1005            
1006             By default, searches will return the L<_source|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-source-field.html>
1007             field which contains the whole document, allowing Elastic::Model to inflate
1008             the original object without having to retrieve the document separately. If you
1009             would like to just retrieve a subset of fields, you can specify them in
1010             L</fields>. See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-fields.html>.
1011            
1012             B<Note:> If you do specify any fields, and you DON'T include C<'_source'> then the
1013             C<_source> field won't be returned, and you won't be able to retrieve the original
1014             object without requesting it from Elasticsearch in a separate (but automatic) step.
1015            
1016             =head2 script_fields
1017            
1018             $new_view = $view->script_fields(
1019             distance => {
1020             script => q{doc['location'].distance(lat,lon)},
1021             params => { lat => $lat, lon => $lon }
1022             },
1023             $name => \%defn,
1024             ...
1025             );
1026            
1027             $new_view = $view->add_script_field( $name => \%defn );
1028             $new_view = $view->remove_script_field($name);
1029            
1030             \%fields = $view->script_fields;
1031             \%defn = $view->get_script_field($name);
1032            
1033             L<Script fields|http://www.elasticsearch.org/guide/en/elasticsearch/reference/0.90/search-request-script-fields.html>
1034             can be generated using the L<mvel|http://mvel.codehaus.org/Language+Guide+for+2.0>
1035             scripting language. (You can also use L<Groovy, Javascript, Python and Java|http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-scripting.html>.)
1036            
1037             =head2 include_paths / exclude_paths
1038            
1039             $new_view = $view->include_paths('foo.*')
1040             ->exclude_paths('foo.bar.*','baz.*');
1041            
1042             $results = $new_view->search->as_partials;
1043             $partial_obj = $results->next;
1044            
1045             If your objects are large, but you only need access to a few attributes to
1046             eg display search results, you may want to retrieve only the relevant parts of
1047             each object. You can specify which parts of the object to include or exclude
1048             using C<include_paths> and C<exclude_paths>. If either of these is set
1049             then the full C<_source> field will not be loaded (unless you specify it
1050             explicitly using L</fields>).
1051            
1052             The partial objects returned when L<Elastic::Model::Results/as_partials()>
1053             is in effect function exactly as real objects, except that they cannot
1054             be saved.
1055            
1056             See L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-source-filtering.html>.
1057            
1058             =head2 routing
1059            
1060             $new_view = $view->routing( 'routing_val' );
1061             $new_view = $view->routing( 'routing_1', 'routing_2' );
1062            
1063             Search queries are usually directed at all shards. If you are using routing
1064             (eg to store related docs on the same shard) then you can limit the search
1065             to just the relevant shard(s). B<Note:> if you are searching on aliases that
1066             have routing configured, then specifying a L</routing> manually will override
1067             those values.
1068            
1069             See L<Elastic::Manual::Scaling> for more.
1070            
1071             =head2 index_boosts
1072            
1073             $new_view = $view->index_boosts(
1074             index_1 => 4,
1075             index_2 => 2
1076             );
1077            
1078             $new_view = $view->add_index_boost( $index => $boost );
1079             $new_view = $view->remove_index_boost( $index );
1080            
1081             \%boosts = $view->index_boosts;
1082             $boost = $view->get_index_boost( $index );
1083            
1084             Make results from one index more relevant than those from another index.
1085            
1086             =head2 min_score
1087            
1088             $new_view = $view->min_score( 2 );
1089             $min_score = $view->min_score;
1090            
1091             Exclude results whose score (relevance) is less than the specified number.
1092            
1093             =head2 preference
1094            
1095             $new_view = $view->preference( '_local' );
1096            
1097             Control which node should return search results. See
1098             L<http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-preference.html> for more.
1099            
1100             =head2 timeout
1101            
1102             $new_view = $view->timeout( 10 ); # 10 ms
1103             $new_view = $view->timeout( '10s' ); # 10 sec
1104            
1105             $timeout = $view->timeout;
1106            
1107             Sets an upper limit on the the time to wait for search results, returning
1108             with whatever results it has managed to receive up until that point.
1109            
1110             =head2 track_scores
1111            
1112             $new_view = $view->track_scores( 1 );
1113             $track = $view->track_scores;
1114            
1115             By default, If you sort on a field other than C<_score>, Elasticsearch
1116             does not return the calculated relevance score for each doc. If
1117             L</track_scores> is true, these scores will be returned regardless.
1118            
1119             =head1 CACHING ATTRIBUTES
1120            
1121             Bounded searches (those returned by calling L</search()>) can be stored
1122             in a L<CHI>-compatible cache.
1123            
1124             =head2 cache
1125            
1126             $cache = CHI->new(...);
1127             $new_view = $view->cache( $cache );
1128            
1129             Stores an instance of a L<CHI>-compatible cache, to be used with
1130             L</cached_search()>.
1131            
1132             =head2 cache_opts
1133            
1134             $new_view = $view->cache_opts( expires_in => '20 sec', ...);
1135            
1136             Stores the default options that should be passed to
1137             L<CHI's get() or set()|'https://metacpan.org/module/CHI#Getting-and-setting>.
1138             These can be overridden by passing options to L</cached_search()>.
1139            
1140             =head1 DEBUGGING ATTRIBUTES
1141            
1142             =head2 explain
1143            
1144             $new_view = $view->explain( 1 );
1145             $explain = $view->explain;
1146            
1147             Set L</explain> to true to return debugging information explaining how
1148             each document's score was calculated. See
1149             L<Elastic::Model::Result/explain> to view the output.
1150            
1151             =head2 stats
1152            
1153             $new_view = $view->stats( 'group_1', 'group_2' );
1154             \@groups = $view->stats;
1155            
1156             The statistics for each search can be aggregated by C<group>. These stats
1157             can later be retrieved using L<Search::Elasticsearch::Client::Direct::Indices/stats()>.
1158            
1159             =head2 search_builder
1160            
1161             $new_view = $view->search_builder( $search_builder );
1162             $builder = $view->search_builder;
1163            
1164             If you would like to use a different search builder than the default
1165             L<Elastic::Model::SearchBuilder> for L</"queryb">, L</"filterb"> or
1166             L</post_filterb>, then you can set a value for L</search_builder>.
1167            
1168             =head1 DELETE ATTRIBUTES
1169            
1170             These parameters are only used with L</delete()>.
1171            
1172             =head2 consistency
1173            
1174             $new_view = $view->consistency( 'quorum' | 'all' | 'one' );
1175             $consistency = $view->consistency;
1176            
1177             At least C<one>, C<all> or a C<quorum> (default) of nodes must be present for
1178             the delete to take place.
1179            
1180             =head2 replication
1181            
1182             $new_view = $view->replication( 'sync' | 'async' );
1183             $replication = $view->replication;
1184            
1185             Should a delete be done synchronously (ie waits until all nodes within
1186             the replcation group have run the delete) or asynchronously (returns
1187             immediately, and performs the delete in the background).
1188            
1189             =head1 AUTHOR
1190            
1191             Clinton Gormley <drtech@cpan.org>
1192            
1193             =head1 COPYRIGHT AND LICENSE
1194            
1195             This software is copyright (c) 2015 by Clinton Gormley.
1196            
1197             This is free software; you can redistribute it and/or modify it under
1198             the same terms as the Perl 5 programming language system itself.
1199            
1200             =cut
1201              
1202             __END__
1203            
1204             # ABSTRACT: Views to query your docs in Elasticsearch
1205            
1206