File Coverage

blib/lib/Mango/Collection.pm
Criterion Covered Total %
statement 18 145 12.4
branch 0 68 0.0
condition 0 31 0.0
subroutine 6 41 14.6
pod 20 20 100.0
total 44 305 14.4


line stmt bran cond sub pod time code
1             package Mango::Collection;
2 9     9   33 use Mojo::Base -base;
  9         10  
  9         52  
3              
4 9     9   960 use Carp 'croak';
  9         24  
  9         369  
5 9     9   30 use Mango::BSON qw(bson_code bson_doc bson_oid);
  9         10  
  9         325  
6 9     9   2732 use Mango::Bulk;
  9         20  
  9         66  
7 9     9   3464 use Mango::Cursor;
  9         18  
  9         56  
8 9     9   3704 use Mango::Cursor::Query;
  9         18  
  9         60  
9              
10             has [qw(db name)];
11              
12             sub aggregate {
13 0     0 1   my ($self, $pipeline) = (shift, shift);
14 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
15              
16             my $command = bson_doc(aggregate => $self->name, pipeline => $pipeline,
17 0   0       %{shift // {}});
  0            
18 0 0 0       $command->{cursor} //= {} unless $command->{explain};
19              
20             # Blocking
21 0 0         return $self->_aggregate($command, $self->db->command($command)) unless $cb;
22              
23             # Non-blocking
24             return $self->db->command($command,
25 0     0     sub { shift; $self->$cb(shift, $self->_aggregate($command, shift)) });
  0            
  0            
26             }
27              
28 0     0 1   sub build_index_name { join '_', keys %{$_[1]} }
  0            
29              
30 0     0 1   sub bulk { Mango::Bulk->new(collection => shift) }
31              
32             sub create {
33 0     0 1   my $self = shift;
34 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
35 0   0       return $self->_command(bson_doc(create => $self->name, %{shift // {}}), $cb);
  0            
36             }
37              
38 0     0 1   sub drop { $_[0]->_command(bson_doc(drop => $_[0]->name), $_[1]) }
39              
40             sub drop_index {
41 0     0 1   my ($self, $name) = (shift, shift);
42 0           return $self->_command(bson_doc(dropIndexes => $self->name, index => $name),
43             shift);
44             }
45              
46             sub ensure_index {
47 0     0 1   my ($self, $spec) = (shift, shift);
48 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
49 0   0       my $doc = shift // {};
50              
51 0   0       $doc->{name} //= $self->build_index_name($spec);
52 0           $doc->{key} = $spec;
53              
54             # Non-blocking
55 0           my $command = bson_doc createIndexes => $self->name, indexes => [$doc];
56 0     0     return $self->db->command($command => sub { shift; $self->$cb(shift) })
  0            
57 0 0         if $cb;
58              
59             # Blocking
60 0           $self->db->command($command);
61             }
62              
63             sub find {
64 0   0 0 1   Mango::Cursor::Query->new(
      0        
65             collection => shift,
66             query => shift // {},
67             fields => shift // {}
68             );
69             }
70              
71             sub find_and_modify {
72 0     0 1   my ($self, $opts) = (shift, shift);
73             return $self->_command(bson_doc(findAndModify => $self->name, %$opts),
74 0     0     shift, sub { shift->{value} });
  0            
75             }
76              
77             sub find_one {
78 0     0 1   my ($self, $query) = (shift, shift);
79 0 0         $query = {_id => $query} if ref $query eq 'Mango::BSON::ObjectID';
80 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
81              
82             # Non-blocking
83 0           my $cursor = $self->find($query, @_)->limit(-1);
84 0 0   0     return $cursor->next(sub { shift; $self->$cb(@_) }) if $cb;
  0            
  0            
85              
86             # Blocking
87 0           return $cursor->next;
88             }
89              
90 0     0 1   sub full_name { join '.', $_[0]->db->name, $_[0]->name }
91              
92             sub index_information {
93 0     0 1   my $self = shift;
94 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
95              
96 0           my $cmd = bson_doc(listIndexes => $self->name, @_);
97              
98             $self->_command($cmd, $cb,
99             sub {
100 0 0   0     my $doc = shift or return bson_doc;
101 0           bson_doc map { delete $_->{ns}; (delete $_->{name}, $_) }
  0            
102 0           @{$doc->{cursor}->{firstBatch}};
  0            
103             }
104 0           );
105             }
106              
107             sub insert {
108 0     0 1   my ($self, $orig_docs, $cb) = @_;
109 0 0         $orig_docs = [$orig_docs] unless ref $orig_docs eq 'ARRAY';
110              
111             # Make a shallow copy of the documents and add an id if needed
112 0           my @docs = map { { %$_ } } @$orig_docs;
  0            
113 0   0       my @ids = map { $_->{_id} //= bson_oid } @docs;
  0            
114              
115 0           my $command = bson_doc
116             insert => $self->name,
117             documents => \@docs,
118             ordered => \1,
119             writeConcern => $self->db->build_write_concern;
120              
121 0 0   0     return $self->_command($command, $cb, sub { @ids > 1 ? \@ids : $ids[0] });
  0            
122             }
123              
124             sub map_reduce {
125 0     0 1   my ($self, $map, $reduce) = (shift, shift, shift);
126 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
127             my $command = bson_doc
128             mapreduce => $self->name,
129             map => ref $map ? $map : bson_code($map),
130             reduce => ref $reduce ? $reduce : bson_code($reduce),
131 0 0 0       %{shift // {}};
  0 0          
132              
133             # Blocking
134 0 0         return $self->_map_reduce($self->db->command($command)) unless $cb;
135              
136             # Non-blocking
137             return $self->db->command(
138 0     0     $command => sub { shift; $self->$cb(shift, $self->_map_reduce(shift)) });
  0            
  0            
139             }
140              
141             sub options {
142 0     0 1   my ($self, $cb) = @_;
143              
144 0           my $cmd = bson_doc(listCollections => 1, filter => { name => $self->name });
145 0     0     $self->_command($cmd, $cb, sub { shift->{cursor}->{firstBatch}->[0] });
  0            
146             }
147              
148             sub remove {
149 0     0 1   my $self = shift;
150 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
151 0   0       my $query = shift // {};
152 0   0       my $flags = shift // {};
153              
154 0 0         ($query, $flags) = ({_id => $query}, {single => 1})
155             if ref $query eq 'Mango::BSON::ObjectID';
156             my $command = bson_doc
157             delete => $self->name,
158 0 0         deletes => [{q => $query, limit => $flags->{single} ? 1 : 0}],
159             ordered => \1,
160             writeConcern => $self->db->build_write_concern;
161              
162 0           return $self->_command($command, $cb);
163             }
164              
165             sub rename {
166 0     0 1   my ($self, $name, $cb) = @_;
167              
168 0           my $admin = $self->db->mango->db('admin');
169 0           my $dbname = $self->db->name;
170 0           my $oldname = join '.', $dbname, $self->name;
171 0           my $newname = join '.', $dbname, $name;
172              
173 0           my $cmd = bson_doc renameCollection => $oldname, to => $newname;
174              
175             # Non-blocking
176             return $admin->command($cmd, sub {
177 0     0     my ($admin_db, $err, $doc) = @_;
178 0 0         my $newcol = $doc->{ok} ? $self->db->collection($name) : undef;
179 0           return $cb->($self, $err, $newcol);
180 0 0         }) if $cb;
181              
182             # Blocking
183 0           my $doc = $admin->command($cmd);
184 0 0         return $doc->{ok} ? $self->db->collection($name) : undef;
185             }
186              
187             sub save {
188 0     0 1   my ($self, $doc, $cb) = @_;
189              
190             # New document
191 0 0         return $self->insert($doc, $cb) unless $doc->{_id};
192              
193             # Update non-blocking
194 0           my @update = ({_id => $doc->{_id}}, $doc, {upsert => 1});
195 0     0     return $self->update(@update => sub { shift->$cb(shift, $doc->{_id}) })
196 0 0         if $cb;
197              
198             # Update blocking
199 0           $self->update(@update);
200 0           return $doc->{_id};
201             }
202              
203 0     0 1   sub stats { $_[0]->_command(bson_doc(collstats => $_[0]->name), $_[1]) }
204              
205             sub update {
206 0     0 1   my ($self, $query, $update) = (shift, shift, shift);
207 0 0         my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
208 0   0       my $flags = shift // {};
209              
210             $update = {
211             q => ref $query eq 'Mango::BSON::ObjectID' ? {_id => $query} : $query,
212             u => $update,
213             upsert => $flags->{upsert} ? \1 : \0,
214 0 0         multi => $flags->{multi} ? \1 : \0
    0          
    0          
215             };
216 0           my $command = bson_doc
217             update => $self->name,
218             updates => [$update],
219             ordered => \1,
220             writeConcern => $self->db->build_write_concern;
221              
222 0           return $self->_command($command, $cb);
223             }
224              
225             sub _aggregate {
226 0     0     my ($self, $command, $doc) = @_;
227              
228             # Document (explain)
229 0 0         return $doc if $command->{explain};
230              
231             # Collection
232 0           my $out = $command->{pipeline}[-1]{'$out'};
233 0 0         return $self->db->collection($out) if defined $out;
234              
235             # Cursor
236 0           my $cursor = $doc->{cursor};
237             return Mango::Cursor->new(collection => $self, id => $cursor->{id})
238 0           ->add_batch($cursor->{firstBatch});
239             }
240              
241             sub _command {
242 0     0     my ($self, $command, $cb, $return) = @_;
243 0   0 0     $return ||= sub {shift};
  0            
244              
245             # Non-blocking
246 0           my $db = $self->db;
247 0           my $protocol = $db->mango->protocol;
248             return $db->command(
249             $command => sub {
250 0     0     my ($db, $err, $doc) = @_;
251 0   0       $err ||= $protocol->write_error($doc);
252 0           $self->$cb($err, $return->($doc));
253             }
254 0 0         ) if $cb;
255              
256             # Blocking
257 0           my $doc = $db->command($command);
258 0 0         if (my $err = $protocol->write_error($doc)) { croak $err }
  0            
259 0           return $return->($doc);
260             }
261              
262             sub _map_reduce {
263 0     0     my ($self, $doc) = @_;
264 0 0         return $doc->{results} unless $doc->{result};
265 0           return $self->db->collection($doc->{result});
266             }
267              
268             1;
269              
270             =encoding utf8
271              
272             =head1 NAME
273              
274             Mango::Collection - MongoDB collection
275              
276             =head1 SYNOPSIS
277              
278             use Mango::Collection;
279              
280             my $collection = Mango::Collection->new(db => $db);
281             my $cursor = $collection->find({foo => 'bar'});
282              
283             =head1 DESCRIPTION
284              
285             L is a container for MongoDB collections used by
286             L.
287              
288             =head1 ATTRIBUTES
289              
290             L implements the following attributes.
291              
292             =head2 db
293              
294             my $db = $collection->db;
295             $collection = $collection->db(Mango::Database->new);
296              
297             L object this collection belongs to.
298              
299             =head2 name
300              
301             my $name = $collection->name;
302             $collection = $collection->name('bar');
303              
304             Name of this collection.
305              
306             =head1 METHODS
307              
308             L inherits all methods from L and implements
309             the following new ones.
310              
311             =head2 aggregate
312              
313             my $cursor = $collection->aggregate(
314             [{'$group' => {_id => undef, total => {'$sum' => '$foo'}}}]);
315             my $collection = $collection->aggregate(
316             [{'$match' => {'$gt' => 23}}, {'$out' => 'some_collection'}]);
317             my $doc = $collection->aggregate(
318             [{'$match' => {'$gt' => 23}}], {explain => bson_true});
319              
320             Aggregate collection with aggregation framework, additional options will be
321             passed along to the server verbatim. You can also append a callback to perform
322             operation non-blocking.
323              
324             my $pipeline = [{'$group' => {_id => undef, total => {'$sum' => '$foo'}}}];
325             $collection->aggregate($pipeline => sub {
326             my ($collection, $err, $cursor) = @_;
327             ...
328             });
329             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
330              
331             =head2 build_index_name
332              
333             my $name = $collection->build_index_name(bson_doc(foo => 1, bar => -1));
334             my $name = $collection->build_index_name({foo => 1});
335              
336             Build name for index specification, the order of keys matters for compound
337             indexes.
338              
339             =head2 bulk
340              
341             my $bulk = $collection->bulk;
342              
343             Build L object.
344              
345             my $bulk = $collection->bulk;
346             $bulk->insert({foo => $_}) for 1 .. 10;
347             $bulk->find({foo => 4})->update_one({'$set' => {bar => 'baz'}});
348             $bulk->find({foo => 7})->remove_one;
349             my $results = $bulk->execute;
350              
351             =head2 create
352              
353             $collection->create;
354             $collection->create({capped => bson_true, max => 5, size => 10000});
355              
356             Create collection. You can also append a callback to perform operation
357             non-blocking.
358              
359             $collection->create({capped => bson_true, max => 5, size => 10000} => sub {
360             my ($collection, $err) = @_;
361             ...
362             });
363             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
364              
365             =head2 drop
366              
367             $collection->drop;
368              
369             Drop collection. You can also append a callback to perform operation
370             non-blocking.
371              
372             $collection->drop(sub {
373             my ($collection, $err) = @_;
374             ...
375             });
376             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
377              
378             =head2 drop_index
379              
380             $collection->drop_index('foo');
381              
382             Drop index. You can also append a callback to perform operation non-blocking.
383              
384             $collection->drop_index(foo => sub {
385             my ($collection, $err) = @_;
386             ...
387             });
388             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
389              
390             =head2 ensure_index
391              
392             $collection->ensure_index(bson_doc(foo => 1, bar => -1));
393             $collection->ensure_index({foo => 1});
394             $collection->ensure_index({foo => 1}, {unique => bson_true});
395              
396             Make sure an index exists, the order of keys matters for compound indexes,
397             additional options will be passed along to the server verbatim. You can also
398             append a callback to perform operation non-blocking.
399              
400             $collection->ensure_index(({foo => 1}, {unique => bson_true}) => sub {
401             my ($collection, $err) = @_;
402             ...
403             });
404             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
405              
406             =head2 find
407              
408             my $cursor = $collection->find;
409             my $cursor = $collection->find({foo => 'bar'});
410             my $cursor = $collection->find({foo => 'bar'}, {foo => 1});
411              
412             Build L object for query.
413              
414             # Exclude "_id" field from results
415             my $docs = $collection->find({foo => 'bar'}, {_id => 0})->all;
416              
417             =head2 find_and_modify
418              
419             my $doc = $collection->find_and_modify(
420             {query => {foo => 'bar'}, update => {'$set' => {foo => 'baz'}}});
421              
422             Fetch and update or remove a document atomically. You can also append a callback
423             to perform operation non-blocking.
424              
425             my $opts = {query => {foo => 'bar'}, update => {'$set' => {foo => 'baz'}}};
426             $collection->find_and_modify($opts => sub {
427             my ($collection, $err, $doc) = @_;
428             ...
429             });
430             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
431              
432             By default this method returns the unmodified version of the document. To
433             change this behaviour, add the option C 1>.
434              
435             =head2 find_one
436              
437             my $doc = $collection->find_one({foo => 'bar'});
438             my $doc = $collection->find_one({foo => 'bar'}, {foo => 1});
439             my $doc = $collection->find_one($oid, {foo => 1});
440              
441             Find one document. You can also append a callback to perform operation
442             non-blocking.
443              
444             $collection->find_one({foo => 'bar'} => sub {
445             my ($collection, $err, $doc) = @_;
446             ...
447             });
448             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
449              
450             =head2 full_name
451              
452             my $name = $collection->full_name;
453              
454             Full name of this collection.
455              
456             =head2 index_information
457              
458             my $info = $collection->index_information;
459             # return only the 5 first indexes
460             my $info = $collection->index_information(cursor => { batchSize => 5 });
461              
462             Get index information for collection. You can also append a callback to
463             perform operation non-blocking.
464              
465             $collection->index_information(sub {
466             my ($collection, $err, $info) = @_;
467             ...
468             });
469             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
470              
471             =head2 insert
472              
473             my $oid = $collection->insert({foo => 'bar'});
474             my $oids = $collection->insert([{foo => 'bar'}, {baz => 'yada'}]);
475              
476             Insert one or more documents into collection. You can also append a callback
477             to perform operation non-blocking.
478              
479             $collection->insert({foo => 'bar'} => sub {
480             my ($collection, $err, $oid) = @_;
481             ...
482             });
483             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
484              
485             Note that C has to ensure each document has an C<_id> before sending
486             them to MongoDB. To avoid modifying your data, it makes a copy of the
487             documents. This can be a bit slow if you are sending big objects like
488             pictures. To avoid that, consider using C instead.
489              
490             =head2 map_reduce
491              
492             my $collection = $collection->map_reduce($map, $reduce, {out => 'foo'});
493             my $docs = $collection->map_reduce($map, $reduce, {out => {inline => 1}});
494             my $docs = $collection->map_reduce(
495             bson_code($map), bson_code($reduce), {out => {inline => 1}});
496              
497             Perform map/reduce operation on collection, additional options will be passed
498             along to the server verbatim. You can also append a callback to perform
499             operation non-blocking.
500              
501             $collection->map_reduce(($map, $reduce, {out => {inline => 1}}) => sub {
502             my ($collection, $err, $docs) = @_;
503             ...
504             }
505             );
506             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
507              
508             =head2 options
509              
510             my $doc = $collection->options;
511              
512             Get options for collection. You can also append a callback to perform
513             operation non-blocking.
514              
515             $collection->options(sub {
516             my ($collection, $err, $doc) = @_;
517             ...
518             });
519             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
520              
521             =head2 remove
522              
523             my $result = $collection->remove;
524             my $result = $collection->remove($oid);
525             my $result = $collection->remove({foo => 'bar'});
526             my $result = $collection->remove({foo => 'bar'}, {single => 1});
527              
528             Remove documents from collection. You can also append a callback to perform
529             operation non-blocking. Returns a WriteResult document.
530              
531             $collection->remove(({foo => 'bar'}, {single => 1}) => sub {
532             my ($collection, $err, $doc) = @_;
533             ...
534             });
535             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
536              
537             These options are currently available:
538              
539             =over 2
540              
541             =item single
542              
543             single => 1
544              
545             Remove only one document.
546              
547             =back
548              
549             =head2 rename
550              
551             my $new_collection = $collection->rename('NewName');
552              
553             Rename a collection, keeping all of its original contents and options. Returns
554             a new Mango::Collection object pointing to the renamed collection. You can
555             also append a callback to perform operation non-blocking.
556              
557             $collection->rename('NewName' => sub {
558             my ($collection, $err, $oid) = @_;
559             ...
560             });
561             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
562              
563             =head2 save
564              
565             my $oid = $collection->save({foo => 'bar'});
566              
567             Save document to collection. The document MUST have an C<_id>. You can also
568             append a callback to perform operation non-blocking.
569              
570             $collection->save({foo => 'bar'} => sub {
571             my ($collection, $err, $oid) = @_;
572             ...
573             });
574             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
575              
576             =head2 stats
577              
578             my $stats = $collection->stats;
579              
580             Get collection statistics. You can also append a callback to perform operation
581             non-blocking.
582              
583             $collection->stats(sub {
584             my ($collection, $err, $stats) = @_;
585             ...
586             });
587             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
588              
589             =head2 update
590              
591             my $result = $collection->update($oid, {foo => 'baz'});
592             my $result = $collection->update({foo => 'bar'}, {foo => 'baz'});
593             my $result = $collection->update({foo => 'bar'}, {foo => 'baz'}, {multi => 1});
594              
595             Update document in collection. You can also append a callback to perform
596             operation non-blocking. Returns a WriteResult document.
597              
598             $collection->update(({foo => 'bar'}, {foo => 'baz'}, {multi => 1}) => sub {
599             my ($collection, $err, $doc) = @_;
600             ...
601             });
602             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
603              
604             These options are currently available:
605              
606             =over 2
607              
608             =item multi
609              
610             multi => 1
611              
612             Update more than one document.
613              
614             =item upsert
615              
616             upsert => 1
617              
618             Insert document if none could be updated.
619              
620             =back
621              
622             =head1 SEE ALSO
623              
624             L, L, L.
625              
626             =cut