File Coverage

blib/lib/MongoDB/QueryResult.pm
Criterion Covered Total %
statement 33 98 33.6
branch 0 16 0.0
condition 0 9 0.0
subroutine 11 26 42.3
pod 4 5 80.0
total 48 154 31.1


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   399 use strict;
  58         142  
  58         1684  
16 58     58   317 use warnings;
  58         143  
  58         1945  
17             package MongoDB::QueryResult;
18              
19             # ABSTRACT: An iterator for Mongo query results
20              
21 58     58   321 use version;
  58         133  
  58         338  
22             our $VERSION = 'v2.2.0';
23              
24 58     58   4710 use Moo;
  58         147  
  58         355  
25 58     58   18057 use MongoDB::Error;
  58         189  
  58         5868  
26 58     58   443 use MongoDB::_Constants;
  58         174  
  58         6731  
27 58     58   24853 use MongoDB::Op::_GetMore;
  58         198  
  58         2618  
28 58     58   25656 use MongoDB::Op::_KillCursors;
  58         180  
  58         2449  
29 58         454 use MongoDB::_Types qw(
30             BSONCodec
31             ClientSession
32             HostAddress
33             Intish
34             Numish
35             Stringish
36 58     58   430 );
  58         115  
37 58         269 use Types::Standard qw(
38             Maybe
39             ArrayRef
40             Any
41             InstanceOf
42             HashRef
43             Overload
44 58     58   95298 );
  58         134  
45 58     58   72722 use namespace::clean;
  58         147  
  58         297  
46              
47             with $_ for qw(
48             MongoDB::Role::_PrivateConstructor
49             MongoDB::Role::_CursorAPI
50             );
51              
52             # attributes needed for get more
53              
54             has _client => (
55             is => 'rw',
56             required => 1,
57             isa => InstanceOf['MongoDB::MongoClient'],
58             );
59              
60             has _address => (
61             is => 'ro',
62             required => 1,
63             isa => HostAddress,
64             );
65              
66             has _full_name => (
67             is => 'ro',
68             required => 1,
69             isa => Stringish
70             );
71              
72             has _bson_codec => (
73             is => 'ro',
74             required => 1,
75             isa => BSONCodec,
76             );
77              
78             has _batch_size => (
79             is => 'ro',
80             required => 1,
81             isa => Intish,
82             );
83              
84             has _max_time_ms => (
85             is => 'ro',
86             isa => Numish,
87             );
88              
89             has _session => (
90             is => 'rwp',
91             isa => Maybe[ClientSession],
92             );
93              
94             # attributes for tracking progress
95              
96             has _cursor_at => (
97             is => 'ro',
98             required => 1,
99             isa => Numish,
100             );
101              
102 0     0     sub _inc_cursor_at { $_[0]{_cursor_at}++ }
103              
104             has _limit => (
105             is => 'ro',
106             required => 1,
107             isa => Numish,
108             );
109              
110             # attributes from actual results
111              
112             # integer or MongoDB::_CursorID or Math::BigInt
113             has _cursor_id => (
114             is => 'ro',
115             required => 1,
116             writer => '_set_cursor_id',
117             isa => Any,
118             );
119              
120             has _post_batch_resume_token => (
121             is => 'ro',
122             required => 0,
123             writer => '_set_post_batch_resume_token',
124             isa => Any,
125             );
126              
127             has _cursor_start => (
128             is => 'ro',
129             required => 1,
130             writer => '_set_cursor_start',
131             isa => Numish,
132             );
133              
134             has _cursor_flags => (
135             is => 'ro',
136             required => 1,
137             writer => '_set_cursor_flags',
138             isa => HashRef,
139             );
140              
141             has _cursor_num => (
142             is => 'ro',
143             required => 1,
144             isa => Numish,
145             );
146              
147 0     0     sub _inc_cursor_num { $_[0]{_cursor_num} += $_[1] }
148              
149             has _docs => (
150             is => 'ro',
151             required => 1,
152             isa => ArrayRef,
153             );
154              
155 0     0     sub _drained { ! @{$_[0]{_docs}} }
  0            
156 0     0     sub _doc_count { scalar @{$_[0]{_docs}} }
  0            
157             sub _add_docs {
158 0     0     my $self = shift;
159 0           push @{$self->{_docs}}, @_;
  0            
160             }
161             sub _next_doc {
162 0     0     my $self = shift;
163 0           my $doc = shift @{$self->{_docs}};
  0            
164 0 0         if (my $resume_token = $self->_post_batch_resume_token) {
165 0           $doc->{postBatchResumeToken} = $resume_token;
166             }
167 0           return $doc;
168             }
169             sub _drain_docs {
170 0     0     my @docs = @{$_[0]{_docs}};
  0            
171 0           $_[0]{_cursor_at} += scalar @docs;
172 0           @{$_[0]{_docs}} = ();
  0            
173 0           return @docs;
174             }
175              
176             # for backwards compatibility
177             sub started_iterating() { 1 }
178              
179             sub _info {
180 0     0     my ($self) = @_;
181             return {
182 0           flag => $self->_cursor_flags,
183             cursor_id => $self->_cursor_id,
184             start => $self->_cursor_start,
185             at => $self->_cursor_at,
186             num => $self->_cursor_num,
187             };
188             }
189              
190             #pod =method has_next
191             #pod
192             #pod if ( $response->has_next ) {
193             #pod ...
194             #pod }
195             #pod
196             #pod Returns true if additional documents are available. This will
197             #pod attempt to get another batch of documents from the server if
198             #pod necessary.
199             #pod
200             #pod =cut
201              
202             sub has_next {
203 0     0 1   my ($self) = @_;
204 0           my $limit = $self->_limit;
205 0 0 0       if ( $limit > 0 && ( $self->_cursor_at + 1 ) > $limit ) {
206 0           $self->_kill_cursor;
207 0           return 0;
208             }
209 0   0       return !$self->_drained || $self->_get_more;
210             }
211              
212             #pod =method next
213             #pod
214             #pod while ( $doc = $result->next ) {
215             #pod process_doc($doc)
216             #pod }
217             #pod
218             #pod Returns the next document or C if the server cursor is exhausted.
219             #pod
220             #pod =cut
221              
222             sub next {
223 0     0 1   my ($self) = @_;
224 0 0         return unless $self->has_next;
225 0           $self->_inc_cursor_at();
226 0           return $self->_next_doc;
227             }
228              
229             #pod =method batch
230             #pod
231             #pod while ( @batch = $result->batch ) {
232             #pod for $doc ( @batch ) {
233             #pod process_doc($doc);
234             #pod }
235             #pod }
236             #pod
237             #pod Returns the next batch of documents or an empty list if the server cursor is exhausted.
238             #pod
239             #pod =cut
240              
241             sub batch {
242 0     0 1   my ($self) = @_;
243 0 0         return unless $self->has_next;
244 0           return $self->_drain_docs;
245             }
246              
247             sub _get_more {
248 0     0     my ($self) = @_;
249 0 0         return 0 if $self->_cursor_id == 0;
250              
251 0           my $limit = $self->_limit;
252 0 0         my $want = $limit > 0 ? ( $limit - $self->_cursor_at ) : $self->_batch_size;
253              
254 0           my ($db_name, $coll_name) = split(/\./, $self->_full_name, 2);
255              
256 0 0         my $op = MongoDB::Op::_GetMore->_new(
257             full_name => $self->_full_name,
258             db_name => $db_name,
259             coll_name => $coll_name,
260             client => $self->_client,
261             bson_codec => $self->_bson_codec,
262             cursor_id => $self->_cursor_id,
263             batch_size => $want,
264             ( $self->_max_time_ms ? ( max_time_ms => $self->_max_time_ms ) : () ),
265             session => $self->_session,
266             monitoring_callback => $self->_client->monitoring_callback,
267             );
268              
269 0           my $result = $self->_client->send_direct_op( $op, $self->_address );
270              
271 0           $self->_set_cursor_id( $result->{cursor_id} );
272 0           $self->_set_cursor_flags( $result->{flags} );
273 0           $self->_set_cursor_start( $result->{starting_from} );
274 0           $self->_inc_cursor_num( $result->{number_returned} );
275 0           $self->_add_docs( @{ $result->{docs} } );
  0            
276 0           $self->_set_post_batch_resume_token($result->{cursor}{postBatchResumeToken});
277 0           return scalar @{ $result->{docs} };
  0            
278             }
279              
280             #pod =method all
281             #pod
282             #pod @docs = $result->all;
283             #pod
284             #pod Returns all documents as a list.
285             #pod
286             #pod =cut
287              
288             sub all {
289 0     0 1   my ($self) = @_;
290 0           my @ret;
291 0           push @ret, $self->_drain_docs while $self->has_next;
292 0           return @ret;
293             }
294              
295             sub _kill_cursor {
296 0     0     my ($self) = @_;
297 0           my $cursor_id = $self->_cursor_id;
298 0 0 0       return if !defined $cursor_id || $cursor_id == 0;
299              
300 0           my ($db_name, $coll_name) = split(/\./, $self->_full_name, 2);
301 0           my $op = MongoDB::Op::_KillCursors->_new(
302             db_name => $db_name,
303             coll_name => $coll_name,
304             full_name => $self->_full_name,
305             bson_codec => $self->_bson_codec,
306             cursor_ids => [$cursor_id],
307             client => $self->_client,
308             session => $self->_session,
309             monitoring_callback => $self->_client->monitoring_callback,
310             );
311 0           $self->_client->send_direct_op( $op, $self->_address );
312 0           $self->_set_cursor_id(0);
313             }
314              
315             sub DEMOLISH {
316 0     0 0   my ($self) = @_;
317 0           $self->_kill_cursor;
318             }
319              
320             #pod =head1 SYNOPSIS
321             #pod
322             #pod $cursor = $coll->find( $filter );
323             #pod $result = $cursor->result;
324             #pod
325             #pod while ( $doc = $result->next ) {
326             #pod process_doc($doc)
327             #pod }
328             #pod
329             #pod =head1 DESCRIPTION
330             #pod
331             #pod This class defines an iterator against a query result. It automatically
332             #pod fetches additional results from the originating mongod/mongos server
333             #pod on demand.
334             #pod
335             #pod For backwards compatibility reasons, L encapsulates query
336             #pod parameters and generates a C object on demand. All
337             #pod iterators on C delegate to C object.
338             #pod
339             #pod Retrieving this object and iterating on it directly will be slightly
340             #pod more efficient.
341             #pod
342             #pod =head1 USAGE
343             #pod
344             #pod =head2 Error handling
345             #pod
346             #pod Unless otherwise explicitly documented, all methods throw exceptions if
347             #pod an error occurs. The error types are documented in L.
348             #pod
349             #pod To catch and handle errors, the L and L modules
350             #pod are recommended:
351             #pod
352             #pod =head2 Cursor destruction
353             #pod
354             #pod When a C object is destroyed, a cursor termination
355             #pod request will be sent to the originating server to free server resources.
356             #pod
357             #pod =head2 Multithreading
358             #pod
359             #pod B: Per L documentation, use of Perl threads is discouraged by the
360             #pod maintainers of Perl and the MongoDB Perl driver does not test or provide support
361             #pod for use with threads.
362             #pod
363             #pod Iterators are cloned in threads, but not reset. Iterating from multiple
364             #pod threads will give unpredictable results. Only iterate from a single
365             #pod thread.
366             #pod
367             #pod =cut
368              
369             1;
370              
371             __END__