File Coverage

blib/lib/ArangoDB/Collection.pm
Criterion Covered Total %
statement 57 311 18.3
branch 0 86 0.0
condition 0 26 0.0
subroutine 19 57 33.3
pod 33 33 100.0
total 109 513 21.2


line stmt bran cond sub pod time code
1             package ArangoDB::Collection;
2 8     8   49 use strict;
  8         13  
  8         320  
3 8     8   43 use warnings;
  8         23  
  8         253  
4 8     8   40 use utf8;
  8         45  
  8         55  
5 8     8   375 use 5.008001;
  8         30  
  8         395  
6 8     8   42 use JSON ();
  8         14  
  8         193  
7 8     8   51 use Carp qw(croak);
  8         27  
  8         591  
8 8     8   51 use Scalar::Util qw(weaken);
  8         27  
  8         12943  
9 8     8   54 use Class::Accessor::Lite ( ro => [qw/id status/], );
  8         13  
  8         82  
10 8     8   7501 use ArangoDB::Constants qw(:api :status);
  8         24  
  8         2628  
11 8     8   6185 use ArangoDB::Document;
  8         30  
  8         288  
12 8     8   110 use ArangoDB::Edge;
  8         15  
  8         158  
13 8     8   5383 use ArangoDB::Index::Primary;
  8         25  
  8         246  
14 8     8   5242 use ArangoDB::Index::Hash;
  8         23  
  8         225  
15 8     8   5434 use ArangoDB::Index::SkipList;
  8         20  
  8         339  
16 8     8   7336 use ArangoDB::Index::Geo;
  8         21  
  8         237  
17 8     8   5983 use ArangoDB::Index::CapConstraint;
  8         22  
  8         221  
18 8     8   9863 use ArangoDB::Cursor;
  8         109  
  8         334  
19 8     8   5664 use ArangoDB::ClientException;
  8         22  
  8         441  
20             use overload
21 0     0     q{""} => sub { shift->id },
22 8     8   54 fallback => 1;
  8         17  
  8         58  
23              
24             my $JSON = JSON->new->utf8;
25              
26             =pod
27              
28             =head1 NAME
29              
30             ArangoDB::Collection - An ArangoDB collection
31              
32             =head1 DESCRIPTION
33              
34             A instance of ArangoDB collection.
35              
36             =head1 METHODS FOR COLLECTION HANDLING
37              
38             =head2 new($connection, $collection_info)
39              
40             Constructor.
41              
42             =cut
43              
44             sub new {
45 0     0 1   my ( $class, $db, $raw_collection ) = @_;
46 0           my $self = bless { db => $db, connection => $db->{connection}, }, $class;
47 0           weaken( $self->{db} );
48 0           weaken( $self->{connection} );
49 0           for my $key (qw/id name status/) {
50 0           $self->{$key} = $raw_collection->{$key};
51             }
52 0           $self->{_api_path} = API_COLLECTION . '/' . $self->{id};
53 0           return $self;
54             }
55              
56             =pod
57              
58             =head2 id()
59              
60             Returns identifer of the collection.
61              
62             =head2 status()
63              
64             Returns status of the collection.
65              
66             =cut
67              
68             =pod
69              
70             =head2 name([$name])
71              
72             Returns name of collection.
73             If $name is set, rename the collection.
74              
75             =cut
76              
77             sub name {
78 0     0 1   my ( $self, $name ) = @_;
79 0 0         if ($name) { #rename
80 0           $self->_put_to_this( 'rename', { name => $name } );
81 0           $self->{name} = $name;
82             }
83 0           return $self->{name};
84             }
85              
86             =pod
87              
88             =head2 count()
89              
90             Returns number of documents in the collection.
91              
92             =cut
93              
94             sub count {
95 0     0 1   my $self = shift;
96 0           my $res = $self->_get_from_this('count');
97 0           return $res->{count};
98             }
99              
100             =pod
101              
102             =head2 drop()
103              
104             Drop the collection.
105              
106             =cut
107              
108             sub drop {
109 0     0 1   my $self = shift;
110 0           my $api = $self->{_api_path};
111 0           eval { $self->{connection}->http_delete($api); };
  0            
112 0 0         if ($@) {
113 0           $self->_server_error_handler( $@, 'Failed to drop the collection(%s)' );
114             }
115             }
116              
117             =pod
118              
119             =head2 truncate()
120              
121             Truncate the collection.
122              
123             =cut
124              
125             sub truncate {
126 0     0 1   my $self = shift;
127 0           eval {
128 0           my $res = $self->_put_to_this('truncate');
129 0           $self->{status} = $res->{status};
130             };
131 0 0         if ($@) {
132 0           $self->_server_error_handler( $@, 'Failed to truncate the collection(%s)' );
133             }
134             }
135              
136             =pod
137              
138             =head2 load()
139              
140             Load the collection.
141              
142             =cut
143              
144             sub load {
145 0     0 1   my $self = shift;
146 0           my $res = $self->_put_to_this('load');
147 0           $self->{status} = $res->{status};
148             }
149              
150             =pod
151              
152             =head2 unload()
153              
154             Unload the collection.
155              
156             =cut
157              
158             sub unload {
159 0     0 1   my $self = shift;
160 0           my $res = $self->_put_to_this('unload');
161 0           $self->{status} = $res->{status};
162             }
163              
164             =pod
165              
166             =head2 is_newborn()
167              
168             Return true if status of the collection is 'new born'.
169              
170             =cut
171              
172             sub is_newborn {
173 0     0 1   $_[0]->{status} == NEWBORN;
174             }
175              
176             =pod
177              
178             =head2 is_unloaded()
179              
180             Return true if status of the collection is 'unloaded'.
181              
182             =cut
183              
184             sub is_unloaded {
185 0     0 1   $_[0]->{status} == UNLOADED;
186             }
187              
188             =pod
189              
190             =head2 is_loaded()
191              
192             Return true if status of the collection is 'loaded'.
193              
194             =cut
195              
196             sub is_loaded {
197 0     0 1   $_[0]->{status} == LOADED;
198             }
199              
200             =pod
201              
202             =head2 is_being_unloaded()
203              
204             Return true if status of the collection is 'being unloaded'.
205              
206             =cut
207              
208             sub is_being_unloaded {
209 0     0 1   $_[0]->{status} == BEING_UNLOADED;
210             }
211              
212             =pod
213              
214             =head2 is_deleted()
215              
216             Return true if status of the collection is 'deleted'.
217              
218             =cut
219              
220             sub is_deleted {
221 0     0 1   $_[0]->{status} == DELETED;
222             }
223              
224             =pod
225              
226             =head2 is_corrupted()
227              
228             Return true if status of the collection is invalid.
229              
230             =cut
231              
232             sub is_corrupted {
233 0     0 1   return $_[0]->{status} >= CORRUPTED;
234             }
235              
236             =pod
237              
238             =head2 figure($type)
239              
240             Returns number of documents and additional statistical information about the collection.
241              
242             $type is key name of figures.The key names are:
243              
244             =over 4
245              
246             =item count
247              
248             The number of documents inside the collection.
249              
250             =item alive-count
251              
252             The number of living documents.
253              
254             =item alive-size
255              
256             The total size in bytes used by all living documents.
257              
258             =item dead-count
259              
260             The number of dead documents.
261              
262             =item dead-size
263              
264             The total size in bytes used by all dead documents.
265              
266             =item dead-deletion
267              
268             The total number of deletion markers.
269              
270             =item datafiles-count
271              
272             The number of active datafiles.
273              
274             =item datafiles-fileSize
275              
276             The total filesize of datafiles.
277              
278             =item journals-count
279              
280             The number of journal files.
281              
282             =item journals-fileSize
283              
284             The total filesize of journal files.
285              
286             =item journalSize
287              
288             The maximal size of the journal in bytes.
289              
290             =back
291              
292             =cut
293              
294             sub figure {
295 0     0 1   my ( $self, $type ) = @_;
296 0           my $res = $self->_get_from_this('figures');
297 0 0         if ( defined $type ) {
298 0 0         return $res->{count} if $type eq 'count';
299 0 0         return $res->{journalSize} if $type eq 'journalSize';
300 0           my ( $area, $name ) = split( '-', $type );
301 0 0         if ( exists $res->{figures}{$area} ) {
302 0 0         return $res->{figures}{$area} unless defined $name;
303 0           return $res->{figures}{$area}{$name};
304             }
305             }
306             else {
307 0           return $res->{figures};
308             }
309 0           return;
310             }
311              
312             =pod
313              
314             =head2 wait_for_sync($boolean)
315              
316             Set or get the property 'wait_for_sync' of the collection.
317              
318             =cut
319              
320             sub wait_for_sync {
321 0     0 1   my $self = shift;
322 0 0         if ( @_ > 0 ) {
323 0 0         my $val = $_[0] ? JSON::true : JSON::false;
324 0           my $res = $self->_put_to_this( 'properties', { waitForSync => $val } );
325             }
326             else {
327 0           my $res = $self->_get_from_this('properties');
328 0 0         my $ret = $res->{waitForSync} eq 'true' ? 1 : 0;
329 0           return $ret;
330             }
331             }
332              
333             =pod
334              
335             =head1 METHODS FOR DOCUMENT HANDLING
336              
337             =head2 save($data)
338              
339             Save document to the collection. Returns instance of L.
340              
341             $collection->save( { name => 'John' } );
342              
343             =cut
344              
345             sub save {
346 0     0 1   my ( $self, $data ) = @_;
347 0           my $api = API_DOCUMENT . '?collection=' . $self->{id};
348 0           my $doc = eval {
349 0           my $res = $self->{connection}->http_post( $api, $data );
350 0           ArangoDB::Document->new( $self->{connection}, $res )->fetch;
351             };
352 0 0         if ($@) {
353 0           $self->_server_error_handler( $@, 'Failed to save the new document to the collection(%s)' );
354             }
355 0           return $doc;
356             }
357              
358             =pod
359              
360             =head2 bulk_import($header,$body)
361              
362             Import multiple documents at once.
363              
364             =over 4
365              
366             =item $header
367              
368             attribute names(ARRAY reference).
369              
370             =item $body
371              
372             document values(ARRAY reference).
373              
374             =back
375              
376             Example:
377              
378             $collection->bulk_import(
379             [qw/fistsName lastName age gender/],
380             [
381             [ "Joe", "Public", 42, "male" ],
382             [ "Jane", "Doe", 31, "female" ],
383             ]
384             );
385              
386             =cut
387              
388             sub bulk_import {
389 0     0 1   my ( $self, $header, $body ) = @_;
390 0 0 0       croak( ArangoDB::ClientException->new('1st parameter must be ARRAY reference.') )
391             unless $header && ref($header) eq 'ARRAY';
392 0 0 0       croak( ArangoDB::ClientException->new('2nd parameter must be ARRAY reference.') )
393             unless $body && ref($body) eq 'ARRAY';
394 0           my $api = API_IMPORT . '?collection=' . $self->{id};
395 0           my $data = join "\n", map { $JSON->encode($_) } ( $header, @$body );
  0            
396 0           my $res = eval { $self->{connection}->http_post( $api, $data, 1 ) };
  0            
397 0 0         if ($@) {
398 0           $self->_server_error_handler( $@, 'Failed to bulk import to the collection(%s)' );
399             }
400 0           return $res;
401             }
402              
403             =pod
404              
405             =head2 bulk_import_self_contained($documents)
406              
407             Import multiple self-contained documents at once.
408              
409             $documents is the ARRAY reference of documents.
410              
411             Example:
412              
413             $collection->bulk_import_self_contained( [
414             { name => 'foo', age => 20 },
415             { type => 'bar', count => 100 },
416             ] );
417              
418             =cut
419              
420             sub bulk_import_self_contained {
421 0     0 1   my ( $self, $documents ) = @_;
422 0 0 0       croak( ArangoDB::ClientException->new('Parameter must be ARRAY reference.') )
423             unless $documents && ref($documents) eq 'ARRAY';
424 0           my $api = API_IMPORT . '?type=documents&collection=' . $self->{id};
425 0           my $data = join "\n", map { $JSON->encode($_) } @$documents;
  0            
426 0           my $res = eval { $self->{connection}->http_post( $api, $data, 1 ) };
  0            
427 0 0         if ($@) {
428 0           $self->_server_error_handler( $@, 'Failed to bulk import to the collection(%s)' );
429             }
430 0           return $res;
431             }
432              
433             =pod
434              
435             =head1 METHODS FOR EDGE HANDLING
436              
437             =head2 save_edge($from,$to[,$data])
438              
439             Save edge to the collection. Returns instance of L.
440              
441             =over 4
442              
443             =item $from
444              
445             The document that start-point of the edge.
446              
447             =item $to
448              
449             The document that end-point of the edge.
450              
451             =item $data
452              
453             Document data.
454              
455             =back
456              
457             $collection->save_edge($document1,$document2, { rel => 'has-a' });
458              
459             =cut
460              
461             sub save_edge {
462 0     0 1   my ( $self, $from, $to, $data ) = @_;
463 0           my $api = API_EDGE . '?collection=' . $self->{id} . '&from=' . $from . '&to=' . $to;
464 0           my $edge = eval {
465 0           my $res = $self->{connection}->http_post( $api, $data );
466 0           $self->{db}->edge( $res->{_id} );
467             };
468 0 0         if ($@) {
469 0           $self->_server_error_handler( $@, "Failed to save the new edge to the collection(%s)" );
470             }
471 0           return $edge;
472             }
473              
474             =pod
475              
476             =head1 METHODS FOR SIMPLE QUERY HANDLING
477              
478             =head2 all([$options])
479            
480             Send 'all' simple query. Returns instance of L.
481              
482             This will return all documents of in the collection.
483              
484             my $cursor = $collection->all({ limit => 100 });
485              
486             $options is query option(HASH reference).The attributes of $options are:
487              
488             =over 4
489              
490             =item limit
491              
492             The maximal amount of documents to return. (optional)
493              
494             =item skip
495              
496             The documents to skip in the query. (optional)
497              
498             =back
499              
500             =cut
501              
502             sub all {
503 0     0 1   my ( $self, $options ) = @_;
504 0   0       $options ||= {};
505 0           my $data = { collection => $self->{id} };
506 0           for my $key ( grep { exists $options->{$_} } qw{limit skip} ) {
  0            
507 0           $data->{$key} = $options->{$key};
508             }
509 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_ALL, $data ) };
  0            
510 0 0         if ($@) {
511 0           $self->_server_error_handler( $@, 'Failed to call Simple API(all) for the collection(%s)' );
512             }
513 0           return ArangoDB::Cursor->new( $self->{connection}, $res );
514             }
515              
516             =pod
517              
518             =head2 by_example($example[,$options])
519              
520             Send 'by_example' simple query. Returns instance of L.
521              
522             This will find all documents matching a given example.
523              
524             my $cursor = $collection->by_example({ age => 20 });
525              
526             =over 4
527              
528             =item $example
529              
530             The exmaple.
531              
532             =item $options
533              
534             Query option(HASH reference).The attributes of $options are:
535              
536             =over 4
537              
538             =item limit
539              
540             The maximal amount of documents to return. (optional)
541              
542             =item skip
543              
544             The documents to skip in the query. (optional)
545              
546             =back
547              
548             =back
549              
550             =cut
551              
552             sub by_example {
553 0     0 1   my ( $self, $example, $options ) = @_;
554 0   0       $options ||= {};
555 0           my $data = { collection => $self->{id}, example => $example };
556 0           map { $data->{$_} = $options->{$_} } grep { exists $options->{$_} } qw(limit skip);
  0            
  0            
557 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_EXAMPLE, $data ) };
  0            
558 0 0         if ($@) {
559 0           $self->_server_error_handler( $@, 'Failed to call Simple API(by_example) for the collection(%s)' );
560             }
561 0           return ArangoDB::Cursor->new( $self->{connection}, $res );
562             }
563              
564             =pod
565              
566             =head2 first_example($example)
567              
568             Send 'first_example' simple query. Returns instance of L.
569              
570             This will return the first document matching a given example.
571              
572             $example is the exmaple.
573              
574             my $document = $collection->by_example({ age => 20 });
575              
576             =cut
577              
578             sub first_example {
579 0     0 1   my ( $self, $example ) = @_;
580 0           my $data = { collection => $self->{id}, example => $example };
581 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_FIRST, $data ) };
  0            
582 0 0         if ($@) {
583 0           $self->_server_error_handler( $@, 'Failed to call Simple API(first_example) for the collection(%s)' );
584             }
585 0           return ArangoDB::Document->new( $self->{connection}, $res->{document} );
586             }
587              
588             =pod
589              
590             =head2 range($attr,$lower,$upper[,$options])
591              
592             Send 'range' simple query. Returns instance of L.
593              
594             It looks for documents in the collection with attribute between two values.
595              
596             Note: You must declare a skip-list index on the attribute in order to be able to use a range query.
597              
598             my $cursor = $collection->range('age', 20, 29, { closed => 1 } );
599              
600             =over 4
601              
602             =item $attr
603              
604             The attribute path to check.
605              
606             =item $lower
607              
608             The lower bound.
609              
610             =item $upper
611              
612             The upper bound.
613              
614             =item $options
615              
616             Query option(HASH reference).The attributes of $options are:
617              
618             =over 4
619              
620             =item closed
621              
622             If true, use intervall including $lower and $upper, otherwise exclude $upper, but include $lower
623              
624             =item limit
625              
626             The maximal amount of documents to return. (optional)
627              
628             =item skip
629              
630             The documents to skip in the query. (optional)
631              
632             =back
633              
634             =back
635              
636             =cut
637              
638             sub range {
639 0     0 1   my ( $self, $attr, $lower, $upper, $options ) = @_;
640 0   0       $options ||= {};
641 0           my $data = { collection => $self->{id}, attribute => $attr, left => $lower, right => $upper, };
642 0           map { $data->{$_} = $options->{$_} } grep { exists $options->{$_} } qw(closed limit skip);
  0            
  0            
643 0 0         $data->{closed} = $data->{closed} ? JSON::true : JSON::false;
644 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_RANGE, $data ) };
  0            
645 0 0         if ($@) {
646 0           $self->_server_error_handler( $@, 'Failed to call Simple API(range) for the collection(%s)' );
647             }
648 0           return ArangoDB::Cursor->new( $self->{connection}, $res );
649             }
650              
651             =pod
652              
653             =head2 near($latitude,$longitude[,$options])
654              
655             Send 'near' simple query. Returns instance of L.
656              
657             The default will find at most 100 documents near a given coordinate.
658             The returned list is sorted according to the distance, with the nearest document coming first.
659              
660             $cursor = $collection->near(0,0, { limit => 20 } );
661              
662             =over 4
663              
664             =item $latitude
665              
666             The latitude of the coordinate.
667              
668             =item $longitude
669              
670             The longitude of the coordinate.
671              
672             =item $options
673              
674             Query option(HASH reference).The attributes of $options are:
675              
676             =over 4
677              
678             =item distance
679              
680             If given, the attribute key used to store the C to document(optional).
681              
682             C is the distance between the given point and the document in meter.
683              
684             =item limit
685              
686             The maximal amount of documents to return. (optional)
687              
688             =item skip
689              
690             The documents to skip in the query. (optional)
691              
692             =item geo
693              
694             If given, the identifier of the geo-index to use. (optional)
695              
696             =back
697              
698             =back
699              
700             =cut
701              
702             sub near {
703 0     0 1   my ( $self, $latitude, $longitude, $options ) = @_;
704 0   0       $options ||= {};
705 0           my $data = { collection => $self->{id}, latitude => $latitude, longitude => $longitude, };
706 0           map { $data->{$_} = $options->{$_} } grep { exists $options->{$_} } qw(distance limit skip geo);
  0            
  0            
707 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_NEAR, $data ) };
  0            
708 0 0         if ($@) {
709 0           $self->_server_error_handler( $@, 'Failed to call Simple API(near) for the collection(%s)' );
710             }
711 0           return ArangoDB::Cursor->new( $self->{connection}, $res );
712             }
713              
714             =pod
715              
716             =head2 within($latitude,$longitude,$radius[,$options])
717              
718             Send 'within' simple query. Returns instance of L.
719              
720             This will find all documents with in a given radius around the coordinate (latitude, longitude).
721             The returned list is sorted by distance.
722              
723             $cursor = $collection->within(0,0, 10 * 1000, { distance => 'distance' } );
724              
725             =over 4
726              
727             =item $latitude
728              
729             The latitude of the coordinate.
730              
731             =item $longitude
732              
733             The longitude of the coordinate.
734              
735             =item $radius
736              
737             The maximal radius(meter).
738              
739             =item $options
740              
741             Query option(HASH reference).The attributes of $options are:
742              
743             =over 4
744              
745             =item distance
746              
747             If given, the attribute name used to store the C to document(optional).
748              
749             C is the distance between the given point and the document in meter.
750              
751             =item limit
752              
753             The maximal amount of documents to return. (optional)
754              
755             =item skip
756              
757             The documents to skip in the query. (optional)
758              
759             =item geo
760              
761             If given, the identifier of the geo-index to use. (optional)
762              
763             =back
764              
765             =back
766              
767             =cut
768              
769             sub within {
770 0     0 1   my ( $self, $latitude, $longitude, $radius, $options ) = @_;
771 0   0       $options ||= {};
772 0           my $data = { collection => $self->{id}, latitude => $latitude, longitude => $longitude, radius => $radius, };
773 0           map { $data->{$_} = $options->{$_} }
  0            
774 0           grep { exists $options->{$_} } qw(distance limit skip geo);
775 0           my $res = eval { $self->{connection}->http_put( API_SIMPLE_WITHIN, $data ) };
  0            
776 0 0         if ($@) {
777 0           $self->_server_error_handler( $@, 'Failed to call Simple API(within) for the collection(%s)' );
778             }
779 0           return ArangoDB::Cursor->new( $self->{connection}, $res );
780             }
781              
782             =pod
783              
784             =head1 METHODS FOR INDEX HANDLING
785              
786             =head2 ensure_hash_index($fileds)
787              
788             Create hash index for the collection. Returns instance of L.
789              
790             This hash is then used in queries to locate documents in O(1) operations.
791              
792             $fileds is the field of index.
793              
794             $collection->ensure_hash_index([qw/user.name/]);
795             $collection->save({ user => { name => 'John', age => 42, } });
796              
797             =cut
798              
799             sub ensure_hash_index {
800 0     0 1   my ( $self, $fields ) = @_;
801 0           my $api = API_INDEX . '?collection=' . $self->{id};
802 0           my $data = { type => 'hash', unique => JSON::false, fields => $fields, };
803 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
804 0 0         if ($@) {
805 0           $self->_server_error_handler( $@, 'Failed to create hash index on the collection(%s)' );
806             }
807 0           return ArangoDB::Index::Hash->new( $self->{connection}, $res );
808             }
809              
810             =pod
811              
812             =head2 ensure_unique_constraint($fileds)
813              
814             Create unique hash index for the collection. Returns instance of L.
815              
816             This hash is then used in queries to locate documents in O(1) operations.
817             If using unique hash index then no two documents are allowed to have the same set of attribute values.
818              
819             $fileds is the field of index.
820              
821             =cut
822              
823             sub ensure_unique_constraint {
824 0     0 1   my ( $self, $fields ) = @_;
825 0           my $api = API_INDEX . '?collection=' . $self->{id};
826 0           my $data = { type => 'hash', unique => JSON::true, fields => $fields, };
827 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
828 0 0         if ($@) {
829 0           $self->_server_error_handler( $@, 'Failed to create unique hash index on the collection(%s)' );
830             }
831 0           return ArangoDB::Index::Hash->new( $self->{connection}, $res );
832             }
833              
834             =pod
835              
836             =head2 ensure_skiplist($fileds)
837              
838             Create skip-list index for the collection. Returns instance of L.
839              
840             This skip-list is then used in queries to locate documents within a given range.
841              
842             $fileds is the field of index.
843              
844             $collection->ensure_skiplist([qw/user.age/]);
845             $collection->save({ user => { name => 'John', age => 42 } });
846              
847             =cut
848              
849             sub ensure_skiplist {
850 0     0 1   my ( $self, $fields ) = @_;
851 0           my $api = API_INDEX . '?collection=' . $self->{id};
852 0           my $data = { type => 'skiplist', unique => JSON::false, fields => $fields, };
853 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
854 0 0         if ($@) {
855 0           $self->_server_error_handler( $@, 'Failed to create skiplist index on the collection(%s)' );
856             }
857 0           return ArangoDB::Index::SkipList->new( $self->{connection}, $res );
858             }
859              
860             =pod
861              
862             =head2 ensure_unique_skiplist($fileds)
863              
864             Create unique skip-list index for the collection. Returns instance of L.
865              
866             This skip-list is then used in queries to locate documents within a given range.
867             If using unique skip-list then no two documents are allowed to have the same set of attribute values.
868              
869             $fileds is the field of index.
870              
871             =cut
872              
873             sub ensure_unique_skiplist {
874 0     0 1   my ( $self, $fields, $unique ) = @_;
875 0           my $api = API_INDEX . '?collection=' . $self->{id};
876 0           my $data = { type => 'skiplist', unique => JSON::true, fields => $fields, };
877 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
878 0 0         if ($@) {
879 0           $self->_server_error_handler( $@, 'Failed to create unique skiplist index on the collection(%s)' );
880             }
881 0           return ArangoDB::Index::SkipList->new( $self->{connection}, $res );
882             }
883              
884             =pod
885              
886             =head2 ensure_geo_index($fileds[,$is_geojson])
887              
888             Create geo index for the collection. Returns instance of L.
889              
890             =over 4
891              
892             =item $fileds
893              
894             The field of index.
895              
896             =item $is_geojson
897              
898             Boolean flag. If it is true, then the order within the list is longitude followed by latitude.
899              
900             =back
901              
902             Create an geo index for a list attribute:
903              
904             $collection->ensure_geo_index( [qw/loc/] );
905             $collection->save({ loc => [0 ,0] });
906              
907             Create an geo index for a hash array attribute:
908              
909             $collection->ensure_geo_index( [qw/location.latitude location.longitude/] );
910             $collection->save({ location => { latitude => 0, longitude => 0 } });
911              
912             =cut
913              
914             sub ensure_geo_index {
915 0     0 1   my ( $self, $fields, $is_geojson ) = @_;
916 0           my $api = API_INDEX . '?collection=' . $self->{id};
917 0 0         my $data = {
918             type => 'geo',
919             fields => $fields,
920             constraint => JSON::false,
921             geoJson => $is_geojson ? JSON::true : JSON::false,
922             };
923 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
924 0 0         if ($@) {
925 0           $self->_server_error_handler( $@, 'Failed to create geo index on the collection(%s)' );
926             }
927 0           return ArangoDB::Index::Geo->new( $self->{connection}, $res );
928             }
929              
930             =pod
931              
932             =head2 ensure_geo_constraint($fileds[,$ignore_null])
933              
934             It works like ensure_geo_index() but requires that the documents contain a valid geo definition.
935             Returns instance of L.
936              
937             =over 4
938              
939             =item $fileds
940              
941             The field of index.
942              
943             =item $ignore_null
944              
945             Boolean flag. If it is true, then documents with a null in location or at least one null in latitude or longitude are ignored.
946              
947             =back
948              
949             =cut
950              
951             sub ensure_geo_constraint {
952 0     0 1   my ( $self, $fields, $ignore_null ) = @_;
953 0           my $api = API_INDEX . '?collection=' . $self->{id};
954 0 0         my $data = {
955             type => 'geo',
956             fields => $fields,
957             constraint => JSON::true,
958             ignoreNull => $ignore_null ? JSON::true : JSON::false,
959             };
960 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
961 0 0         if ($@) {
962 0           $self->_server_error_handler( $@, 'Failed to create geo constraint on the collection(%s)' );
963             }
964 0           return ArangoDB::Index::Geo->new( $self->{connection}, $res );
965             }
966              
967             =pod
968              
969             =head2 ensure_cap_constraint($size)
970              
971             Create cap constraint for the collection.Returns instance of L.
972              
973             It is possible to restrict the size of collection.
974              
975             $size is the maximal number of documents.
976              
977             Restrict the number of document to at most 100 documents:
978              
979             $collection->ensure_cap_constraint(100);
980              
981             =cut
982              
983             sub ensure_cap_constraint {
984 0     0 1   my ( $self, $size ) = @_;
985 0           my $api = API_INDEX . '?collection=' . $self->{id};
986 0           my $data = { type => 'cap', size => $size, };
987 0           my $res = eval { $self->{connection}->http_post( $api, $data ) };
  0            
988 0 0         if ($@) {
989 0           $self->_server_error_handler( $@, 'Failed to create cap constraint on the collection(%s)' );
990             }
991 0           return ArangoDB::Index::CapConstraint->new( $self->{connection}, $res );
992             }
993              
994             =pod
995              
996             =head2 get_indexes()
997              
998             Returns list of indexes of the collection.
999              
1000             =cut
1001              
1002             sub get_indexes {
1003 0     0 1   my $self = shift;
1004 0           my $api = API_INDEX . '?collection=' . $self->{id};
1005 0           my @indexes = eval {
1006 0           my $res = $self->{connection}->http_get($api);
1007 0           map { $self->_get_index_instance($_) } @{ $res->{indexes} };
  0            
  0            
1008             };
1009 0 0         if ($@) {
1010 0           $self->_server_error_handler( $@, 'Failed to get the index($index_id) on the collection(%s)' );
1011             }
1012 0           return \@indexes;
1013             }
1014              
1015             # Get property of the collection.
1016             sub _get_from_this {
1017 0     0     my ( $self, $path ) = @_;
1018 0           my $api = $self->{_api_path} . '/' . $path;
1019 0           my $res = eval { $self->{connection}->http_get($api) };
  0            
1020 0 0         if ($@) {
1021 0           $self->_server_error_handler( $@, "Failed to get the property($path) of the collection(%s)" );
1022             }
1023 0           return $res;
1024             }
1025              
1026             # Set property of the collection.
1027             sub _put_to_this {
1028 0     0     my ( $self, $path, $params ) = @_;
1029 0           my $api = $self->{_api_path} . '/' . $path;
1030 0           my $res = eval { $self->{connection}->http_put( $api, $params ) };
  0            
1031 0 0         if ($@) {
1032 0           $self->_server_error_handler( $@, "Failed to update the property($path) of the collection(%s)" );
1033             }
1034 0           return $res;
1035             }
1036              
1037             # get instance of index
1038             sub _get_index_instance {
1039 0     0     my ( $self, $index ) = @_;
1040 0   0       my $type = $index->{type} || q{};
1041 0           my $conn = $self->{connection};
1042 0 0         if ( $type eq 'primary' ) {
    0          
    0          
    0          
    0          
1043 0           return ArangoDB::Index::Primary->new( $conn, $index );
1044             }
1045             elsif ( $type eq 'hash' ) {
1046 0           return ArangoDB::Index::Hash->new( $conn, $index );
1047             }
1048             elsif ( $type eq 'skiplist' ) {
1049 0           return ArangoDB::Index::SkipList->new( $conn, $index );
1050             }
1051             elsif ( $type eq 'cap' ) {
1052 0           return ArangoDB::Index::CapConstraint->new( $conn, $index );
1053             }
1054             elsif ( $type =~ /^geo[12]$/ ) {
1055 0           return ArangoDB::Index::Geo->new( $conn, $index );
1056             }
1057             else {
1058 0           croak(
1059             ArangoDB::ServerException->new(
1060             { code => 500, status => '', detail => { errorMessage => "Unknown index type($type)", } }
1061             )
1062             );
1063             }
1064             }
1065              
1066             # Handling server error
1067             sub _server_error_handler {
1068 0     0     my ( $self, $error, $message ) = @_;
1069 0           my $msg = sprintf( $message, $self->{name} );
1070 0 0 0       if ( ref($error) && $error->isa('ArangoDB::ServerException') ) {
1071 0   0       $msg .= ':' . ( $error->detail->{errorMessage} || q{} );
1072             }
1073 0           croak $msg;
1074             }
1075              
1076             1;
1077             __END__