File Coverage

blib/lib/MendeleySQLite.pm
Criterion Covered Total %
statement 118 127 92.9
branch 17 32 53.1
condition 5 11 45.4
subroutine 16 16 100.0
pod 9 9 100.0
total 165 195 84.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 1     1   206469 use strict;
  1         3  
  1         30  
4 1     1   4 use warnings;
  1         3  
  1         35  
5              
6             package MendeleySQLite;
7              
8 1     1   4 use Data::Dumper;
  1         7  
  1         44  
9 1     1   492206 use DBI;
  1         22667  
  1         77  
10 1     1   1383 use SQL::Abstract;
  1         10238  
  1         1467  
11              
12             # ABSTRACT: A collection of tools for working with Mendeley Desktop's SQLite backend.
13              
14              
15             sub new {
16 1     1 1 38 my $class = shift;
17 1         2 my $rh_params = shift;
18 1         3 my $self = { };
19            
20 1 50 33     19 unless ( defined $rh_params->{dbfile} && -e $rh_params->{dbfile} ){
21 0         0 die "Specify the path to the SQLite database using the dbfile parameter."
22             }
23            
24 1         12 $self->{dbh} =
25             DBI->connect("dbi:SQLite:dbname=$rh_params->{dbfile}","","");
26            
27 1 50       12001 unless ( $self->{dbh} ) {
28 0         0 die "Could not connect to $rh_params->{dbfile}."
29             }
30            
31 1         10 $self->{sql} = SQL::Abstract->new();
32            
33 1         70 return bless $self, $class;
34             }
35              
36              
37             sub get_all_keywords {
38 1     1 1 468 my $self = shift;
39            
40 1         3 my $sql_all_keywords =
41             'SELECT keyword,COUNT(*) AS n from DocumentKeywords GROUP BY keyword';
42            
43 1         4 my $sth = $self->_execute_sql( $sql_all_keywords );
44            
45 1         115 my $ra_all = $sth->fetchall_arrayref();
46            
47 1         3 my $rh_out = { };
48            
49 1         4 foreach my $ra ( @$ra_all ) {
50 36         70 $rh_out->{$ra->[0]} = $ra->[1];
51             }
52            
53 1         29 return $rh_out;
54            
55             }
56              
57              
58             sub get_all_tags {
59 1     1 1 17292 my $self = shift;
60            
61 1         3 my $sql_all_tags =
62             'SELECT tag,COUNT(*) AS n from DocumentTags GROUP BY tag';
63            
64 1         5 my $sth = $self->_execute_sql( $sql_all_tags );
65            
66 1         32 my $ra_all = $sth->fetchall_arrayref();
67            
68 1         3 my $rh_out = { };
69            
70 1         3 foreach my $ra ( @$ra_all ) {
71 7         14 $rh_out->{$ra->[0]} = $ra->[1];
72             }
73            
74 1         17 return $rh_out;
75            
76             }
77              
78              
79             sub get_all_tags_for_document {
80 5     5 1 1600 my $self = shift;
81 5         8 my $document_id = shift;
82            
83 5 50       14 return undef if ( ! defined $document_id );
84            
85 5         9 my $ra_output = [ ];
86            
87 5         23 my ( $sql, $ra_bind ) =
88             $self->_create_sql( 'select', 'DocumentTags', [ 'tag' ], { documentId => $document_id } );
89            
90 5         21 my $sth = $self->_execute_sql( $sql, $ra_bind );
91            
92 5         72 my $ra_all = $sth->fetchall_arrayref();
93            
94 5 50       15 unless ( scalar(@$ra_all) ) {
95 0         0 return $ra_output;
96             }
97            
98 5         10 foreach my $ra ( @$ra_all ) {
99 7         20 push( @$ra_output, $ra->[0] );
100             }
101              
102 5         60 return $ra_output;
103             }
104              
105              
106             sub get_all_keywords_for_document {
107 5     5 1 7404 my $self = shift;
108 5         8 my $document_id = shift;
109            
110 5 50       10 return undef if ( ! defined $document_id );
111            
112 5         9 my $ra_output = [ ];
113            
114 5         87 my ( $sql, $ra_bind ) =
115             $self->_create_sql( 'select', 'DocumentKeywords', [ 'keyword' ], { documentId => $document_id } );
116            
117 5         20 my $sth = $self->_execute_sql( $sql, $ra_bind );
118            
119 5         87 my $ra_all = $sth->fetchall_arrayref();
120            
121 5 50       17 unless ( scalar(@$ra_all) ) {
122 0         0 return $ra_output;
123             }
124            
125 5         12 foreach my $ra ( @$ra_all ) {
126 16         34 push( @$ra_output, $ra->[0] );
127             }
128              
129 5         69 return $ra_output;
130             }
131              
132              
133             sub set_keyword_for_document {
134 2     2 1 611 my $self = shift;
135 2         3 my $id = shift;
136 2         4 my $keyword = shift;
137            
138             return undef
139 2 50 33     10 if ( ! defined $id || ! defined $keyword );
140            
141 2         6 my $ra_keywords =
142             $self->get_all_keywords_for_document( $id );
143            
144 2         5 my %keywords = map { $_ => 1 } @$ra_keywords;
  8         19  
145            
146             ## If the keyword exists already, do not try to re-insert it as the query
147             ## will fail the referential constraint set by the table's schema.
148            
149 2 50       8 if ( exists $keywords{$keyword} ) {
150 2         10 return 1;
151             }
152            
153 0         0 my ( $sql, $ra_bind ) =
154             $self->_create_sql( 'insert', 'DocumentKeywords', { 'documentId' => $id, 'keyword' => $keyword } );
155              
156 0         0 return $self->_execute_sql( $sql, $ra_bind );
157             }
158              
159              
160             sub set_tag_for_document {
161 2     2 1 563 my $self = shift;
162 2         2 my $id = shift;
163 2         3 my $tag = shift;
164            
165             return undef
166 2 50 33     11 if ( ! defined $id || ! defined $tag );
167            
168 2         7 my $ra_tags =
169             $self->get_all_tags_for_document( $id );
170            
171 2         7 my %tags = map { $_ => 1 } @$ra_tags;
  2         8  
172            
173             ## If the tag exists already, do not try to re-insert it as the query
174             ## will fail the referential constraint set by the table's schema.
175            
176 2 50       8 if ( exists $tags{$tag} ) {
177 2         9 return 1;
178             }
179            
180 0         0 my ( $sql, $ra_bind ) =
181             $self->_create_sql( 'insert', 'DocumentTags', { 'documentId' => $id, 'tag' => $tag } );
182              
183 0         0 return $self->_execute_sql( $sql, $ra_bind );
184             }
185              
186              
187              
188             sub get_document {
189 3     3 1 6809 my $self = shift;
190 3         5 my $id = shift;
191            
192 3 100       13 return undef if ( ! defined $id );
193            
194 2         17 my ( $sql, $ra_bind ) =
195             $self->_create_sql( 'select', 'Documents', [ '*' ], { id => $id } );
196            
197 2         10 my $sth = $self->_execute_sql( $sql, $ra_bind );
198              
199 2         24 my $rhh = $sth->fetchall_hashref('id');
200            
201 2         722 my $rh_document = $rhh->{ $id };
202            
203 2 100       11 if ( ! defined $rh_document ) {
204 1         38 return undef;
205             }
206            
207 1         4 my $ra_keywords = $self->get_all_keywords_for_document( $id );
208            
209 1 50       4 if ( scalar(@$ra_keywords) ) {
210 1         3 $rh_document->{keywords} = $ra_keywords;
211             }
212            
213 1         5 my $ra_tags = $self->get_all_tags_for_document( $id );
214            
215 1 50       4 if ( scalar(@$ra_tags) ) {
216 1         3 $rh_document->{tags} = $ra_tags
217             }
218            
219 1         26 return $rh_document;
220            
221             }
222              
223              
224             sub get_all_document_ids {
225 1     1 1 213 my $self = shift;
226            
227 1         3 my $ra_out = [ ];
228            
229 1         5 my ( $sql, $ra_bind ) =
230             $self->_create_sql( 'select', 'Documents', [ 'id' ], { } );
231            
232 1         4 my $sth = $self->_execute_sql( $sql, $ra_bind );
233            
234 1         22 my $raa = $sth->fetchall_arrayref();
235            
236 1         3 foreach my $ra ( @$raa ) {
237 5         11 push(@$ra_out, $ra->[0]);
238             }
239            
240 1         15 return $ra_out;
241             }
242              
243             sub _create_sql {
244 13     13   15 my $self = shift;
245 13         20 my $op = shift;
246 13         15 my $table = shift;
247 13         16 my $r_fields = shift;
248 13         11 my $r_params = shift;
249            
250 13         13 my $stmt;
251             my @bind;
252            
253 13 50       30 if ( $op eq 'select' ) {
    0          
254 13         55 ( $stmt, @bind ) =
255             $self->{sql}->select( $table, $r_fields, $r_params );
256             }
257            
258             elsif ( $op eq 'insert' ) {
259 0         0 ( $stmt, @bind ) =
260             $self->{sql}->insert( $table, $r_fields );
261             }
262            
263 13         2298 return ( $stmt, \@bind );
264             }
265              
266             sub _execute_sql {
267 15     15   27 my $self = shift;
268 15         20 my $sql = shift;
269 15   100     47 my $ra_params = shift || [ ];
270            
271 15         110 my $sth =
272             $self->{dbh}->prepare( $sql );
273            
274 15         4030 $sth->execute( @$ra_params );
275            
276 15         47 return $sth;
277            
278             }
279              
280              
281             1;
282             __END__