File Coverage

blib/lib/Elastic/Model/Role/Model.pm
Criterion Covered Total %
statement 98 267 36.7
branch 4 100 4.0
condition 4 39 10.2
subroutine 75 101 74.2
pod 21 23 91.3
total 202 530 38.1


line stmt bran cond sub pod time code
1             package Elastic::Model::Role::Model;
2             $Elastic::Model::Role::Model::VERSION = '0.51';
3 23     23   33452 use Moose::Role;
  23         97464  
  23         125  
4 23     23   116496 use Carp;
  23         47  
  23         1860  
5 23     23   28624 use Elastic::Model::Types qw(ES);
  23         93  
  23         165  
6 23     23   137250 use Search::Elasticsearch 1.20 ();
  23         958  
  23         635  
7 23     23   128 use Class::Load qw(load_class);
  23         38  
  23         1646  
8 23     23   214 use Moose::Util qw(does_role);
  23         41  
  23         314  
9 23     23   4573 use MooseX::Types::Moose qw(:all);
  23         45  
  23         183  
10 23     23   193541 use Elastic::Model::UID();
  23         93  
  23         994  
11 23     23   14203 use Elastic::Model::Deleted();
  23         85  
  23         925  
12 23     23   197 use Scalar::Util qw(blessed refaddr weaken);
  23         35  
  23         1742  
13 23     23   1061 use List::MoreUtils qw(uniq);
  23         108  
  23         248  
14 23     23   13666 use JSON();
  23         55555  
  23         1040  
15             our $JSON = JSON->new->canonical->utf8;
16              
17 23     23   130 use namespace::autoclean;
  23         33  
  23         185  
18             my @wrapped_classes = qw(
19             domain namespace store view scope
20             results cached_results scrolled_results result bulk
21             );
22              
23             #===================================
24             sub BUILD {
25             #===================================
26 24     24 0 46673     my $self = shift;
27 24         975     my $es = $self->es;
28 24 50       343     if ( $es->isa('Search::Elasticsearch::Client::0_90::Direct') ) {
29 0         0         $self->_set_result_class(
30                         $self->_wrap_class('Elastic::Model::0_90::Result') );
31 0         0         $self->_set_store_class(
32                         $self->_wrap_class('Elastic::Model::0_90::Store') );
33                 }
34 24         896     $self->doc_class_wrappers;
35 24         144     return $self;
36              
37             }
38              
39             for my $class (@wrapped_classes) {
40             #===================================
41                 has "${class}_class" => (
42             #===================================
43                     isa => Str,
44                     is => 'ro',
45                     lazy => 1,
46                     writer => "_set_${class}_class",
47                     default => sub { shift->wrap_class($class) }
48                 );
49             }
50              
51             #===================================
52             has 'typemap' => (
53             #===================================
54                 is => 'ro',
55                 isa => Str,
56                 default => sub { shift->wrap_class('typemap') }
57             );
58              
59             #===================================
60             has [ 'deflators', 'inflators' ] => (
61             #===================================
62                 isa => HashRef,
63                 is => 'ro',
64                 default => sub { {} }
65             );
66              
67             #===================================
68             has 'store' => (
69             #===================================
70                 does => 'Elastic::Model::Role::Store',
71                 is => 'ro',
72                 lazy => 1,
73                 builder => '_build_store'
74             );
75              
76             #===================================
77             has 'es' => (
78             #===================================
79                 isa => ES,
80                 is => 'ro',
81                 lazy => 1,
82                 builder => '_build_es',
83                 coerce => 1,
84             );
85              
86             #===================================
87             has '_unique_index' => (
88             #===================================
89                 isa => Str,
90                 is => 'ro',
91                 lazy => 1,
92                 builder => '_build_unique_index',
93             );
94              
95             #===================================
96             has 'doc_class_wrappers' => (
97             #===================================
98                 is => 'ro',
99                 isa => HashRef,
100                 traits => ['Hash'],
101                 lazy => 1,
102                 builder => '_build_doc_class_wrappers',
103                 handles => {
104                     class_for => 'get',
105                     knows_class => 'exists'
106                 }
107             );
108              
109             #===================================
110             has 'namespaces' => (
111             #===================================
112                 is => 'ro',
113                 isa => HashRef,
114                 traits => ['Hash'],
115                 builder => '_build_namespaces',
116                 handles => { namespace => 'get' }
117             );
118              
119             #===================================
120             has '_domain_cache' => (
121             #===================================
122                 isa => HashRef,
123                 is => 'bare',
124                 traits => ['Hash'],
125                 default => sub { {} },
126              
127             # TODO clear domain cache when changing indices/aliases?
128                 handles => {
129                     _get_domain => 'get',
130                     _cache_domain => 'set',
131                 },
132             );
133              
134             #===================================
135             has '_domain_namespace' => (
136             #===================================
137                 is => 'ro',
138                 isa => HashRef,
139                 traits => ['Hash'],
140                 lazy => 1,
141                 builder => '_build_domain_namespace',
142                 clearer => 'clear_domain_namespace',
143                 handles => { _get_domain_namespace => 'get', }
144             );
145              
146             #===================================
147             has 'current_scope' => (
148             #===================================
149                 is => 'rw',
150                 isa => 'Elastic::Model::Scope',
151                 weak_ref => 1,
152                 clearer => 'clear_current_scope',
153                 predicate => 'has_current_scope',
154             );
155              
156             #===================================
157 1     1   27 sub _build_store { $_[0]->store_class->new( es => $_[0]->es ) }
158 24     24   242 sub _build_es { Search::Elasticsearch->new }
159             #===================================
160              
161             #===================================
162             sub _build_unique_index {
163             #===================================
164 0     0   0     my $self = shift;
165 0         0     my $index = Class::MOP::class_of($self)->unique_index;
166 0         0     $self->store->bootstrap_uniques( index => $index );
167 0         0     return $index;
168             }
169              
170             #===================================
171             sub _build_namespaces {
172             #===================================
173 24     24   22615     my $self = shift;
174 24         113     my $conf = Class::MOP::class_of($self)->namespaces;
175 24         45     my %namespaces;
176 24         728     my $ns_class = $self->namespace_class;
177 24         166     while ( my ( $name, $args ) = each %$conf ) {
178 52         127         my $types = $args->{types};
179 338         13022         my %classes
180 52         253             = map { $_ => $self->class_for( $types->{$_} ) } keys %$types;
181 52   100     2656         $namespaces{$name} = $ns_class->new(
182                         name => $name,
183                         types => \%classes,
184                         fixed_domains => $args->{fixed_domains} || []
185                     );
186                 }
187 24         186     \%namespaces;
188             }
189              
190             #===================================
191             sub _build_doc_class_wrappers {
192             #===================================
193 24     24   76     my $self = shift;
194 24         116     my $namespaces = Class::MOP::class_of($self)->namespaces;
195 172         841     +{ map { $_ => $self->wrap_doc_class($_) }
  28         196  
196 24         92         map { values %{ $_->{types} } } values %$namespaces
  28         47  
197                 };
198             }
199              
200             #===================================
201             sub _build_domain_namespace {
202             #===================================
203 0     0   0     my $self = shift;
204 0         0     my $namespaces = $self->namespaces;
205 0         0     my %domains;
206              
207 0         0     for my $name ( keys %$namespaces ) {
208 0         0         my $ns = $namespaces->{$name};
209 0         0         for my $domain ( $namespaces->{$name}->all_domains ) {
210 0 0       0             croak "Cannot map domain ($domain) to namespace ($name). "
211                             . "It is already mapped to namespace ("
212                             . $domains{$domain}->name . ")."
213                             if $domains{$domain};
214 0         0             $domains{$domain} = $ns;
215                     }
216                 }
217 0         0     \%domains;
218             }
219              
220             #===================================
221             sub namespace_for_domain {
222             #===================================
223 0     0 1 0     my ( $self, $domain ) = @_;
224 0         0     my $ns;
225 0 0       0     $ns = $self->_get_domain_namespace($domain) and return $ns;
226 0         0     $self->clear_domain_namespace;
227 0 0       0     $self->_get_domain_namespace($domain)
228                     or croak "No namespace found for domain ($domain). ";
229             }
230              
231             #===================================
232             sub all_live_indices {
233             #===================================
234 0     0 1 0     my $self = shift;
235 0         0     return map { $_->all_live_indices } values %{ $self->namespaces };
  0         0  
  0         0  
236             }
237              
238             #===================================
239             sub wrap_doc_class {
240             #===================================
241 172     172 1 395     my $self = shift;
242 172         311     my $class = shift;
243              
244 172         912     load_class($class);
245              
246 172 50       16986775     croak "Class ($class) does not do Elastic::Model::Role::Doc. "
247                     . "Please add : use Elastic::Doc;\n\n"
248                     unless Moose::Util::does_role( $class, 'Elastic::Model::Role::Doc' );
249              
250 172         53610     $self->_wrap_class($class);
251             }
252              
253             #===================================
254             sub wrap_class {
255             #===================================
256 62     62 1 148     my $self = shift;
257 62   50     253     my $name = shift || '';
258 62 50       350     my $class = $self->meta->get_class($name)
259                     or croak "Unknown class for ($name)";
260              
261 62         278     $self->_wrap_class($class);
262             }
263              
264             #===================================
265             sub _wrap_class {
266             #===================================
267 234     234   646     my $self = shift;
268 234         530     my $class = shift;
269 234         1030     load_class($class);
270              
271 234         9562     my $meta
272                     = Moose::Meta::Class->create(
273                     Class::MOP::class_of($self)->wrapped_class_name($class),
274                     superclasses => [$class] );
275              
276 234         519602     weaken( my $weak_model = $self );
277 234     76   2090     $meta->add_method( model => sub {$weak_model} );
  76     76   2162  
        76      
        76      
        76      
        76      
        76      
        54      
        54      
        54      
        34      
        34      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
278 234     1153   14015     $meta->add_method( original_class => sub {$class} );
  1153     1153   2379  
        1153      
        1153      
        1153      
        1153      
        1153      
        1118      
        1118      
        1118      
        84      
        84      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
        22      
279 234         8377     $meta->make_immutable;
280              
281 234         426626     return $meta->name;
282             }
283              
284             #===================================
285             sub domain {
286             #===================================
287 0     0 1 0     my $self = shift;
288 0 0       0     my $name = shift or croak "No domain name passed to domain()";
289 0         0     my $domain;
290              
291 0 0       0     $domain = $self->_get_domain($name) and return $domain;
292 0 0       0     my $ns = $self->namespace_for_domain($name)
293                     or croak "Unknown domain name ($name)";
294              
295 0         0     $domain = $self->domain_class->new(
296                     name => $name,
297                     namespace => $ns
298                 );
299 0         0     return $self->_cache_domain( $name => $domain );
300             }
301              
302             #===================================
303 1     1 1 56 sub view { shift->view_class->new(@_) }
304             #===================================
305              
306             #===================================
307             sub new_scope {
308             #===================================
309 0     0 1 0     my $self = shift;
310                 my @args
311 0 0       0         = $self->has_current_scope ? ( parent => $self->current_scope ) : ();
312 0         0     $self->current_scope( $self->scope_class->new(@args) );
313             }
314              
315             #===================================
316             sub detach_scope {
317             #===================================
318 0     0 1 0     my ( $self, $scope ) = @_;
319 0         0     my $current = $self->current_scope;
320 0 0 0     0     return unless $current && refaddr($current) eq refaddr($scope);
321 0         0     my $parent = $scope->parent;
322 0 0       0     return $self->clear_current_scope unless $parent;
323 0         0     $self->current_scope($parent);
324             }
325              
326             #===================================
327             sub get_doc {
328             #===================================
329 0     0 1 0     my ( $self, %args ) = @_;
330 0 0       0     my $uid = $args{uid}
331                     or croak "No UID passed to get_doc()";
332              
333 0         0     my $ns = $self->namespace_for_domain( $uid->index );
334 0         0     my $scope = $self->current_scope;
335 0         0     my $source = $args{source};
336              
337 0         0     my $object;
338 0 0 0     0     $object = $scope->get_object( $ns->name, $uid )
339                     if $scope && !$source;
340              
341 0 0       0     unless ($object) {
342 0 0 0     0         unless ( $source || $uid->from_store ) {
343 0 0       0             $source = $self->get_doc_source(%args) or return;
344                     }
345 0         0         my $class = $ns->class_for_type( $uid->type );
346 0         0         $object = $class->meta->new_stub( $uid, $source );
347 0 0       0         $object = $scope->store_object( $ns->name, $object )
348                         if $scope;
349                 }
350 0         0     $object;
351             }
352              
353             #===================================
354             sub get_doc_source {
355             #===================================
356 0     0 1 0     my ( $self, %args ) = @_;
357              
358 0         0     my $uid = delete $args{uid};
359 0 0       0     my $result = $self->store->get_doc( $uid, %args ) or return;
360 0         0     $uid->update_from_store($result);
361 0         0     return $result->{_source};
362             }
363              
364             #===================================
365             sub new_partial_doc {
366             #===================================
367 0     0 1 0     my ( $self, %args ) = @_;
368 0 0       0     my $uid = $args{uid}
369                     or croak "No UID passed to new_partial_doc()";
370              
371 0 0       0     my $source = $args{partial_source}
372                     or croak "No (partial_source) passed to new_partial_doc()";
373              
374 0         0     my $ns = $self->namespace_for_domain( $uid->index );
375              
376 0         0     my $class = $ns->class_for_type( $uid->type );
377 0         0     return $class->meta->new_stub( $uid, $source );
378             }
379              
380             #===================================
381             sub doc_exists {
382             #===================================
383 0     0 1 0     my ( $self, %args ) = @_;
384 0 0       0     my $uid = delete $args{uid}
385                     or croak "No UID passed to doc_exists()";
386 0         0     return $self->store->doc_exists( $uid, %args );
387             }
388              
389             #===================================
390             sub save_doc {
391             #===================================
392 0     0 1 0     my ( $self, %args ) = @_;
393              
394 0         0     my $doc = delete $args{doc};
395 0         0     my $uid = $doc->uid;
396              
397 0 0       0     croak "Cannot save partial doc type ("
398                     . $uid->type
399                     . ") id ("
400                     . $uid->id . ")"
401                     if $uid->is_partial;
402              
403 0         0     my $data = $self->deflate_object($doc);
404              
405 0 0 0     0     my $action
406                     = ( $uid->from_store or $uid->id and defined $args{version} )
407                     ? 'index_doc'
408                     : 'create_doc';
409              
410 0         0     my $on_unique = delete $args{on_unique};
411 0         0     my $on_conflict = delete $args{on_conflict};
412              
413 0 0       0     my $unique = $self->_update_unique_keys( $doc, $action, $on_unique )
414                     or return;
415              
416 0 0       0     my $result = eval { $self->store->$action( $uid, $data, %args ) } or do {
  0         0  
417 0         0         my $error = $@;
418 0         0         $unique->{rollback}->();
419 0         0         return $self->_handle_error( $error, $on_conflict, $doc );
420                 };
421              
422 0         0     $unique->{commit}->();
423              
424 0         0     $uid->update_from_store($result);
425 0         0     $doc->_set_source($data);
426              
427 0 0       0     my $scope = $self->current_scope
428                     or return $doc;
429              
430 0         0     my $ns = $self->namespace_for_domain( $uid->index );
431 0         0     return $scope->store_object( $ns->name, $doc );
432             }
433              
434             my $noops = {
435                 commit => sub { },
436                 rollback => sub { }
437             };
438              
439             #===================================
440             sub _update_unique_keys {
441             #===================================
442 0     0   0     my ( $self, $doc, $action, $on_unique ) = @_;
443 0         0     my $meta = Class::MOP::class_of($doc);
444 0 0       0     my $uniques = $meta->unique_keys
445                     or return $noops;
446              
447 0         0     my $from_store = $doc->uid->from_store;
448              
449 0 0 0     0     croak "Cannot overwrite a new doc of class ("
450                     . $doc->original_class
451                     . ") because it has unique keys"
452                     if $action eq 'index_doc' and not $from_store;
453              
454 0         0     my ( %old, %new );
455 0         0     for my $key ( keys %$uniques ) {
456 0         0         my $unique_key = $uniques->{$key};
457 0         0         my $new = $doc->$key;
458 23     23   50414         no warnings 'uninitialized';
  23         48  
  23         14331  
459              
460 0 0       0         if ($from_store) {
461 0         0             my $old = $doc->_source->{$key};
462 0 0       0             next if $old eq $new;
463 0 0       0             $old{$unique_key} = $old if length $old;
464                     }
465              
466 0 0       0         $new{$unique_key} = $new if length $new;
467                 }
468              
469 0         0     my $uniq = $self->_unique_index;
470 0         0     my $store = $self->store;
471              
472 0 0       0     if ( my %failed
473                     = $store->create_unique_keys( index => $uniq, keys => \%new ) )
474                 {
475 0 0       0         if ($on_unique) {
476 0         0             $on_unique->( $doc, \%failed );
477 0         0             return;
478                     }
479 0         0         croak "Unique keys already exist: "
480 0         0             . join( ', ', map { $_ . '/' . $failed{$_} } sort keys %failed );
481              
482                 }
483                 return {
484                     commit => sub {
485 0     0   0             $store->delete_unique_keys( index => $uniq, keys => \%old );
486                     },
487                     rollback => sub {
488 0     0   0             $store->delete_unique_keys( index => $uniq, keys => \%new );
489                     },
490 0         0     };
491             }
492              
493             #===================================
494             sub _handle_error {
495             #===================================
496 0     0   0     my ( $self, $error, $on_conflict, $original ) = @_;
497 0   0     0     $error ||= 'Unknown error';
498              
499 0 0 0     0     die $error
500                     unless $on_conflict
501                     and $error->is('Conflict');
502              
503 0         0     my $new;
504 0 0       0     if ( my $current_version = $error->{vars}{current_version} ) {
505 0         0         my $uid = Elastic::Model::UID->new(
506 0         0             %{ $original->uid->read_params },
507                         version => $current_version,
508                         from_store => 1
509                     );
510 0         0         $new = $self->get_doc( uid => $uid );
511              
512                 }
513                 else {
514 0         0         $new = $self->get_doc( uid => $original->uid->clone );
515                 }
516              
517 0         0     $on_conflict->( $original, $new );
518              
519 0         0     return;
520             }
521              
522             #===================================
523             sub delete_doc {
524             #===================================
525 0     0 1 0     my ( $self, %args ) = @_;
526 0 0       0     my $uid = delete $args{uid}
527                     or croak "No UID passed to delete_doc()";
528              
529 0         0     my $unique = $self->_delete_unique_keys($uid);
530 0 0       0     my $result = $self->store->delete_doc( $uid, %args )
531                     or return;
532 0         0     $unique->{commit}->();
533              
534 0         0     $uid->update_from_store($result);
535              
536 0 0       0     if ( my $scope = $self->current_scope ) {
537 0         0         my $ns = $self->namespace_for_domain( $uid->index );
538 0         0         $scope->delete_object( $ns->name, $uid );
539                 }
540 0         0     return $uid;
541             }
542              
543             #===================================
544             sub _delete_unique_keys {
545             #===================================
546 0     0   0     my ( $self, $uid ) = @_;
547              
548 0 0       0     my $doc = $self->get_doc( uid => $uid, ignore => 404 )
549                     or return $noops;
550              
551 0         0     my $meta = Class::MOP::class_of($doc);
552 0 0       0     my $uniques = $meta->unique_keys or return $noops;
553              
554 0         0     my %old;
555 0         0     for my $key ( keys %$uniques ) {
556 23     23   144         no warnings 'uninitialized';
  23         38  
  23         16007  
557 0         0         my $old = $doc->_source->{$key};
558 0 0       0         $old{ $uniques->{$key} } = $old if length $old;
559                 }
560 0         0     my $uniq = $self->_unique_index;
561 0         0     my $store = $self->store;
562                 return {
563                     commit => sub {
564 0     0   0             $store->delete_unique_keys(
565                             index => $uniq,
566                             keys => \%old
567                         );
568                     },
569 0         0     };
570             }
571              
572             #===================================
573 0     0 1 0 sub bulk { shift->bulk_class->new(@_) }
574             #===================================
575              
576             #===================================
577 0     0 1 0 sub search { shift->store->search(@_) }
578             #===================================
579              
580             #===================================
581             sub deflate_object {
582             #===================================
583 0     0 1 0     my $self = shift;
584 0 0       0     my $object = shift or die "No object passed to deflate()";
585 0 0       0     my $class = blessed $object
586                     or die "deflate() can only deflate objects";
587 0         0     $self->deflator_for_class($class)->($object);
588             }
589              
590             #===================================
591             sub deflator_for_class {
592             #===================================
593 0     0 1 0     my $self = shift;
594 0         0     my $class = shift;
595 0   0     0     $class = $self->class_for($class) || $class;
596 0   0     0     return $self->deflators->{$class} ||= do {
597 0 0       0         die "Class $class is not an Elastic class."
598                         unless does_role( $class, 'Elastic::Model::Role::Doc' );
599 0         0         $self->typemap->class_deflator($class);
600                 };
601             }
602              
603             #===================================
604             sub inflate_object {
605             #===================================
606 0     0 1 0     my $self = shift;
607 0 0       0     my $object = shift or die "No object passed to inflate()";
608 0 0       0     my $hash = shift or die "No hash pashed to inflate()";
609 0         0     $self->inflator_for_class( blessed $object)->( $object, $hash );
610             }
611              
612             #===================================
613             sub inflator_for_class {
614             #===================================
615 0     0 1 0     my $self = shift;
616 0         0     my $class = shift;
617 0   0     0     $class = $self->class_for($class) || $class;
618 0   0     0     return $self->inflators->{$class} ||= do {
619 0 0       0         die "Class $class is not an Elastic class."
620                         unless does_role( $class, 'Elastic::Model::Role::Doc' );
621 0         0         $self->typemap->class_inflator($class);
622                 };
623             }
624              
625             #===================================
626             sub map_class {
627             #===================================
628 9     9 1 21     my $self = shift;
629 9         17     my $class = shift;
630 9   33     394     $class = $self->class_for($class) || $class;
631              
632 9 50       55     die "Class $class is not an Elastic class."
633                     unless does_role( $class, 'Elastic::Model::Role::Doc' );
634              
635 9         3389     my $meta = $class->original_class->meta;
636              
637 9         384     my %mapping = (
638 9         168         %{ $meta->mapping },
639                     $self->typemap->class_mapping($class),
640                     dynamic => 'strict',
641                     _timestamp => { enabled => 1, path => 'timestamp' },
642                     numeric_detection => 1,
643                 );
644 9         33     delete $mapping{type};
645 9         107     return \%mapping;
646             }
647              
648             #===================================
649 0     0 0   sub json {$JSON}
650             #===================================
651              
652             1;
653              
654             =pod
655            
656             =encoding UTF-8
657            
658             =head1 NAME
659            
660             Elastic::Model::Role::Model - The role applied to your Model
661            
662             =head1 VERSION
663            
664             version 0.51
665            
666             =head1 SYNOPSIS
667            
668             use MyApp;
669            
670             my $es = Search::Elasticsearch->new( nodes => 'es.domain.com:9200' );
671             my $model = MyApp->new( es => $es );
672            
673             my $namespace = $model->namespace('myapp');
674             my $domain = $model->domain('my_domain');
675             my $view = $model->view();
676            
677             my $scope = $model->new_scope;
678            
679             =head1 DESCRIPTION
680            
681             A "Model" is the Boss Object, which ties an instance of your application to
682             a particular Elasticsearch cluster. You can have multiple instances of your
683             Model class which connect to different clusters.
684            
685             C<Elastic::Model::Role::Model> is applied to your Model class when you
686             include the line:
687            
688             use Elastic::Model;
689            
690             See L<Elastic::Model> for more about how to setup your Model class.
691            
692             =head1 COMMONLY USED METHODS
693            
694             =head2 new()
695            
696             Usually, the only parameter that you need to pass to C<new()> is C<es>,
697             which contains your L<Search::Elasticsearch> connection.
698            
699             $es = Search::Elasticsearch->new( nodes => 'es1.domain.com:9200' );
700             $model = MyApp->new( es => $es );
701            
702             If the C<es> parameter is omitted, then it will default to a L<Search::Elasticsearch>
703             connection to C<localhost:9200>.
704            
705             $model = MyApp->new(); # localhost:9200
706            
707             =head2 namespace()
708            
709             $namespace = $model->namespace($name);
710            
711             Returns the L<Elastic::Model::Namespace> instance corresponding to
712             C<$name>. The namespace must have been configured via
713             L<Elastic::Model/has_namespace>.
714            
715             Use a C<$namespace> to create, delete and update
716             L<indices|Elastic::Manual::Terminology/Index> and
717             L<index aliases|Elastic::Manual::Terminology/Alias>.
718            
719             =head2 domain()
720            
721             $domain = $model->domain($name);
722            
723             Returns an L<Elastic::Model::Domain> instance where C<$name> is the name
724             of an L<index|Elastic::Manual::Terminology/Index> or
725             L<index alias|Elastic::Manual::Terminology/Alias> (which points at a single
726             index) and is known to one of the L</namespaces>.
727            
728             Use a C<$domain> to create, retrieve, update or delete individual
729             objects/documents.
730            
731             =head2 view()
732            
733             $view = $model->view(%args);
734            
735             Creates a new L<Elastic::Model::View> instance. Any args are passed on to
736             L<Elastic::Model::View/"new()">.
737            
738             Use a C<$view> to query your documents. Views can be multi-domain and
739             multi-type.
740            
741             =head2 new_scope()
742            
743             $scope = $model->new_scope();
744            
745             Creates a new L<Elastic::Model::Scope> instance (in-memory cache). If there is
746             an existing scope, then the new scope inherits from the existing scope.
747            
748             $scope = $model->new_scope(); # scope_1
749             $scope = $model->new_scope(); # scope_2, inherits from scope_1
750             undef $scope; # scope_2 and scope_1 are destroyed
751            
752             Scopes are optional unless you have attributes which are weakened.
753            
754             See L<Elastic::Model::Scoping> and L<Elastic::Model::Scope> to read more about
755             how scopes work.
756            
757             =head1 OTHER METHODS AND ATTRIBUTES
758            
759             These methods and attributes, while public, are usually used only by internal
760             modules. They are documented here for completeness.
761            
762             =head2 CRUD
763            
764             =head3 get_doc()
765            
766             Normally, you want to use L<Elastic::Model::Domain/"get()"> rather than this
767             method.
768            
769             $doc = $model->get_doc(uid => $uid);
770             $doc = $model->get_doc(uid => $uid, ignore => 404, ...);
771            
772             C<get_doc()> tries to retrieve the object corresponding to the
773             L<$uid|Elastic::Model::UID>, first from the L</current_scope()> (if there is one)
774             or from any of its parents. Failing that, it tries to retrieve the doc
775             from the L</store>. If it finds the doc, then
776             it stores it in the current scope (again, if there is one), otherwise it
777             throws an error.
778            
779             C<get_doc()> also accepts an optional C<$source> parameter which is
780             used internally for inflating search results.
781             See L<Elastic::Model::Scope> for a more detailed explanation.
782            
783             Any other args are passed on to L<Elastic::Model::Store/get_doc()>.
784            
785             =head3 get_doc_source()
786            
787             $doc = $model->get_doc_source(uid => $uid);
788             $doc = $model->get_doc_source(uid => $uid, ignore => 404, ...);
789            
790             Calls L<Elastic::Model::Store/"get_doc()"> and returns the raw source hashref
791             as stored in Elasticsearch for the doc with the corresponding
792             L<$uid|Elastic::Model::UID>. Throws an error if it doesn't exist.
793            
794             Any other args are passed on to L<Elastic::Model::Store/get_doc()>.
795            
796             =head3 doc_exists()
797            
798             $bool = $model->doc_exists( uid => $uid, %args );
799            
800             Calls L<Elastic::Model::Role::Store/doc_exists()> to check whether the doc
801             exists.
802            
803             =head3 save_doc()
804            
805             Normally, you want to use L<Elastic::Model::Role::Doc/"save()"> rather than this
806             method.
807            
808             $doc = $model->save_doc(doc => $doc, %args);
809            
810             Saves C<$doc> to Elasticsearch by calling
811             L<Elastic::Model::Store/"index_doc()"> (if the C<$doc> was originally loaded
812             from Elasticsearch), or L<Elastic::Model::Store/"create_doc()">, which
813             will throw an error if a doc with the same C<index|type|id> already
814             exists.
815            
816             Any C<%args> are passed on to L<index_doc()|Elastic::Model::Store/"index_doc()"> or
817             L<create_doc()|Elastic::Model::Store/"create_doc()">.
818            
819             If there is a L</current_scope()> then the object is also stored there.
820            
821             Also see the L<Elastic::Model::Role::Doc/on_conflict> and
822             L<Elastic::Model::Role::Doc/on_unique> parameters.
823            
824             =head3 delete_doc()
825            
826             $uid = $model->delete_doc(uid => $uid, ignore => 404, ...)
827            
828             Calls L<Elastic::Model::Store/delete_doc()> and returns the updated
829             L<Elastic::Model::UID> object. Throws an error if it doesn't exist.
830             If there is a L</current_scope()> then an L<Elastic::Model::Deleted> object
831             is stored there.
832            
833             =head3 search()
834            
835             Normally, you want to use L<Elastic::Model::View> rather than this
836             method.
837            
838             $results = $model->search(%args)
839            
840             Passes C<%args> through to L<Elastic::Model::Store/"search()">
841            
842             =head3 new_partial_doc()
843            
844             part_doc = $model->new_partial_doc(
845             uid => $uid,
846             partial_source => \%source
847             );
848            
849             Creates an instance of a partial doc (ie an object which contains only some of
850             the values stored in Elasticsearch). These partial docs are useful when
851             your objects are large, and you need to display search results which
852             require only a few attributes, instead of the whole object.
853            
854             Attempting to save a partial doc will cause an error to be thrown.
855            
856             You shouldn't need to call this method yourself.
857            
858             =head3 bulk()
859            
860             Returns a new instance of L<Elastic::Model::Bulk> for fast indexing
861             of multiple docs in batches.
862            
863             $bulk = $model->bulk(
864             size => 1000,
865             on_conflict => sub {...},
866             on_error => sub {...},
867             on_success => sub {...}
868             );
869            
870             =head2 Miscellaneous
871            
872             =head3 namespaces
873            
874             \%namespaces = $model->namespaces;
875            
876             A hashref whose keys are the namespace names, and whose values are the
877             corresponding L<Elastic::Model::Namespace> instances.
878            
879             =head3 namespace_for_domain()
880            
881             $namespace = $model->namespace_for_domain($domain_name)
882            
883             Returns the L<Elastic::Model::Namespace> object which corresponds to the
884             C<$domain_name>. If the index (or alias) name is not yet known to the
885             C<$model>, it will update the known domain list from the namespace objects.
886            
887             =head3 all_live_indices()
888            
889             @indices = $model->all_live_indices();
890            
891             Queries Elasticsearch to find all existing indices related to all namespaces
892             known to the model.
893            
894             =head3 es
895            
896             $es = $model->es
897            
898             Returns the L<Search::Elasticsearch> instance that was passed to L</"new()">.
899            
900             =head3 store
901            
902             $store = $model->store
903            
904             Returns the L<Elastic::Model::Store> instance.
905            
906             =head2 Deflation, Inflation And Mapping
907            
908             =head3 typemap
909            
910             $typemap_class = $model->typemap;
911            
912             Elastic::Model uses L<Elastic::Model::TypeMap::Default> (after
913             L<wrapping|/wrap_class()> it) to figure out how
914             to deflate and inflate your objects, and how to configure (map) them in
915             Elasticsearch.
916            
917             You can specify your own type-map class in your model configuration with
918             L<has_typemap|Elastic::Model/Custom TypeMap>. See
919             L<Elastic::Model::TypeMap::Base> for instructions on how to define
920             your own type-map classes.
921            
922             =head3 deflator_for_class()
923            
924             $deflator = $model->deflator_for_class($class);
925            
926             Returns a code-ref that knows how to deflate a class which does
927             L<Elastic::Model::Role::Doc>, and caches the deflator in L</"deflators">.
928            
929             =head3 deflate_object()
930            
931             $hash = $model->deflate_object($object);
932            
933             Uses the deflator returned by L</"deflator_for_class()"> to deflate
934             an object which does L<Elastic::Model::Role::Doc> into a hash ref
935             suitable for conversion to JSON.
936            
937             =head3 deflators
938            
939             $deflators = $model->deflators
940            
941             A hashref which caches all of the deflators which have been generated by
942             L</"deflator_for_class()">.
943            
944             =head3 inflator_for_class()
945            
946             $inflator = $model->inflator_for_class($class);
947            
948             Returns a code-ref that knows how to inflate a plain hashref into the correct
949             attribute values for a class which does L<Elastic::Model::Role::Doc>,
950             and caches the inflator in L</"inflators">.
951            
952             =head3 inflate_object()
953            
954             $object = $model->inflate_object($object,$hash);
955            
956             Uses the inflator returned by L</"inflator_for_class()"> to inflate
957             the attribute values of C<$object> from the value stored in C<$hash>.
958            
959             =head3 inflators
960            
961             $inflators = $model->inflators
962            
963             A hashref which caches all of the inflators which have been generated by
964             L</"inflator_for_class()">.
965            
966             =head3 map_class()
967            
968             $mapping = $model->map_class($class);
969            
970             Returns the type mapping / schema for a class which does
971             L<Elastic::Model::Role::Doc>, suitable for passing to Elasticsearch.
972            
973             =head2 Scoping
974            
975             Also see L</"new_scope()"> and L<Elastic::Model::Scope>.
976            
977             =head3 current_scope()
978            
979             $scope = $model->current_scope($scope);
980            
981             Read/write accessor for the current scope. Throws an exception if no scope
982             is currently set.
983            
984             =head3 detach_scope()
985            
986             $model->detach_scope($scope);
987            
988             Removes the passed in C<$scope> if it is the current scope. Replaces
989             the current scope with its parent scope, if there is one. L</"detach_scope()">
990             is called automatically when a scope goes out of scope:
991            
992             {
993             my $scope = $model->new_scope;
994             # do work
995             }
996             # current scope is removed
997            
998             =head3 has_current_scope()
999            
1000             $bool = $model->has_current_scope
1001            
1002             Returns a true or false value signalling whether a L</"current_scope()">
1003             exists.
1004            
1005             =head3 clear_current_scope()
1006            
1007             $model->clear_current_scope
1008            
1009             Clears the L</"current_scope()">
1010            
1011             =head2 Core classes
1012            
1013             The following core classes are used internally by Elasticsearch, after
1014             being wrapped by L</wrap_class()>, which pins the new anonymous class
1015             to the current C<$model> instance. An instance of the wrapped class
1016             can be created with, eg:
1017            
1018             $domain = $model->domain_class->new(%args);
1019            
1020             If you would like to override any of the core classes, then you can specify
1021             them in your model setup using
1022             L<override_classes|Elastic::Model/Overriding Core Classes>.
1023            
1024             =head3 Default core classes:
1025            
1026             =over
1027            
1028             =item *
1029            
1030             C<domain_class> C<--------------> L<Elastic::Model::Domain>
1031            
1032             =item *
1033            
1034             C<store_class> C<---------------> L<Elastic::Model::Store>
1035            
1036             =item *
1037            
1038             C<view_class> C<----------------> L<Elastic::Model::View>
1039            
1040             =item *
1041            
1042             C<scope_class> C<---------------> L<Elastic::Model::Scope>
1043            
1044             =item *
1045            
1046             C<results_class> C<-------------> L<Elastic::Model::Results>
1047            
1048             =item *
1049            
1050             C<cached_results_class> C<------> L<Elastic::Model::Results::Cached>
1051            
1052             =item *
1053            
1054             C<scrolled_results_class> C<----> L<Elastic::Model::Results::Scrolled>
1055            
1056             =item *
1057            
1058             C<result_class> C<--------------> L<Elastic::Model::Result>
1059            
1060             =back
1061            
1062             =head3 wrap_class()
1063            
1064             $wrapped_class = $model->wrap_class($class)
1065            
1066             Wraps a class in an anonymous class and adds two methods: C<model()> (which
1067             returns the current C<$model> instance, and C<original_class())>, which
1068             returns the name of the wrapped class:
1069            
1070             $model = $wrapped_class->model
1071             $class = $wrapped_class->original_class;
1072            
1073             =head3 wrap_doc_class()
1074            
1075             Like L</"wrap_class()">, but specifically for classes which do
1076             L<Elastic::Model::Role::Doc>.
1077            
1078             =head3 doc_class_wrappers
1079            
1080             $wrapped_classes = $model->doc_class_wrappers
1081            
1082             A hashref of all wrapped doc classes (ie those classes which do
1083             L<Elastic::Model::Role::Doc>). The keys are the original class names, and
1084             the values are the wrapped class names.
1085            
1086             =head3 class_for()
1087            
1088             $wrapped_class = $model->class_for($class);
1089            
1090             Returns the name of the wrapped class which corresponds to C<$class>.
1091            
1092             =head3 knows_class()
1093            
1094             $bool = $model->knows_class($class);
1095            
1096             Returns a true or false value to signal whether doc C<$class> has been wrapped.
1097            
1098             =head1 AUTHOR
1099            
1100             Clinton Gormley <drtech@cpan.org>
1101            
1102             =head1 COPYRIGHT AND LICENSE
1103            
1104             This software is copyright (c) 2015 by Clinton Gormley.
1105            
1106             This is free software; you can redistribute it and/or modify it under
1107             the same terms as the Perl 5 programming language system itself.
1108            
1109             =cut
1110              
1111             __END__
1112            
1113             # ABSTRACT: The role applied to your Model
1114            
1115