line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Elastic::Model::Role::Doc; |
2
|
|
|
|
|
|
|
$Elastic::Model::Role::Doc::VERSION = '0.51'; |
3
|
23
|
|
|
23
|
|
16837
|
use Moose::Role; |
|
23
|
|
|
|
|
53
|
|
|
23
|
|
|
|
|
224
|
|
4
|
|
|
|
|
|
|
|
5
|
23
|
|
|
23
|
|
126604
|
use Elastic::Model::Trait::Exclude; |
|
23
|
|
|
|
|
83
|
|
|
23
|
|
|
|
|
162
|
|
6
|
23
|
|
|
23
|
|
84077
|
use MooseX::Types::Moose qw(Maybe Bool HashRef); |
|
23
|
|
|
|
|
58
|
|
|
23
|
|
|
|
|
186
|
|
7
|
23
|
|
|
23
|
|
111781
|
use Elastic::Model::Types qw(Timestamp UID); |
|
23
|
|
|
|
|
57
|
|
|
23
|
|
|
|
|
227
|
|
8
|
23
|
|
|
23
|
|
136627
|
use Scalar::Util qw(refaddr); |
|
23
|
|
|
|
|
56
|
|
|
23
|
|
|
|
|
1660
|
|
9
|
23
|
|
|
23
|
|
130
|
use Try::Tiny; |
|
23
|
|
|
|
|
43
|
|
|
23
|
|
|
|
|
1457
|
|
10
|
23
|
|
|
23
|
|
18265
|
use Time::HiRes(); |
|
23
|
|
|
|
|
36539
|
|
|
23
|
|
|
|
|
680
|
|
11
|
23
|
|
|
23
|
|
161
|
use Carp; |
|
23
|
|
|
|
|
44
|
|
|
23
|
|
|
|
|
1651
|
|
12
|
23
|
|
|
23
|
|
145
|
use namespace::autoclean; |
|
23
|
|
|
|
|
48
|
|
|
23
|
|
|
|
|
260
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
has 'uid' => ( |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
isa => UID, |
18
|
|
|
|
|
|
|
is => 'ro', |
19
|
|
|
|
|
|
|
required => 1, |
20
|
|
|
|
|
|
|
writer => '_set_uid', |
21
|
|
|
|
|
|
|
traits => ['Elastic::Model::Trait::Exclude'], |
22
|
|
|
|
|
|
|
exclude => 1, |
23
|
|
|
|
|
|
|
handles => [ 'id', 'type' ] |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has 'timestamp' => ( |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
traits => ['Elastic::Model::Trait::Field'], |
30
|
|
|
|
|
|
|
isa => Timestamp, |
31
|
|
|
|
|
|
|
is => 'rw', |
32
|
|
|
|
|
|
|
exclude => 0 |
33
|
|
|
|
|
|
|
); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
has '_can_inflate' => ( |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
isa => Bool, |
39
|
|
|
|
|
|
|
is => 'rw', |
40
|
|
|
|
|
|
|
default => 0, |
41
|
|
|
|
|
|
|
traits => ['Elastic::Model::Trait::Exclude'], |
42
|
|
|
|
|
|
|
exclude => 1, |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
has '_source' => ( |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
isa => Maybe [HashRef], |
49
|
|
|
|
|
|
|
is => 'ro', |
50
|
|
|
|
|
|
|
traits => ['Elastic::Model::Trait::Exclude'], |
51
|
|
|
|
|
|
|
lazy => 1, |
52
|
|
|
|
|
|
|
exclude => 1, |
53
|
|
|
|
|
|
|
builder => '_get_source', |
54
|
|
|
|
|
|
|
writer => '_set_source', |
55
|
|
|
|
|
|
|
); |
56
|
|
|
|
|
|
|
|
57
|
23
|
|
|
23
|
|
4568
|
no Moose::Role; |
|
23
|
|
|
|
|
47
|
|
|
23
|
|
|
|
|
496
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub has_changed { |
61
|
|
|
|
|
|
|
|
62
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
63
|
0
|
|
|
|
|
|
my $old = $self->old_values; |
64
|
0
|
0
|
|
|
|
|
return '' unless keys %$old; |
65
|
|
|
|
|
|
|
return @_ |
66
|
0
|
0
|
|
|
|
|
? exists $old->{ $_[0] } |
67
|
|
|
|
|
|
|
: 1; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub old_values { |
72
|
|
|
|
|
|
|
|
73
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
74
|
0
|
0
|
|
|
|
|
return {} if $self->_can_inflate; |
75
|
0
|
|
|
|
|
|
my $current = $self->model->deflate_object($self); |
76
|
0
|
0
|
|
|
|
|
my %original = %{ $self->uid->from_store ? $self->_source : {} }; |
|
0
|
|
|
|
|
|
|
77
|
0
|
|
|
|
|
|
my $json = $self->model->json; |
78
|
0
|
|
|
|
|
|
my %old; |
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
|
my ( $o, $c, $o_str, $c_str ); |
81
|
0
|
|
|
|
|
|
my $meta = Class::MOP::class_of($self); |
82
|
0
|
|
|
|
|
|
my $model = $self->model; |
83
|
0
|
|
|
|
|
|
for my $key ( keys %$current ) { |
84
|
0
|
0
|
|
|
|
|
unless ( exists $original{$key} ) { |
85
|
0
|
|
|
|
|
|
$old{$key} = undef; |
86
|
0
|
|
|
|
|
|
next; |
87
|
|
|
|
|
|
|
} |
88
|
23
|
|
|
23
|
|
9724
|
no warnings 'uninitialized'; |
|
23
|
|
|
|
|
50
|
|
|
23
|
|
|
|
|
16562
|
|
89
|
0
|
|
|
|
|
|
( $o, $c ) = ( delete $original{$key}, $current->{$key} ); |
90
|
0
|
0
|
|
|
|
|
( $o_str, $c_str ) |
91
|
0
|
|
|
|
|
|
= map { ref $_ ? $json->encode($_) : $_ } ( $o, $c ); |
92
|
|
|
|
|
|
|
|
93
|
0
|
0
|
|
|
|
|
if ( $o_str ne $c_str ) { |
94
|
0
|
|
|
|
|
|
$old{$key} = $meta->inflator_for( $model, $key )->($o); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
$old{$_} = $meta->inflator_for( $model, $_ )->( $original{$_} ) |
98
|
0
|
|
|
|
|
|
for keys %original; |
99
|
0
|
|
|
|
|
|
return \%old; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub old_value { |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
0
|
0
|
|
die('old_value($attr) has been removed. Use old_values->{$attr} instead'); |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _get_source { |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
0
|
|
|
my $self = shift; |
112
|
0
|
|
|
|
|
|
$self->model->get_doc_source( |
113
|
|
|
|
|
|
|
uid => $self->uid, |
114
|
|
|
|
|
|
|
ignore => 404, |
115
|
|
|
|
|
|
|
@_ |
116
|
|
|
|
|
|
|
); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub _inflate_doc { |
121
|
|
|
|
|
|
|
|
122
|
0
|
|
|
0
|
|
|
my $self = $_[0]; |
123
|
0
|
0
|
|
|
|
|
my $source = $self->_source |
124
|
|
|
|
|
|
|
or return bless( $self, 'Elastic::Model::Deleted' )->croak; |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
$self->_can_inflate(0); |
127
|
|
|
|
|
|
|
eval { |
128
|
0
|
|
|
|
|
|
$self->model->inflate_object( $self, $source ); |
129
|
0
|
|
|
|
|
|
1; |
130
|
0
|
0
|
|
|
|
|
} or do { |
131
|
0
|
|
|
|
|
|
my $error = $@; |
132
|
0
|
|
|
|
|
|
$self->_can_inflate(1); |
133
|
0
|
|
|
|
|
|
die $error; |
134
|
|
|
|
|
|
|
}; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub touch { |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
141
|
0
|
|
|
|
|
|
$self->timestamp( int( Time::HiRes::time * 1000 + 0.5 ) / 1000 ); |
142
|
0
|
|
|
|
|
|
$self; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub save { |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
149
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
unless ( $self->_can_inflate ) { |
151
|
0
|
|
|
|
|
|
$self->touch; |
152
|
0
|
|
|
|
|
|
$self->model->save_doc( doc => $self, @_ ); |
153
|
|
|
|
|
|
|
} |
154
|
0
|
|
|
|
|
|
$self; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
0
|
|
|
0
|
1
|
|
sub overwrite { shift->save( @_, version => 0 ) } |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub delete { |
163
|
|
|
|
|
|
|
|
164
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
165
|
0
|
0
|
|
|
|
|
$self->model->delete_doc( uid => $self->uid, @_ ) |
166
|
|
|
|
|
|
|
or return; |
167
|
0
|
|
|
|
|
|
bless $self, 'Elastic::Model::Deleted'; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub has_been_deleted { |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
174
|
0
|
|
|
|
|
|
my $uid = $self->uid; |
175
|
0
|
0
|
|
|
|
|
$uid->from_store or return 0; |
176
|
0
|
|
|
|
|
|
!$self->model->doc_exists( uid => $uid, @_ ); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub terms_indexed_for_field { |
181
|
|
|
|
|
|
|
|
182
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
183
|
0
|
0
|
|
|
|
|
my $field = shift or croak "Missing required param (fieldname)"; |
184
|
0
|
|
0
|
|
|
|
my $size = shift || 20; |
185
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
|
my $uid = $self->uid; |
187
|
0
|
|
|
|
|
|
return $self->model->view->domain( $uid->index )->type( $uid->type ) |
188
|
|
|
|
|
|
|
->filterb( _id => $uid->id ) |
189
|
|
|
|
|
|
|
->facets( field => { terms => { field => $field, size => 20 } } ) |
190
|
|
|
|
|
|
|
->size(0)->search->facet('field'); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
1; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=pod |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=encoding UTF-8 |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=head1 NAME |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Elastic::Model::Role::Doc - The role applied to your Doc classes |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head1 VERSION |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
version 0.51 |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head1 SYNOPSIS |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head2 Creating a doc |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$doc = $domain->new_doc( |
212
|
|
|
|
|
|
|
user => { |
213
|
|
|
|
|
|
|
id => 123, # auto-generated if not specified |
214
|
|
|
|
|
|
|
email => 'clint@domain.com', |
215
|
|
|
|
|
|
|
name => 'Clint' |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
$doc->save; |
220
|
|
|
|
|
|
|
$uid = $doc->uid; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head2 Retrieving a doc |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
$doc = $domain->get( user => 123 ); |
225
|
|
|
|
|
|
|
$doc = $model->get_doc( uid => $uid ); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head2 Updating a doc |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
$doc->name('John'); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
print $doc->has_changed(); # 1 |
232
|
|
|
|
|
|
|
print $doc->has_changed('name'); # 1 |
233
|
|
|
|
|
|
|
print $doc->has_changed('email'); # 0 |
234
|
|
|
|
|
|
|
dump $doc->old_values; # { name => 'Clint' } |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
$doc->save; |
237
|
|
|
|
|
|
|
print $doc->has_changed(); # 0 |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head2 Deleting a doc |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
$doc->delete; |
242
|
|
|
|
|
|
|
print $doc->has_been_deleted # 1 |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 DESCRIPTION |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
L<Elastic::Model::Role::Doc> is applied to your "doc" classes (ie those classes |
247
|
|
|
|
|
|
|
that you want to be stored in Elasticsearch), when you include this line: |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
use Elastic::Doc; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
This document explains the changes that are made to your class by applying the |
252
|
|
|
|
|
|
|
L<Elastic::Model::Role::Doc> role. Also see L<Elastic::Doc>. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The following attributes are added to your class: |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head2 uid |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
The L<uid|Elastic::Model::UID> is the unique identifier for your doc in |
261
|
|
|
|
|
|
|
Elasticsearch. It contains an L<index|Elastic::Model::UID/"index">, |
262
|
|
|
|
|
|
|
a L<type|Elastic::Model::UID/"type">, an L<id|Elastic::Model::UID/"id"> and |
263
|
|
|
|
|
|
|
possibly a L<routing|Elastic::Model::UID/"routing">. This is what is required |
264
|
|
|
|
|
|
|
to identify your document uniquely in Elasticsearch. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
The UID is created when you create your document, eg: |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
$doc = $domain->new_doc( |
269
|
|
|
|
|
|
|
user => { |
270
|
|
|
|
|
|
|
id => 123, |
271
|
|
|
|
|
|
|
other => 'foobar' |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
); |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=over |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item * |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
C<index> : initially comes from the C<< $domain->name >> - this is changed |
280
|
|
|
|
|
|
|
to the actual domain name when you save your doc. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=item * |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
C<type> : comes from the first parameter passed to |
285
|
|
|
|
|
|
|
L<new_doc()|Elastic::Model::Domain/"new_doc()"> (C<user> in this case). |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=item * |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
C<id> : is optional - if you don't provide it, then it will be |
290
|
|
|
|
|
|
|
auto-generated when you save it to Elasticsearch. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=back |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
B<Note:> the C<namespace_name/type/ID> of a document must be unique. |
295
|
|
|
|
|
|
|
Elasticsearch can enforce uniqueness for a single index, but when your |
296
|
|
|
|
|
|
|
L<namespace|Elastic::Model::Namespace> contains multiple indices, it is up |
297
|
|
|
|
|
|
|
to you to ensure uniqueness. Either leave the ID blank, in which case |
298
|
|
|
|
|
|
|
Elasticsearch will generate a unique ID, or ensure that the way you |
299
|
|
|
|
|
|
|
generate IDs will not cause a collision. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head2 type / id |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
$type = $doc->type; |
304
|
|
|
|
|
|
|
$id = $doc->id; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
C<type> and C<id> are provided as convenience, read-only accessors which |
307
|
|
|
|
|
|
|
call the equivalent accessor on L</uid>. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
You can defined your own C<id()> and C<type()> methods, in which case they |
310
|
|
|
|
|
|
|
won't be imported, or you can import them under a different name, eg: |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
package MyApp::User; |
313
|
|
|
|
|
|
|
use Elastic::Doc; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
with 'Elastic::Model::Role::Doc' => { |
316
|
|
|
|
|
|
|
-alias => { |
317
|
|
|
|
|
|
|
id => 'doc_id', |
318
|
|
|
|
|
|
|
type => 'doc_type', |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
}; |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head2 timestamp |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
$timestamp = $doc->timestamp($timestamp); |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
This stores the last-modified time (in epoch seconds with milli-seconds), which |
327
|
|
|
|
|
|
|
is set automatically when your doc is saved. The C<timestamp> is indexed |
328
|
|
|
|
|
|
|
and can be used in queries. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head2 Private attributes |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
These private attributes are also added to your class, and are documented |
333
|
|
|
|
|
|
|
here so that you don't override them without knowing what you are doing: |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=head3 _can_inflate |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
A boolean indicating whether the object has had its attributes values |
338
|
|
|
|
|
|
|
inflated already or not. |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=head3 _source |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
The raw uninflated source value as loaded from Elasticsearch. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=head1 METHODS |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=head2 save() |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
$doc->save( %args ); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Saves the C<$doc> to Elasticsearch. If this is a new doc, and a doc with the |
351
|
|
|
|
|
|
|
same type and ID already exists in the same index, then Elasticsearch |
352
|
|
|
|
|
|
|
will throw an exception. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Also see L<Elastic::Model::Bulk> for bulk indexing of multiple docs. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
If the doc was previously loaded from Elasticsearch, then that doc will be |
357
|
|
|
|
|
|
|
updated. However, because Elasticsearch uses |
358
|
|
|
|
|
|
|
L<optimistic locking|http://en.wikipedia.org/wiki/Optimistic_locking> |
359
|
|
|
|
|
|
|
(ie the doc version number is incremented on every change), it is possible that |
360
|
|
|
|
|
|
|
another process has already updated the C<$doc> while the current process has |
361
|
|
|
|
|
|
|
been working, in which case it will throw a conflict error. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
For instance: |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
ONE TWO |
366
|
|
|
|
|
|
|
-------------------------------------------------- |
367
|
|
|
|
|
|
|
get doc 1-v1 |
368
|
|
|
|
|
|
|
get doc 1-v1 |
369
|
|
|
|
|
|
|
save doc 1-v2 |
370
|
|
|
|
|
|
|
save doc1-v2 |
371
|
|
|
|
|
|
|
-> # conflict error |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=head3 on_conflict |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
If you don't care, and you just want to overwrite what is stored in Elasticsearch |
376
|
|
|
|
|
|
|
with the current values, then use L</overwrite()> instead of L</save()>. If you |
377
|
|
|
|
|
|
|
DO care, then you can handle this situation gracefully, using the |
378
|
|
|
|
|
|
|
C<on_conflict> parameter: |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
$doc->save( |
381
|
|
|
|
|
|
|
on_conflict => sub { |
382
|
|
|
|
|
|
|
my ($original_doc,$new_doc) = @_; |
383
|
|
|
|
|
|
|
# resolve conflict |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
See L</has_been_deleted()> for a fuller example of an L</on_conflict> callback. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
The doc will only be saved if it has changed. If you want to force saving |
391
|
|
|
|
|
|
|
on a doc that hasn't changed, then you can do: |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
$doc->touch->save; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=head3 on_unique |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
If you have any L<unique attributes|Elastic::Manual::Attributes/unique_key> then |
398
|
|
|
|
|
|
|
you can catch unique-key conflicts with the C<on_unique> handler. |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
$doc->save( |
401
|
|
|
|
|
|
|
on_unique => sub { |
402
|
|
|
|
|
|
|
my ($doc,$conflicts) = @_; |
403
|
|
|
|
|
|
|
# do something |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
) |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
The C<$conflicts> hashref will contain a hashref whose keys are the name |
408
|
|
|
|
|
|
|
of the L<unique_keys|Elastic::Manual::Attributes/unique_key> that have |
409
|
|
|
|
|
|
|
conflicts, and whose values are the values of those keys which already exist, |
410
|
|
|
|
|
|
|
and so cannot be overwritten. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
See L<Elastic::Manual::Attributes::Unique> for more. |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head2 overwrite() |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$doc->overwrite( %args ); |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
L</overwrite()> is exactly the same as L</save()> except it will overwrite |
419
|
|
|
|
|
|
|
any previous doc, regardless of whether another process has created or updated |
420
|
|
|
|
|
|
|
a doc with the same UID in the meantime. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 delete() |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
$doc->delete; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
This will delete the current doc. If the doc has already been updated |
427
|
|
|
|
|
|
|
to a new version by another process, it will throw a conflict error. You |
428
|
|
|
|
|
|
|
can override this and delete the document anyway with: |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
$doc->delete( version => 0 ); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
The C<$doc> will be reblessed into the L<Elastic::Model::Deleted> class, |
433
|
|
|
|
|
|
|
and any attempt to access its attributes will throw an error. |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head2 has_been_deleted() |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
$bool = $doc->has_been_deleted(); |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
As a rule, you shouldn't delete docs that are currently in use elsewhere in |
440
|
|
|
|
|
|
|
your application, otherwise you have to wrap all of your code in C<eval>s |
441
|
|
|
|
|
|
|
to ensure that you're not accessing a stale doc. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
However, if you do need to delete current docs, then L</has_been_deleted()> |
444
|
|
|
|
|
|
|
checks if the doc exists in Elasticsearch. For instance, you |
445
|
|
|
|
|
|
|
might have an L</on_conflict> handler which looks like this: |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
$doc->save( |
448
|
|
|
|
|
|
|
on_conflict => sub { |
449
|
|
|
|
|
|
|
my ($original, $new) = @_; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
return $original->overwrite |
452
|
|
|
|
|
|
|
if $new->has_been_deleted; |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
for my $attr ( keys %{ $old->old_values }) { |
455
|
|
|
|
|
|
|
$new->$attr( $old->$attr ): |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
$new->save |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
); |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
It is a much better approach to remove docs from the main flow of your |
463
|
|
|
|
|
|
|
application (eg, set a C<status> attribute to C<"deleted">) then physically |
464
|
|
|
|
|
|
|
delete the docs only after some time has passed. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head2 touch() |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
$doc = $doc->touch() |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Updates the L</"timestamp"> to the current time. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head2 has_changed() |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Has the value for any attribute changed? |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
$bool = $doc->has_changed; |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Has the value of attribute C<$attr_name> changed? |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
$bool = $doc->has_changed($attr_name); |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
B<Note:> If you're going to check more than one attribute, rather get |
483
|
|
|
|
|
|
|
all the L</old_values()> and check if the attribute name exists in the |
484
|
|
|
|
|
|
|
returned hash, rather than calling L<has_changed()> multiple times. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=head2 old_values() |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
\%old_vals = $doc->old_values(); |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Returns a hashref containing the original values of any attributes that have |
491
|
|
|
|
|
|
|
been changed. If an attribute wasn't set originally, but is now, it will |
492
|
|
|
|
|
|
|
be included in the hash with the value C<undef>. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=head2 terms_indexed_for_field() |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
$terms = $doc->terms_indexed_for_field( $fieldname, $size ); |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
This method is useful for debugging queries and analysis - it returns |
499
|
|
|
|
|
|
|
the actual terms (ie after analysis) that have been indexed for |
500
|
|
|
|
|
|
|
field C<$fieldname> in the current doc. C<$size> defaults to 20. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head2 Private methods |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
These private methods are also added to your class, and are documented |
505
|
|
|
|
|
|
|
here so that you don't override them without knowing what you are doing: |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=head3 _inflate_doc |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Inflates the attribute values from the hashref stored in L</"_source">. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head3 _get_source / _set_source |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
The raw doc source from Elasticsearch. |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head1 AUTHOR |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Clinton Gormley <drtech@cpan.org> |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
This software is copyright (c) 2015 by Clinton Gormley. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
524
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=cut |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
__END__ |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
# ABSTRACT: The role applied to your Doc classes |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
|