bug fixes: date limit now works again, correct number of displayed results,
[BackupPC.git] / lib / BackupPC / SearchLib.pm
index fd019e7..d0d27d0 100644 (file)
@@ -15,104 +15,132 @@ 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 $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
-       my $sth = $dbh->prepare(qq{ SELECT id, share FROM shares ORDER BY share} );
+       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;
        }
-       $dbh->disconnect();
        return @ret;
 }
 
 sub epoch_to_iso {
        my $t = shift || return;
-       my $iso = BackupPC::Lib::timeStamp($t);
+       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, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
 
+       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 $offset = $param->{'offset'} || 0;
+       $offset *= $on_page;
 
-       my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
+       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,
                files.date                      AS date,
-               files.type                      AS filetype,
-               files.size                      AS size,
-               -- dvds.name                    AS dvd
-               null                            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 AND backups.shareID = shares.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{
@@ -122,10 +150,7 @@ sub getFiles($$) {
        };
 
        my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
-       my $sql_results = qq{ select $sql_cols $sql_from $sql_dvd_from $sql_where $sql_order };
-
-       $offset ||= 0;
-       $offset = ($offset * $on_page);
+       my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
 
        my $sth = $dbh->prepare($sql_count);
        $sth->execute();
@@ -143,44 +168,92 @@ sub getFiles($$) {
        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
                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,
                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
-       WHERE 
-               files.dvdid     IS NULL
+               INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
        GROUP BY 
                backups.hostID, backups.num
        ORDER BY min(backups.date)
@@ -266,13 +339,13 @@ 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>' .
@@ -289,13 +362,22 @@ 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 = "";
  
        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);
@@ -314,19 +396,13 @@ sub displayGrid($$$$) {
        }
 
 
-       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">};
-       }
-
-
        $retHTML .= qq{
        <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>
@@ -351,17 +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 .= qq{<tr class="fviewborder">};
 
+               $retHTML .= qq{<td>$i</td>};
+
                $retHTML .=
-                       qq{<td class="fviewborder" align="right">} . $file->{'sharename'} . qq{</td>} .
-                       qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'fpath'}, $hilite ) . qq{</td>} .
-                       qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupno'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'fpath'} )}, $file->{'backupno'} ) . qq{</td>} .
+                       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">} . $file->{'dvd'} . qq{</td>};
+                       qq{<td class="fviewborder">} . '?' . qq{</td>};
 
                $retHTML .= "</tr>";
        }
@@ -376,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) {
@@ -406,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;
 }