File Coverage

blib/lib/Thesaurus/DBI.pm
Criterion Covered Total %
statement 15 129 11.6
branch 0 26 0.0
condition 0 5 0.0
subroutine 5 20 25.0
pod 2 2 100.0
total 22 182 12.0


line stmt bran cond sub pod time code
1             package Thesaurus::DBI;
2              
3 1     1   25362 use strict;
  1         2  
  1         36  
4              
5 1     1   5 use vars qw[$VERSION];
  1         2  
  1         60  
6              
7             $VERSION = '0.01';
8              
9 1     1   4 use base 'Thesaurus';
  1         6  
  1         895  
10 1     1   59765 use DBI;
  1         38279  
  1         68  
11              
12 1     1   11 use Params::Validate qw( validate SCALAR BOOLEAN OBJECT );
  1         2  
  1         1355  
13              
14             # database structure (only needed to create db)
15             my $DB_TABLES = << "END" ;
16             CREATE TABLE assignments (
17             word1 bigint(20) unsigned NOT NULL default '0',
18             word2 bigint(20) unsigned NOT NULL default '0',
19             UNIQUE KEY word1 (word1,word2),
20             UNIQUE KEY word2 (word2,word1)
21             );
22              
23             CREATE TABLE word (
24             ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,
25             word varchar(150) NOT NULL default '',
26             wordindex varchar(150) default NULL,
27             PRIMARY KEY (ID),
28             UNIQUE KEY word (word),
29             KEY wordindex (wordindex)
30             ) AUTO_INCREMENT=1 ;
31              
32             END
33              
34             #########################
35             # initialize dbconnection
36             # overwritten
37             # param: db-connection or db-access data
38             # return:
39             # added: jseibert, 30.05.2006
40             #########################
41             sub _init {
42 0     0     my $self = shift;
43             # check parameters
44 0           my %p = validate( @_,
45             { dbhandle => { type => OBJECT, optional => 1, isa => [ qw( DBI ) ] },
46             dbtype => {type=>SCALAR, optional => 1, default=>'dbi:mysql'},
47             dbname => {type=>SCALAR},
48             dbhost => { type => SCALAR, optional => 1, default => 'localhost' },
49             dbuser => { type => SCALAR, optional => 1, default=>'' },
50             dbpassword => { type => SCALAR, optional => 1, default=>'' },
51            
52             },
53             );
54            
55             # use existing database connection
56 0 0         if ($p{dbhandle}) {
57 0           $self->{db} = $p{dbhandle};
58              
59             # open database connection
60             } else {
61 0           my $dsn = "$p{dbtype}:host=$p{dbhost};database=$p{dbname}";
62 0           my $dbh = DBI->connect($dsn, $p{dbuser}, $p{dbpassword}, {'PrintError' => '1', 'RaiseError' => '0'} );
63 0 0         if (!$dbh) {
64 0           die "db_connect:" . DBI::errstr();
65 0           return undef;
66             }
67 0           $self->{db} = $dbh;
68             }
69            
70 0           $self->{params} = \%p;
71 0           return 1;
72             }
73              
74              
75             #########################
76             # save synonyms
77             # param: list_of_synonyms: ARRAYREF_OF_String
78             # return: Boolean
79             # added: jseibert, 01.06.2006
80             #########################
81             sub _add_list {
82 0     0     my $self = shift;
83 0           my $list = shift;
84              
85             # create / get id's for every string
86 0           my @ids = map {$self->_save_word($_)} @$list;
  0            
87            
88             # save assignments
89 0           $self->_save_assignment_list(\@ids);
90             }
91              
92             #########################
93             # Search entries in Thesaurus
94             # in mysql: everything ist case-insensitive
95             # param: list_of_synonyms: ARRAY_OF_String
96             # return: HASHREF
97             # added: jseibert, 01.06.2006
98             #########################
99             sub _find {
100 0     0     my $self = shift;
101            
102             # hash for results
103 0           my %lists;
104            
105             # process all parameter and query database
106 0           foreach my $key (@_) {
107 0 0         my $search_key = $self->{params}{ignore_case} ? lc $key : $key;
108             # ignore duplicates
109 0 0         next if $lists{$key};
110            
111 0           my $words = $self->_find_in_db($key);
112            
113 0           foreach my $w (@$words) {
114 0           push( @{ $lists{$key} }, $w ) ;
  0            
115             }
116             }
117 0           return \%lists;
118             }
119              
120             #########################
121             # delete synonym
122             # delete word and all corresponding assignments
123             # param: word: ARRAY_OF_String
124             # return: Boolean
125             # added: jseibert, 30.05.2006
126             #########################
127             sub delete {
128 0     0 1   my $self = shift;
129 0           my @list = @_;
130            
131             # map words to (existing) id's
132 0           my @ids = map {$self->_find_word($_)} @list;
  0            
133            
134             # delete all words
135 0           for (my $i=0; $i<@ids; $i++) {
136 0           my $id = $ids[$i];
137 0 0         next if (!$id);
138 0           $self->_delete_word($id);
139             }
140             }
141              
142             #########################
143             # Create database-tables for a new thesaurus
144             # param:
145             # return: Boolean
146             # added: jseibert, 30.05.2006
147             #########################
148             sub create_tables {
149 0     0 1   my $self = shift;
150 0           my @queries = split(';', $DB_TABLES);
151 0           for (@queries) {
152             # ingore empty lines / queries
153 0 0         next if ($_ =~ /^\s*$/);
154 0           $self->_db_do($_);
155             }
156 0           return;
157             }
158              
159              
160             ######################### Internal helper methods #######################
161              
162             #########################
163             # search for an existing keyword in database. Create new one if none found.
164             # param: word: String
165             # return: ID: INT
166             # added: jseibert, 30.05.2006
167             #########################
168             sub _save_word {
169 0     0     my $self = shift;
170 0           my $word = shift;
171 0           my $id = $self->_find_word($word);
172             # existing word?
173 0 0         if ($id) {
174 0           return $id;
175              
176             # create new
177             } else {
178             # create index value: (additional information in brackets will be removed)
179 0           my $key = $word;
180 0           $key =~ s/\s*\(.*?\)//gi;
181 0           $key =~ s/\s*$//gi;
182 0           $key =~ s/^\s*//gi;
183            
184 0           $self->_db_do("INSERT INTO word SET word = ?, wordindex = ? ", $word, $key);
185 0           my $id = $self->_db_singlevalue("SELECT LAST_INSERT_ID() FROM word LIMIT 1");
186 0           return $id;
187             }
188             }
189              
190             #########################
191             # Save assignments between words
192             # param: ids: ARRAYREF_OF_INT
193             # return: Boolean
194             # added: jseibert, 30.05.2006
195             #########################
196             sub _save_assignment_list {
197 0     0     my $self = shift;
198 0           my $ids = shift;
199             # assign every word(id) with all others
200 0           for (my $i=0; $i<@$ids; $i++) {
201 0           my $id1 = $ids->[$i];
202 0           for (my $j=$i+1; $j < @$ids; $j++) {
203 0           my $id2 = $ids->[$j];
204 0           $self->_save_assignment($id1, $id2);
205             }
206             }
207 0           return 1;
208             }
209              
210             #########################
211             # helper method: save a single word assignment
212             # param: id1: INT, id2: INT
213             # return: Boolean
214             # added: jseibert, 30.05.2006
215             #########################
216             sub _save_assignment {
217 0     0     my $self = shift;
218 0           my $id1 = shift;
219 0           my $id2 = shift;
220 0 0 0       return if (!$id1 || !$id2);
221            
222 0 0         if (!$self->_find_assignment($id1, $id2)) {
223 0           $self->_db_do('INSERT INTO assignments SET word1 = ?, word2 = ?', $id1, $id2);
224             }
225            
226 0           return 1;
227             }
228              
229             #########################
230             # helper-method: serach for an existing assignment
231             # param: id1: INT, id2: INT
232             # return: Boolean
233             # added: jseibert, 30.05.2006
234             #########################
235             sub _find_assignment {
236 0     0     my $self = shift;
237 0           my $id1 = shift;
238 0           my $id2 = shift;
239            
240 0           my $sql = 'SELECT 1 FROM assignments WHERE (word1 = ? AND word2 = ?) OR (word1 = ? AND word2 = ?) LIMIT 1';
241 0           my $found = $self->_db_singlevalue($sql, $id1, $id2, $id2, $id1);
242 0   0       return $found || 0;
243             }
244              
245             #########################
246             # delete a word and all assignments to others
247             # param: word_id: INT
248             # return: Boolean
249             # added: jseibert, 30.05.2006
250             #########################
251             sub _delete_word {
252 0     0     my $self = shift;
253 0           my $id = shift;
254            
255 0           my $sql = 'DELETE FROM word WHERE ID = ?';
256 0           $self->_db_do($sql, $id);
257 0           $sql = 'DELETE FROM assignments WHERE (word1 = ? OR word2 = ?)';
258 0           return $self->_db_do($sql, $id, $id);
259             }
260              
261              
262              
263             #########################
264             # helper-method: search all synonyms of a given word
265             # param: word: String
266             # return: ARRAYREF_OF_string
267             # added: jseibert, 30.05.2006
268             #########################
269             sub _find_in_db {
270 0     0     my $self = shift;
271 0           my $key = shift;
272            
273             # find list of all aliases for the given word
274 0           my $sql = "SELECT IF (word.ID = w1.ID, w2.word, w1.word) AS alias, word.word AS word FROM word
275             INNER JOIN `assignments` ON ( assignments.word1 = word.ID
276             OR assignments.word2 = word.ID )
277             INNER JOIN word AS w1 ON assignments.word1 = w1.ID
278             INNER JOIN word AS w2 ON assignments.word2 = w2.ID
279             WHERE word.wordindex = ?";
280            
281 0           my $sth = $self->_db_query($sql, $key);
282            
283 0           my @words = ();
284             # fetch every single alias
285 0           while (my ($word) = $sth->fetchrow_array()) {
286 0           push(@words, $word);
287             }
288             # synonyms found? add the given word in the result list
289 0 0         if (@words) {
290 0           unshift(@words, $key);
291             }
292 0           return \@words;
293             }
294              
295             #########################
296             # search for the given word in the database and return it's ID
297             # param: word: String
298             # return: ID: INT
299             # added: jseibert, 01.06.2006
300             #########################
301             sub _find_word {
302 0     0     my $self = shift;
303 0           my $word = shift;
304 0           return $self->_db_singlevalue('SELECT ID FROM word WHERE word = ?', $word);
305             }
306              
307             #########################
308             # execute db-query, with error detection
309             # param: query: String, [params: ARRAY_OF_String]
310             # return: statement: DBI::st
311             # added: jseibert, 30.05.2006
312             #########################
313             sub _db_query {
314 0     0     my $self = shift;
315 0           my $sql = shift;
316 0           my @data = @_;
317            
318             # prepare sql query
319 0           my $sth = $self->{'db'}->prepare($sql);
320 0 0         if (!$sth) {
321 0           my $error = $self->{'db'}->errstr();
322 0           die "db_prepare: $error . on query: $sql";
323             }
324            
325             # execute query
326 0           my $result = $sth->execute(@data);
327 0 0         if (not defined $result) {
328 0           my $error = $self->{'db'}->errstr();
329 0           die "db_execute: $error . on query: $sql";
330             }
331             # return statement handler
332 0           return $sth;
333             }
334              
335             #########################
336             # get a single value (one row) from db
337             # param: sql: String, data: ARRAY_OF_String
338             # return: String | ARRAY_OF_String
339             # added: jseibert, 30.05.2006
340             #########################
341             sub _db_singlevalue {
342 0     0     my $self = shift;
343 0           my $sql = shift;
344 0           my $sth = $self->_db_query($sql, @_);
345            
346 0           my @values = $sth->fetchrow_array();
347             # return eather one value or the whole row
348 0 0         return (wantarray) ? @values : $values[0];
349             }
350              
351             #########################
352             # send insert/update/delete (no result data)
353             # param: sql: String
354             # return: Boolean
355             # added: jseibert, 30.05.2006
356             #########################
357             sub _db_do {
358 0     0     my $self = shift;
359 0           my $sql = shift;
360 0           my $sth = $self->_db_query($sql, @_);
361 0           return 1;
362             }
363              
364             1;
365             __END__