File Coverage

blib/lib/MorboDB/Collection.pm
Criterion Covered Total %
statement 88 118 74.5
branch 24 50 48.0
condition 46 103 44.6
subroutine 18 26 69.2
pod 17 17 100.0
total 193 314 61.4


line stmt bran cond sub pod time code
1             package MorboDB::Collection;
2              
3             # ABSTRACT: A MorboDB collection
4              
5 4     4   22 use Moo;
  4         7  
  4         24  
6 4     4   4110 use boolean;
  4         18279  
  4         25  
7 4     4   331 use Carp;
  4         40  
  4         258  
8 4     4   3207 use Clone qw/clone/;
  4         17450  
  4         322  
9 4     4   8814 use MorboDB::Cursor;
  4         5373  
  4         134  
10 4     4   2082 use MorboDB::OID;
  4         86  
  4         142  
11 4     4   28 use MQUL 0.003 qw/update_doc/;
  4         115  
  4         376  
12 4     4   87 use Scalar::Util qw/blessed/;
  4         7  
  4         7738  
13              
14             our $VERSION = "1.000000";
15             $VERSION = eval $VERSION;
16              
17             =head1 NAME
18              
19             MorboDB::Collection - A MorboDB collection
20              
21             =head1 VERSION
22              
23             version 1.000000
24              
25             =head1 SYNOPSIS
26              
27             my $coll = $db->get_collection('users');
28            
29             my $id = $coll->insert({
30             username => 'someguy98',
31             password => 's3cr3t',
32             email => 'email at address dot com',
33             });
34              
35             my $cursor = $coll->find({ email => qr/\@address\.com$/ })->sort({ username => 1 });
36             # use cursor according to MorboDB::Cursor
37              
38             =head1 DESCRIPTION
39              
40             This module provides the API for handling collections in a L.
41              
42             =head1 ATTRIBUTES
43              
44             =head2 name
45              
46             The name of the collection. String, required.
47              
48             =head2 full_name
49              
50             The full name of the collection, including the name of the database, joined
51             by dots. String, created automatically.
52              
53             =cut
54              
55             has 'name' => (is => 'ro', required => 1);
56              
57             has 'full_name' => (is => 'ro', lazy_build => 1);
58              
59             has '_database' => (is => 'ro', required => 1, weak_ref => 1);
60              
61             has '_data' => (is => 'ro', default => sub { {} }, clearer => '_clear_data');
62              
63             =head1 STATIC FUNCTIONS
64              
65             =head2 to_index_string( $keys )
66              
67             Receives a hash-reference, array-reference or L object and
68             converts into a query string.
69              
70             =cut
71              
72             sub to_index_string {
73             # this function is just stolen as-is from MongoDB::Collection
74 0     0 1 0 my $keys = shift;
75              
76 0         0 my @name;
77 0 0 0     0 if (ref $keys eq 'ARRAY' || ref $keys eq 'HASH') {
    0          
78 0         0 while ((my $idx, my $d) = each(%$keys)) {
79 0         0 push(@name, $idx);
80 0         0 push(@name, $d);
81             }
82             } elsif (ref $keys eq 'Tie::IxHash') {
83 0         0 my @ks = $keys->Keys;
84 0         0 my @vs = $keys->Values;
85 0         0 @vs = $keys->Values;
86              
87 0         0 for (my $i=0; $i<$keys->Length; $i++) {
88 0         0 push(@name, $ks[$i]);
89 0         0 push(@name, $vs[$i]);
90             }
91             } else {
92 0         0 confess 'expected Tie::IxHash, hash, or array reference for keys';
93             }
94              
95 0         0 return join('_', @name);
96             }
97              
98             =head1 OBJECT METHODS
99              
100             =head2 get_collection( $name )
101              
102             Returns a MorboDB::Collection for the collection called C<$name> within this collection.
103              
104             =cut
105              
106             sub get_collection {
107 2     2 1 6 my ($self, $name) = @_;
108              
109 2         21 return $self->_database->get_collection($self->name.'.'.$name);
110             }
111              
112             =head2 find( [ $query ] )
113              
114             Executes the given query and returns a L object with the
115             results (if query is not provided, all documents in the collection will
116             match). C<$query> can be a hash reference, a L object, or
117             array reference (with an even number of elements).
118              
119             The set of fields returned can be limited through the use of the
120             C<< MorboDB::Cursor->fields() >> method on the resulting cursor object.
121             Other commonly used cursor methods are C, C, and C.
122              
123             As opposed to C<< MongoDB::Collection->find() >>, this method doesn't take a hash-ref
124             of options such as C and C, use the appropriate methods on
125             the cursor instead (this is also deprecated in MongoDB anyway).
126              
127             Note that currently, providing a C object or array reference
128             will have no special effect, as the query will be converted into a hash
129             reference. This may or may not change in future version.
130              
131             For a complete reference on querying in MorboDB, please look at L.
132              
133             =cut
134              
135             sub find {
136 33     33 1 8821 my ($self, $query) = @_;
137              
138 33 0 66     182 confess "query must be a hash reference, even-numbered array reference or Tie::IxHash object."
      33        
      0        
      33        
139             if $query && ref $query ne 'HASH' &&
140             ref $query ne 'Tie::IxHash' &&
141             (ref $query ne 'ARRAY' ||
142             (ref $query eq 'ARRAY' && scalar @$query % 2 != 0)
143             );
144              
145             # turn array queries into Tie::IxHash objects
146 33 50 66     137 if ($query && ref $query eq 'ARRAY') {
147 0         0 $query = Tie::IxHash->new(@$query);
148             }
149              
150             # turn Tie::IxHash objects into hash-refs
151 33 50 66     119 if ($query && ref $query eq 'Tie::IxHash') {
152 0         0 my %new_query = map { $_ => $query->FETCH($_) } $query->Keys;
  0         0  
153 0         0 $query = \%new_query;
154             }
155              
156 33   100     99 $query ||= {};
157              
158 33         874 return MorboDB::Cursor->new(_coll => $self, _query => $query);
159             }
160              
161             =head2 query( [ $query ] )
162              
163             Alias for C.
164              
165             =cut
166              
167 0     0 1 0 sub query { shift->find(@_) }
168              
169             =head2 find_one( [ $query ] )
170              
171             Executes the provided query and returns the first result found (if any,
172             otherwise C is returned).
173              
174             Internally, this is really a shortcut for running C<< $coll->find($query)->limit(1)->next() >>.
175              
176             =cut
177              
178 9     9 1 2349 sub find_one { shift->find(@_)->limit(1)->next }
179              
180             =head2 insert( $doc )
181              
182             Inserts the given document into the database and returns it's ID.
183             The document can be a hash reference, an even-numbered array reference
184             or a Tie::IxHash object. The ID is the _id value specified in the data
185             or a L object created automatically.
186              
187             Note that providing a Tie::IxHash object or array reference will not make
188             your document ordered, as documents are always saved as hash references,
189             so this has no benefit except compatibility with MongoDB.
190              
191             =cut
192              
193 4     4 1 1075 sub insert { ($_[0]->batch_insert([$_[1]]))[0] }
194              
195             =head2 batch_insert( \@docs )
196              
197             Inserts each of the documents in the array into the database and returns
198             an array of their _id attributes.
199              
200             =cut
201              
202             sub batch_insert {
203 7     7 1 69 my ($self, $docs) = @_;
204              
205 7 50 33     54 confess "batch_insert() expects an array reference of documents."
206             unless $docs && ref $docs eq 'ARRAY';
207              
208 7         17 foreach my $doc (@$docs) {
209 12 0 33     69 confess "data to insert must be a hash reference, even-numbered array reference or Tie::IxHash object."
      33        
210             unless $doc && (ref $doc eq 'HASH' || ref $doc eq 'Tie::IxHash' || (ref $doc eq 'ARRAY' && scalar @$doc % 2 == 0));
211              
212             # turn array documents into Tie::IxHash objects
213 12 50 33     61 if ($doc && ref $doc eq 'ARRAY') {
214 0         0 $doc = Tie::IxHash->new(@$doc);
215             }
216              
217             # turn Tie::IxHash objects into hash-refs
218 12 50 33     57 if ($doc && ref $doc eq 'Tie::IxHash') {
219 0         0 my %new_doc = map { $_ => $doc->FETCH($_) } $doc->Keys;
  0         0  
220 0         0 $doc = \%new_doc;
221             }
222              
223 12   66     142 $doc->{_id} ||= MorboDB::OID->new;
224              
225 12 100 66     682 my $oid = blessed $doc->{_id} && blessed $doc->{_id} eq 'MorboDB::OID' ?
226             $doc->{_id}->value : $doc->{_id};
227 12 50       61 confess "duplicate key error, ID $oid already exists in the collection."
228             if exists $self->_data->{$oid};
229             }
230              
231 7         19 return map { $self->save($_) } @$docs;
  12         30  
232             }
233              
234             =head2 update( $query, \%update, [ \%opts ] )
235              
236             Updates document(s) that match the provided query (which is the same as
237             what C accepts) according to the update (C<\%update>) hash-ref.
238              
239             Return a hash-ref of information about the update, including number of documents
240             updated (n).
241              
242             C can take a hash reference of options. The options currently supported are:
243              
244             =over
245              
246             =item * C - If no object matches the query, C<\%update> will be inserted
247             as a new document (possibly taking values from C<$query> too).
248              
249             =item * C - All of the documents that match the query will be updated,
250             not just the first document found.
251              
252             =back
253              
254             For a complete reference on update syntax and behavior, please look at
255             L.
256              
257             =cut
258              
259             sub update {
260 4     4 1 14 my ($self, $query, $update, $opts) = @_;
261              
262 4 0 33     42 confess "query must be a hash reference, even-numbered array reference or Tie::IxHash object."
      33        
      0        
      33        
263             if $query && ref $query ne 'HASH' &&
264             ref $query ne 'Tie::IxHash' &&
265             (ref $query ne 'ARRAY' ||
266             (ref $query eq 'ARRAY' && scalar @$query % 2 != 0)
267             );
268              
269 4   50     14 $query ||= {};
270              
271 4 50 33     30 confess "the update structure must be a hash reference."
272             unless $update && ref $update eq 'HASH';
273              
274 4 50 66     25 confess "the options structure must be a hash reference."
275             if $opts && ref $opts ne 'HASH';
276              
277 4   100     28 $opts ||= {};
278              
279 4         8 my @docs;
280 4 50       14 if ($opts->{multiple}) {
281 0         0 @docs = $self->find($query)->all;
282             } else {
283 4         18 my $doc = $self->find_one($query);
284 4 100       29 push(@docs, $doc) if $doc;
285             }
286              
287 4 100 66     27 if (scalar @docs == 0 && $opts->{upsert}) {
288             # take attributes from the query where appropriate
289 2         5 my $doc = {};
290 2         8 foreach (keys %$query) {
291 2 50       18 next if $_ eq '_id';
292 2 50       23 $doc->{$_} = $query->{$_}
293             if !ref $query->{$_};
294             }
295 2   33     48 $doc->{_id} ||= MorboDB::OID->new;
296 2         233 my $id = $self->save(update_doc($doc, $update));
297             return {
298 2         14 ok => 1,
299             n => 1,
300             upserted => $id,
301             updatedExisting => false,
302             wtime => 0,
303             };
304             } else {
305 2         5 foreach (@docs) {
306 2         11 $self->save(update_doc($_, $update));
307             }
308             return {
309 2         14 ok => 1,
310             n => scalar @docs,
311             updatedExisting => true,
312             wtime => 0,
313             };
314             }
315             }
316              
317             =head2 remove( [ $query, \%opts ] )
318              
319             Removes all objects matching the given query from the database. If a query
320             is not given, removes all objects from the collection.
321              
322             Returns a hash-ref of information about the remove, including how many
323             documents were removed (n).
324              
325             C can take a hash reference of options. The options currently supported are:
326              
327             =over
328              
329             =item * C - Only one matching document to be removed instead of all.
330              
331             =back
332              
333             =cut
334              
335             sub remove {
336 4     4 1 1570 my ($self, $query, $opts) = @_;
337              
338 4 0 66     26 confess "query must be a hash reference, even-numbered array reference or Tie::IxHash object."
      33        
      0        
      33        
339             if $query && ref $query ne 'HASH' &&
340             ref $query ne 'Tie::IxHash' &&
341             (ref $query ne 'ARRAY' ||
342             (ref $query eq 'ARRAY' && scalar @$query % 2 != 0)
343             );
344              
345 4 50 33     16 confess "the options structure must be a hash reference."
346             if $opts && ref $opts ne 'HASH';
347              
348 4   100     19 $query ||= {};
349 4   50     23 $opts ||= {};
350              
351 4 50       17 my @docs = $opts->{just_one} ? ($self->find_one($query)) : $self->find($query)->all;
352 4         23 foreach (@docs) {
353 10 100 66     71 my $oid = blessed $_->{_id} && blessed $_->{_id} eq 'MorboDB::OID' ?
354             $_->{_id}->value : $_->{_id};
355 10         57 delete $self->_data->{$oid};
356             }
357              
358             return {
359 4         38 ok => 1,
360             n => scalar @docs,
361             wtime => 0,
362             };
363             }
364              
365             =head2 ensure_index()
366              
367             Not implemented. Simply returns true here.
368              
369             =cut
370              
371 0     0 1 0 sub ensure_index { 1 } # not implemented
372              
373             =head2 save( \%doc )
374              
375             Inserts a document into the database if it does not have an C<_id> field,
376             upserts it if it does have an C<_id> field. Mostly used internally. Document
377             must be a hash-reference.
378              
379             =cut
380              
381             sub save {
382 18     18 1 732 my ($self, $doc) = @_;
383              
384 18 50 33     102 confess "document to save must be a hash reference."
385             unless $doc && ref $doc eq 'HASH';
386              
387 18   66     77 $doc->{_id} ||= MorboDB::OID->new;
388              
389 18 100 66     216 my $oid = blessed $doc->{_id} && blessed $doc->{_id} eq 'MorboDB::OID' ?
390             $doc->{_id}->value : $doc->{_id};
391              
392 18         433 $self->_data->{$oid} = clone($doc);
393              
394 18         93 return $doc->{_id};
395             }
396              
397             =head2 count( [ $query ] )
398              
399             Shortcut for running C<< $coll->find($query)->count() >>.
400              
401             =cut
402              
403             sub count {
404 8     8 1 14 my ($self, $query) = @_;
405              
406 8         21 $self->find($query)->count;
407             }
408              
409             =head2 validate()
410              
411             Not implemented. Returns an empty hash-ref here.
412              
413             =cut
414              
415 0     0 1 0 sub validate { {} } # not implemented
416              
417             =head2 drop_indexes()
418              
419             Not implemented. Returns true here.
420              
421             =cut
422              
423 0     0 1 0 sub drop_indexes { 1 } # not implemented
424              
425             =head2 drop_index()
426              
427             Not implemented. Returns true here.
428              
429             =cut
430              
431 0     0 1 0 sub drop_index { 1 } # not implemented
432              
433             =head2 get_indexes()
434              
435             Not implemented. Returns false here.
436              
437             =cut
438              
439 0     0 1 0 sub get_indexes { return } # not implemented
440              
441             =head2 drop()
442              
443             Deletes the collection and all documents in it.
444              
445             =cut
446              
447             sub drop {
448 2     2 1 5 my $self = shift;
449              
450 2         12 $self->_clear_data;
451 2         1058 delete $self->_database->_colls->{$self->name};
452 2         6 return;
453             }
454              
455 0     0     sub _build_full_name { $_[0]->_database->name.'.'.$_[0]->name }
456              
457             =head1 DIAGNOSTICS
458              
459             This module throws the following exceptions:
460              
461             =over
462              
463             =item C<< expected Tie::IxHash, hash, or array reference for keys >>
464              
465             This error is returned by the static C function if you're
466             not providing it with a hash reference, array reference (even-numbered)
467             or Tie::IxHash object.
468              
469             =item C<< query must be a hash reference, even-numbered array reference or Tie::IxHash object. >>
470              
471             This error is returned by the C, C, C, C
472             and C methods, that expect a query that is either a hash reference, even-numbered
473             array reference or Tie::IxHash object. Just make sure you're providing
474             a valid query variable.
475              
476             =item C<< batch_insert() expects an array reference of documents. >>
477              
478             This error is thrown by C if you're not giving it an
479             array reference of documents to insert into the database.
480              
481             =item C<< data to insert must be a hash reference, even-numbered array reference or Tie::IxHash object. >>
482              
483             This error is thrown by C and C when you're providing
484             them with a document which is not a hash reference, even-numbered array
485             reference or Tie::IxHash object. Just make sure your document(s) is/are
486             valid.
487              
488             =item C<< duplicate key error, ID %s already exists in the collection. >>
489              
490             This error is thrown by C and C, when you're trying
491             to insert a document with an C<_id> attribute that already exists in the
492             collection. If you're trying to update a document you know already exists,
493             use the C method instead. Otherwise you're just doing it wrong.
494              
495             =item C<< the update structure must be a hash reference. >>
496              
497             This error is thrown by the C method when you're not giving it
498             a proper update hash-ref, as described by L.
499              
500             =item C<< the options structure must be a hash reference. >>
501              
502             This error is thrown by C when you're providing it with a third
503             argument that should be an options hash-ref, or by the C method
504             when you're providing it with a second argument that should be an options
505             hash-ref. Just make sure you're not sending non hash-refs to these methods.
506              
507             =item C<< document to save must be a hash reference. >>
508              
509             This error is thrown by the C method when it receives a document
510             which is not a hash reference. If this happens when invoking C
511             or C, and non of the specific errors of these methods were
512             thrown, please submit a bug report. Otherwise (if you've called C
513             directly, please make sure you're providing a hash reference. As opposed
514             to C and C, C does not take a Tie::IxHash
515             objects or even-numbered array references.
516              
517             =back
518              
519             =head1 BUGS AND LIMITATIONS
520              
521             No bugs have been reported.
522              
523             Please report any bugs or feature requests to
524             C, or through the web interface at
525             L.
526              
527             =head1 SEE ALSO
528              
529             L.
530              
531             =head1 AUTHOR
532              
533             Ido Perlmuter
534              
535             =head1 LICENSE AND COPYRIGHT
536              
537             Copyright (c) 2011-2013, Ido Perlmuter C<< ido@ido50.net >>.
538              
539             This module is free software; you can redistribute it and/or
540             modify it under the same terms as Perl itself, either version
541             5.8.1 or any later version. See L
542             and L.
543              
544             The full text of the license can be found in the
545             LICENSE file included with this module.
546              
547             =head1 DISCLAIMER OF WARRANTY
548              
549             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
550             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
551             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
552             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
553             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
554             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
555             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
556             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
557             NECESSARY SERVICING, REPAIR, OR CORRECTION.
558              
559             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
560             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
561             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
562             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
563             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
564             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
565             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
566             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
567             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
568             SUCH DAMAGES.
569              
570             =cut
571              
572             __PACKAGE__->meta->make_immutable;
573             __END__