bug fixes: date limit now works again, correct number of displayed results,
[BackupPC.git] / lib / BackupPC / SearchLib.pm
index 07a43cf..d0d27d0 100644 (file)
@@ -15,186 +15,257 @@ my $pager_pages = 10;
 my $dsn = $Conf{SearchDSN};
 my $db_user = $Conf{SearchUser} || '';
 
+my $index_path = $Conf{HyperEstraierIndex};
+if ($index_path) {
+       $index_path = $TopDir . '/' . $index_path;
+       $index_path =~ s#//#/#g;
+}
+
+my $dbh;
+
+sub get_dbh {
+       $dbh ||= DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
+       return $dbh;
+}
+
 sub getUnits() {
-    my @ret = ();
-    my $tmp;
-    my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
-    my $st =
-      $dbh->prepare(
-        " SELECT shares.ID AS ID, shares.share AS name FROM shares;");
-    $st->execute();
-    push (@ret, { 'ID' => '', 'name' => '-'});
-    while ( $tmp = $st->fetchrow_hashref() ) {
-        push( @ret, { 'ID' => $tmp->{'ID'}, 'name' => $tmp->{'name'} } );
-    }
-    $dbh->disconnect();
-    return @ret;
+       my @ret;
+
+       my $dbh = get_dbh();
+       my $sth = $dbh->prepare(qq{
+               SELECT
+                       shares.id       as id,
+                       hosts.name || ':' || shares.name as share
+               FROM shares
+               JOIN hosts on hostid = hosts.id
+               ORDER BY share
+       } );
+       $sth->execute();
+       push @ret, { 'id' => '', 'share' => '-'};       # dummy any
+
+       while ( my $row = $sth->fetchrow_hashref() ) {
+               push @ret, $row;
+       }
+       return @ret;
 }
 
 sub epoch_to_iso {
        my $t = shift || return;
-       my $dt = DateTime->from_epoch( epoch => $t ) || return;
-print STDERR "$t == ",$dt->epoch,"\n";
-       return $dt->ymd . ' ' . $dt->hms;
+       my $iso = BackupPC::Lib::timeStamp(undef, $t);
+       $iso =~ s/\s/ /g;
+       return $iso;
 }
 
-sub getWhere($) {
-       my ($param)    = @_;
-       my @conditions;
+sub dates_from_form($) {
+       my $param = shift || return;
 
        sub mk_epoch_date($$) {
                my ($name,$suffix) = @_;
 
-               my $yyyy = $param->{ $name . '_year_' . $suffix} || return;
+               my $yyyy = $param->{ $name . '_year_' . $suffix} || return undef;
                my $mm .= $param->{ $name . '_month_' . $suffix} ||
                        ( $suffix eq 'from' ? 1 : 12);
                my $dd .= $param->{ $name . '_day_' . $suffix} ||
                        ( $suffix eq 'from' ? 1 : 31);
+
+               $yyyy =~ s/\D//g;
+               $mm =~ s/\D//g;
+               $dd =~ s/\D//g;
+
                my $dt = new DateTime(
                        year => $yyyy,
                        month => $mm,
                        day => $dd
                );
+               print STDERR "mk_epoch_date($name,$suffix) [$yyyy-$mm-$dd] = " . $dt->ymd . " " . $dt->hms . "\n";
                return $dt->epoch || 'NULL';
        }
 
-       my $backup_from = mk_epoch_date('search_backup', 'from');
+       my @ret = (
+               mk_epoch_date('search_backup', 'from'),
+               mk_epoch_date('search_backup', 'to'),
+               mk_epoch_date('search', 'from'),
+               mk_epoch_date('search', 'to'),
+       );
+
+       return @ret;
+
+}
+
+
+sub getWhere($) {
+       my $param = shift || return;
+
+       my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
+
+       my @conditions;
        push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
-       my $backup_to = mk_epoch_date('search_backup', 'to');
        push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
-
-       my $files_from = mk_epoch_date('search', 'from');
        push @conditions, qq{ files.date >= $files_from } if ($files_from);
-       my $files_to = mk_epoch_date('search', 'to');
        push @conditions, qq{ files.date <= $files_to } if ($files_to);
 
-       print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:",join(" | ",@conditions);
-    
-       push( @conditions, ' backups.hostID = ' . $param->{'search_host'} ) if ($param->{'search_host'});
+       print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" | ",@conditions);
 
-       push (@conditions, " upper(files.name) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
+       push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
+       push (@conditions, " upper(files.path) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
 
-       return (
-               join(" and ", @conditions),
-               $files_from, $files_to,
-               $backup_from, $backup_to
-       );
+       return join(" and ", @conditions);
 }
 
 
-sub getFiles($$) {
-       my ($where, $offset) = @_;
+sub getFiles($) {
+       my ($param) = @_;
 
-       my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
+       my $offset = $param->{'offset'} || 0;
+       $offset *= $on_page;
+
+       my $dbh = get_dbh();
 
        my $sql_cols = qq{
                files.id                        AS fid,
                hosts.name                      AS hname,
                shares.name                     AS sname,
-               shares.share                    AS sharename,
-               files.backupNum                 AS backupNum,
-               files.name                      AS filename,
+               files.backupnum                 AS backupnum,
                files.path                      AS filepath,
-               shares.share||files.fullpath    AS networkPath,
                files.date                      AS date,
-               files.type                      AS filetype,
-               files.size                      AS size,
-       };
-
-       my $sql_dvd_cols = qq{
-               dvds.name                       AS dvd
+               files.type                      AS type,
+               files.size                      AS size
        };
 
        my $sql_from = qq{
                FROM files 
                        INNER JOIN shares       ON files.shareID=shares.ID
                        INNER JOIN hosts        ON hosts.ID = shares.hostID
-                       INNER JOIN backups      ON backups.num = files.backupNum and backups.hostID = hosts.ID
-       };
-
-       my $sql_dvd_from = qq{
-                       LEFT  JOIN dvds         ON dvds.ID = files.dvdid
+                       INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
        };
 
        my $sql_where;
+       my $where = getWhere($param);
        $sql_where = " WHERE ". $where if ($where);
 
        my $sql_order = qq{
-               ORDER BY files.id
-                       LIMIT $on_page
-                       OFFSET ?
+               ORDER BY files.date
+               LIMIT $on_page
+               OFFSET ?
        };
 
-       $offset ||= 0;
-       $offset = ($offset * $on_page) + 1;
+       my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
+       my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
 
-       my $sth = $dbh->prepare(qq{ select count(files.id) $sql_from $sql_where });
+       my $sth = $dbh->prepare($sql_count);
        $sth->execute();
-
        my ($results) = $sth->fetchrow_array();
 
-       $sth = $dbh->prepare(qq{ select $sql_cols $sql_dvd_cols $sql_from $sql_dvd_from $sql_where $sql_order });
+       $sth = $dbh->prepare($sql_results);
        $sth->execute( $offset );
 
+       if ($sth->rows != $results) {
+               my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
+               $bug =~ s/\s+/ /gs;
+               print STDERR "$bug\n";
+       }
+
        my @ret;
       
        while (my $row = $sth->fetchrow_hashref()) {
-               push(@ret, { 
-                       'hname'         => $row->{'hname'}, 
-                       'sname'         => $row->{'sname'},
-                       'sharename'     => $row->{'sharename'},
-                       'backupno'      => $row->{'backupnum'},
-                       'fname'         => $row->{'filename'},
-                       'fpath'         => $row->{'filepath'},
-                       'networkpath'   => $row->{'networkpath'},
-                       'date'          => $row->{'date'},
-                       'type'          => $row->{'filetype'},
-                       'size'          => $row->{'size'},
-                       'id'            => $row->{'fid'},
-                       'dvd'           => $row->{'dvd'}
-               });
+               push @ret, $row;
        }
-      
+     
        $sth->finish();
-       $dbh->disconnect();
        return ($results, \@ret);
 }
 
+sub getFilesHyperEstraier($) {
+       my ($param) = @_;
+
+       my $offset = $param->{'offset'} || 0;
+       $offset *= $on_page;
+
+       die "no index_path?" unless ($index_path);
+
+       use HyperEstraier;
+
+       # open the database
+       my $db = HyperEstraier::Database->new();
+       $db->open($index_path, $HyperEstraier::ESTDBREADER);
+
+       # create a search condition object
+       my $cond = HyperEstraier::Condition->new();
+
+       my $q = $param->{'search_filename'};
+       my $shareid = $param->{'search_share'};
+
+       if ($q) {
+               $q =~ s/(.)/$1 /g;
+
+               # set the search phrase to the search condition object
+               $cond->set_phrase($q);
+       }
+
+       my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
+
+       $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
+       $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
+
+       $cond->add_attr("date NUMGE $files_from") if ($files_from);
+       $cond->add_attr("date NUMLE $files_to") if ($files_to);
+
+       $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
+
+#      $cond->set_max( $offset + $on_page );
+       $cond->set_options( $HyperEstraier::Condition::SURE );
+       $cond->set_order( 'date NUMA' );
+
+       # get the result of search
+       my $result = $db->search($cond, 0);
+
+       my @res;
+       my $hits = $result->size;
+
+       # for each document in result
+       for my $i ($offset .. ($offset + $on_page - 1)) {
+               last if ($i >= $hits);
+
+               my $id = $result->get($i);
+               my $doc = $db->get_doc($id, 0);
+
+               my $row;
+               foreach my $c (qw/fid hname sname backupnum fiilename filepath date type size/) {
+                       $row->{$c} = $doc->attr($c);
+               }
+               push @res, $row;
+       }
+
+       return ($hits, \@res);
+}
+
 sub getBackupsNotBurned() {
 
-       my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
+       my $dbh = get_dbh();
        my $sql = q{ 
        SELECT
-               hosts.ID                AS hostid,
+               backups.hostID          AS hostid,
                min(hosts.name)         AS host,
-               backups.num             AS backupno,
+               backups.num             AS backupnum,
                min(backups.type)       AS type,
-               min(backups.date)       AS date
-       FROM backups, shares, files, hosts
-       WHERE 
-               backups.num     = files.backupNum       AND
-               shares.ID       = files.shareID         AND         
-               backups.hostID  = shares.hostID         AND
-               hosts.ID        = backups.hostID        AND
-               files.dvdid     IS NULL
+               min(backups.date)       AS date,
+               min(backups.size)       AS size
+       FROM files 
+               INNER JOIN shares       ON files.shareID=shares.ID
+               INNER JOIN hosts        ON hosts.ID = shares.hostID
+               INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
        GROUP BY 
-               backups.hostID, backups.num, hosts.id
+               backups.hostID, backups.num
        ORDER BY min(backups.date)
        };
        my $sth = $dbh->prepare( $sql );
        my @ret;
        $sth->execute();
 
-       while ( my $row = $sth->fetchrow_hashref() ) {      
-               push(@ret, { 
-                        'host'         => $row->{'host'},
-                        'hostid'       => $row->{'hostid'},
-                        'backupno'     => $row->{'backupno'},
-                        'type'         => $row->{'type'},
-                        'date'         => $row->{'date'},
-                        'age'          => sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) ),
-                      }
-               );
+       while ( my $row = $sth->fetchrow_hashref() ) {
+               $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
+               $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
+               push @ret, $row;
        }
       
        return @ret;      
@@ -232,17 +303,21 @@ EOF3
               $retHTML.= q{<input type="hidden" value="burn" name="action">};
               $retHTML .= q{<input type="hidden" value="results" name="search_results">};
        }
-       $retHTML .= qq{<table style="fview"><tr>};
+       $retHTML .= qq{
+               <table style="fview" border="1" cellspacing="0" cellpadding="3">
+               <tr class="tableheader">
+       };
 
        if ($addForm) {
            $retHTML .= "<td class=\"tableheader\"><input type=\"checkbox\" name=\"allFiles\" onClick=\"checkAll('allFiles');\"></td>";
        }
        $retHTML .=  qq{
-               <td class="tableheader">Host</td>
-               <td class="tableheader">Backup no</td>
-               <td class="tableheader">Type</td>
-               <td class="tableheader">date</td>
-               <td class="tableheader">age/days</td>
+               <td align="center">Host</td>
+               <td align="center">Backup no</td>
+               <td align="center">Type</td>
+               <td align="center">date</td>
+               <td align="center">age/days</td>
+               <td align="center">size/MB</td>
                </tr>
        };
 
@@ -264,16 +339,17 @@ EOF3
                $retHTML .= "<tr>";
                if ($addForm) {
                        $retHTML .= '<td class="fview"><input type="checkbox" name="fcb' .
-                               $backup->{'hostid'}.'_'.$backup->{'backupno'} . 
-                               '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupno'} .
+                               $backup->{'hostid'}.'_'.$backup->{'backupnum'} . 
+                               '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
                                '"></td>';
                }           
            
                $retHTML .= '<td class="fviewborder">' . $backup->{'host'} . '</td>' .
-                       '<td class="fviewborder">' . $backup->{'backupno'} . '</td>' .
+                       '<td class="fviewborder">' . $backup->{'backupnum'} . '</td>' .
                        '<td class="fviewborder">' . $backup->{'type'} . '</td>' .
                        '<td class="fviewborder">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
                        '<td class="fviewborder">' . $backup->{'age'} . '</td>' .
+                       '<td class="fviewborder">' . $backup->{'size'} . '</td>' .
                        '</tr>';
        }
 
@@ -286,36 +362,53 @@ EOF3
        return $retHTML;
 }      
 
-sub displayGrid($$$$) {
-       my ($where, $addForm, $offset, $hilite) = @_;
+sub displayGrid($) {
+       my ($param) = @_;
+
+       my $offset = $param->{'offset'};
+       my $hilite = $param->{'search_filename'};
+
        my $retHTML = "";
  
-       if ($addForm) {
-               $retHTML .= qq{<form name="forma" method="GET" action="$MyURL">};
-               $retHTML.= qq{<input type="hidden" value="search" name="action">};
-               $retHTML .= qq{<input type="hidden" value="results" name="search_results">};
-       }
-
        my $start_t = time();
 
-       my ($results, $files) = getFiles($where, $offset);
+       my ($results, $files);
+       if ($param->{'use_hest'}) {
+               ($results, $files) = getFilesHyperEstraier($param);
+       } else {
+               ($results, $files) = getFiles($param);
+       }
 
        my $dur_t = time() - $start_t;
        my $dur = sprintf("%0.4fs", $dur_t);
 
        my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
 
+       if ($results <= 0) {
+               $retHTML .= qq{
+                       <p style="color: red;">No results found...</p>
+               };
+               return $retHTML;
+       } else {
+               # DEBUG
+               #use Data::Dumper;
+               #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
+       }
+
+
        $retHTML .= qq{
-       <br/>Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
-       <table style="fview" width="100%">
-               <tr> 
-               <td class="tableheader">Share</td>
-               <td class="tableheader">Name</td>
-               <td class="tableheader">Type</td>
-               <td class="tableheader">#</td>
-               <td class="tableheader">Size</td>
-               <td class="tableheader">Date</td>
-               <td class="tableheader">Media</td>
+       <div>
+       Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
+       </div>
+       <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
+               <tr class="fviewheader"> 
+               <td></td>
+               <td align="center">Share</td>
+               <td align="center">Type and Name</td>
+               <td align="center">#</td>
+               <td align="center">Size</td>
+               <td align="center">Date</td>
+               <td align="center">Media</td>
                </tr>
        };
 
@@ -334,21 +427,23 @@ sub displayGrid($$$$) {
                return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
        }
 
+       my $i = $offset * $on_page;
+
        foreach $file (@{ $files }) {
+               $i++;
+
                my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
-               $retHTML .= "<tr>";
+               $retHTML .= qq{<tr class="fviewborder">};
 
-               foreach my $v ((
-                       $file->{'sharename'},
-                       qq{<img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" align="center">&nbsp;} . hilite_html( $file->{'fpath'}, $hilite ),
-                       $typeStr,
-                       restore_link( $typeStr, $file->{'hname'}, $file->{'backupno'}, $file->{'sname'}, $file->{'fpath'}, $file->{'backupno'} ),
-                       $file->{'size'},
-                       epoch_to_iso( $file->{'date'} ),
-                       $file->{'dvd'}
-               )) {
-                       $retHTML .= qq{<td class="fviewborder">$v</td>};
-               }
+               $retHTML .= qq{<td>$i</td>};
+
+               $retHTML .=
+                       qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
+                       qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
+                       qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
+                       qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
+                       qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
+                       qq{<td class="fviewborder">} . '?' . qq{</td>};
 
                $retHTML .= "</tr>";
        }
@@ -363,19 +458,33 @@ sub displayGrid($$$$) {
        my $max_page = int( $results / $on_page );
        my $page = 0;
 
-       my $link_fmt = '<a href = "#" onclick="document.forma.offset.value=%d;document.forma.submit();">%s</a>';
+       sub page_link($$$) {
+               my ($param,$page,$display) = @_;
+
+               $param->{'offset'} = $page;
+
+               my $html = '<a href = "' . $MyURL;
+               my $del = '?';
+               foreach my $k (keys %{ $param }) {
+                       if ($param->{$k}) {
+                               $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
+                               $del = '&';
+                       }
+               }
+               $html .= '">' . $display . '</a>';
+       }
 
        $retHTML .= '<div style="text-align: center;">';
 
        if ($offset > 0) {
-               $retHTML .= sprintf($link_fmt, $offset - 1, '&lt;&lt;') . ' ';
+               $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
        }
 
        while ($page <= $max_page) {
                if ($page == $offset) {
                        $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
                } else {
-                       $retHTML .= $del . sprintf($link_fmt, $page, $page + 1);
+                       $retHTML .= $del . page_link($param, $page, $page + 1);
                }
 
                if ($page < $offset - $pager_pages && $page != 0) {
@@ -393,13 +502,11 @@ sub displayGrid($$$$) {
        }
 
        if ($offset < $max_page) {
-               $retHTML .= ' ' . sprintf($link_fmt, $offset + 1, '&gt;&gt;');
+               $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
        }
 
        $retHTML .= "</div>";
 
-       $retHTML .= "</form>" if ($addForm);
-
        return $retHTML;
 }