File Coverage

blib/lib/MongoDB/Op/_Update.pm
Criterion Covered Total %
statement 33 64 51.5
branch 0 48 0.0
condition 0 13 0.0
subroutine 11 15 73.3
pod 0 1 0.0
total 44 141 31.2


line stmt bran cond sub pod time code
1             # Copyright 2014 - present MongoDB, Inc.
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14              
15 58     58   478 use strict;
  58         147  
  58         1883  
16 58     58   357 use warnings;
  58         135  
  58         2121  
17             package MongoDB::Op::_Update;
18              
19             # Encapsulate an update operation; returns a MongoDB::UpdateResult
20              
21 58     58   331 use version;
  58         144  
  58         325  
22             our $VERSION = 'v2.2.0';
23              
24 58     58   4665 use Moo;
  58         141  
  58         329  
25              
26 58     58   40567 use MongoDB::UpdateResult;
  58         204  
  58         2168  
27 58     58   487 use MongoDB::_Protocol;
  58         140  
  58         1648  
28 58         330 use MongoDB::_Types qw(
29             Boolish
30             Document
31 58     58   338 );
  58         139  
32 58         268 use Types::Standard qw(
33             Maybe
34             ArrayRef
35 58     58   65096 );
  58         194  
36 58     58   45078 use Tie::IxHash;
  58         200  
  58         1272  
37 58     58   306 use boolean;
  58         138  
  58         430  
38              
39 58     58   3938 use namespace::clean;
  58         144  
  58         315  
40              
41             has filter => (
42             is => 'ro',
43             required => 1,
44             isa => Document,
45             );
46              
47             has update => (
48             is => 'ro',
49             required => 1,
50             );
51              
52             has is_replace => (
53             is => 'ro',
54             required => 1,
55             isa => Boolish,
56             );
57              
58             has multi => (
59             is => 'ro',
60             required => 1,
61             isa => Boolish,
62             );
63              
64             has upsert => (
65             is => 'ro',
66             );
67              
68             has collation => (
69             is => 'ro',
70             isa => Maybe( [Document] ),
71             );
72              
73             has arrayFilters => (
74             is => 'ro',
75             isa => Maybe( [ArrayRef[Document]] ),
76             );
77              
78             with $_ for qw(
79             MongoDB::Role::_PrivateConstructor
80             MongoDB::Role::_CollectionOp
81             MongoDB::Role::_SingleBatchDocWrite
82             MongoDB::Role::_UpdatePreEncoder
83             MongoDB::Role::_BypassValidation
84             );
85              
86             # cached
87             my ($true, $false) = (true, false);
88              
89             sub execute {
90 0     0 0   my ( $self, $link ) = @_;
91              
92 0 0         if ( defined $self->collation ) {
93 0 0         MongoDB::UsageError->throw(
94             "MongoDB host '" . $link->address . "' doesn't support collation" )
95             if !$link->supports_collation;
96 0 0         MongoDB::UsageError->throw(
97             "Unacknowledged updates that specify a collation are not allowed")
98             if ! $self->_should_use_acknowledged_write;
99             }
100              
101 0 0         if ( defined $self->arrayFilters ) {
102 0 0         MongoDB::UsageError->throw(
103             "MongoDB host '" . $link->address . "' doesn't support arrayFilters" )
104             if !$link->supports_arrayFilters;
105 0 0         MongoDB::UsageError->throw(
106             "Unacknowledged updates that specify arrayFilters are not allowed")
107             if ! $self->_should_use_acknowledged_write;
108             }
109              
110             my $orig_op = {
111             q => (
112             ref( $self->filter ) eq 'ARRAY'
113 0 0         ? { @{ $self->filter } }
  0 0          
    0          
    0          
    0          
114             : $self->filter
115             ),
116             u => $self->_pre_encode_update( $link->max_bson_object_size,
117             $self->update, $self->is_replace ),
118             multi => $self->multi ? $true : $false,
119             upsert => $self->upsert ? $true : $false,
120             ( defined $self->collation ? ( collation => $self->collation ) : () ),
121             ( defined $self->arrayFilters ? ( arrayFilters => $self->arrayFilters ) : () ),
122             };
123              
124             return $self->_send_legacy_op_noreply(
125             $link,
126             MongoDB::_Protocol::write_update(
127             $self->full_name,
128             $self->bson_codec->encode_one( $orig_op->{q}, { invalid_chars => '' } ),
129             $self->_pre_encode_update( $link->max_bson_object_size,
130             $orig_op->{u}, $self->is_replace )->{bson},
131             {
132             upsert => $orig_op->{upsert},
133             multi => $orig_op->{multi},
134             },
135 0 0         ),
136             $orig_op,
137             "MongoDB::UpdateResult",
138             "update",
139             ) if ! $self->_should_use_acknowledged_write;
140              
141             return $self->_send_write_command(
142             $link,
143             $self->_maybe_bypass(
144             $link->supports_document_validation,
145             [
146             update => $self->coll_name,
147             updates => [
148             {
149             %$orig_op,
150             u => $self->_pre_encode_update(
151             $link->max_bson_object_size,
152             $orig_op->{u}, $self->is_replace
153             ),
154             }
155             ],
156 0 0         @{ $self->write_concern->as_args },
  0            
157             ],
158             ),
159             $orig_op,
160             "MongoDB::UpdateResult"
161             )->assert
162             if $link->supports_write_commands;
163              
164             return $self->_send_legacy_op_with_gle(
165             $link,
166             MongoDB::_Protocol::write_update(
167             $self->full_name,
168             $self->bson_codec->encode_one( $orig_op->{q}, { invalid_chars => '' } ),
169             $self->_pre_encode_update( $link->max_bson_object_size,
170             $orig_op->{u}, $self->is_replace )->{bson},
171             {
172             upsert => $orig_op->{upsert},
173             multi => $orig_op->{multi},
174             },
175 0           ),
176             $orig_op,
177             "MongoDB::UpdateResult",
178             "update",
179             )->assert;
180             }
181              
182             sub _parse_cmd {
183 0     0     my ( $self, $res ) = @_;
184              
185             return (
186 0 0         matched_count => ($res->{n} || 0) - @{ $res->{upserted} || [] },
187             modified_count => $res->{nModified},
188             upserted_id => $res->{upserted} ? $res->{upserted}[0]{_id} : undef,
189 0 0 0       );
190             }
191              
192             sub _parse_gle {
193 0     0     my ( $self, $res, $orig_doc ) = @_;
194              
195             # For 2.4 and earlier, 'upserted' has _id only if the _id is an OID.
196             # Otherwise, we have to pick it out of the update document or query
197             # document when we see updateExisting is false but the number of docs
198             # affected is 1
199              
200 0           my $upserted = $res->{upserted};
201 0 0 0       if (! defined( $upserted )
      0        
      0        
202             && exists( $res->{updatedExisting} )
203             && !$res->{updatedExisting}
204             && $res->{n} == 1 )
205             {
206 0           $upserted = $self->_find_id( $orig_doc->{u} );
207 0 0         $upserted = $self->_find_id( $orig_doc->{q} ) unless defined $upserted;
208             }
209              
210             return (
211 0 0 0       matched_count => ($upserted ? 0 : $res->{n} || 0),
212             modified_count => undef,
213             upserted_id => $upserted,
214             );
215             }
216              
217             sub _find_id {
218 0     0     my ($self, $doc) = @_;
219 0 0         if (ref($doc) eq "BSON::Raw") {
220 0           $doc = $self->bson_codec->decode_one($doc);
221             }
222 0           my $type = ref($doc);
223             return (
224             $type eq 'HASH' ? $doc->{_id}
225             : $type eq 'ARRAY' ? do {
226 0           my $i;
227 0 0         for ( $i = 0; $i < @$doc; $i++ ) { last if $doc->[$i] eq '_id' }
  0            
228 0 0         $i < $#$doc ? $doc->[ $i + 1 ] : undef;
229             }
230             : $type eq 'Tie::IxHash' ? $doc->FETCH('_id')
231             : $doc->{_id} # hashlike?
232 0 0         );
    0          
    0          
233             }
234              
235             1;