]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/SearchIdx.pm
searchidx: index lower-case List-Id value
[public-inbox.git] / lib / PublicInbox / SearchIdx.pm
index 098fead78ccf0774cf0ea41c513242ce1c215d6d..2c5c815f605755da79e5d551b088331b6bee5e86 100644 (file)
@@ -17,6 +17,7 @@ use PublicInbox::MsgIter;
 use PublicInbox::IdxStack;
 use Carp qw(croak);
 use POSIX qw(strftime);
+use Time::Local qw(timegm);
 use PublicInbox::OverIdx;
 use PublicInbox::Spawn qw(spawn nodatacow_dir);
 use PublicInbox::Git qw(git_unquote);
@@ -69,8 +70,8 @@ sub new {
        if ($version == 1) {
                $self->{lock_path} = "$inboxdir/ssoma.lock";
                my $dir = $self->xdir;
-               $self->{over} = PublicInbox::OverIdx->new("$dir/over.sqlite3");
-               $self->{over}->{-no_fsync} = 1 if $ibx->{-no_fsync};
+               $self->{oidx} = PublicInbox::OverIdx->new("$dir/over.sqlite3");
+               $self->{oidx}->{-no_fsync} = 1 if $ibx->{-no_fsync};
        } elsif ($version == 2) {
                defined $shard or die "shard is required for v2\n";
                # shard is a number
@@ -104,6 +105,7 @@ sub load_xapian_writable () {
        }
        eval 'require '.$X->{WritableDatabase} or die;
        *sortable_serialise = $xap.'::sortable_serialise';
+       *sortable_unserialise = $xap.'::sortable_unserialise';
        $DB_CREATE_OR_OPEN = eval($xap.'::DB_CREATE_OR_OPEN()');
        $DB_OPEN = eval($xap.'::DB_OPEN()');
        my $ver = (eval($xap.'::major_version()') << 16) |
@@ -131,14 +133,13 @@ sub idx_acquire {
                                ($is_shard && need_xapian($self)))) {
                        File::Path::mkpath($dir);
                        nodatacow_dir($dir);
+                       $self->{-set_has_threadid_once} = 1;
                }
        }
        return unless defined $flag;
        $flag |= $DB_NO_SYNC if $self->{ibx}->{-no_fsync};
        my $xdb = eval { ($X->{WritableDatabase})->new($dir, $flag) };
-       if ($@) {
-               die "Failed opening $dir: ", $@;
-       }
+       croak "Failed opening $dir: $@" if $@;
        $self->{xdb} = $xdb;
 }
 
@@ -339,7 +340,7 @@ sub index_ids ($$$$) {
        $doc->add_boolean_term('Q' . $_) for @$mids;
        for my $l ($hdr->header_raw('List-Id')) {
                $l =~ /<([^>]+)>/ or next;
-               my $lid = $1;
+               my $lid = lc $1;
                $doc->add_boolean_term('G' . $lid);
                index_text($self, $lid, 1, 'XL'); # probabilistic
        }
@@ -356,6 +357,7 @@ sub add_xapian ($$$$) {
        add_val($doc, PublicInbox::Search::DT(), $dt);
        add_val($doc, PublicInbox::Search::BYTES(), $smsg->{bytes});
        add_val($doc, PublicInbox::Search::UID(), $smsg->{num});
+       add_val($doc, PublicInbox::Search::THREADID, $smsg->{tid});
 
        my $tg = term_generator($self);
        $tg->set_document($doc);
@@ -419,8 +421,8 @@ sub add_message {
                # of the fields which exist in over.sqlite3.  We may stop
                # storing doc_data in Xapian sometime after we get multi-inbox
                # search working.
-               if (my $over = $self->{over}) { # v1 only
-                       $over->add_overview($mime, $smsg);
+               if (my $oidx = $self->{oidx}) { # v1 only
+                       $oidx->add_overview($mime, $smsg);
                }
                if (need_xapian($self)) {
                        add_xapian($self, $mime, $smsg, $mids);
@@ -434,6 +436,23 @@ sub add_message {
        $smsg->{num};
 }
 
+sub get_val ($$) {
+       my ($doc, $col) = @_;
+       sortable_unserialise($doc->get_value($col));
+}
+
+sub smsg_from_doc ($) {
+       my ($doc) = @_;
+       my $data = $doc->get_data or return;
+       my $smsg = bless {}, 'PublicInbox::Smsg';
+       $smsg->{ts} = get_val($doc, PublicInbox::Search::TS());
+       my $dt = get_val($doc, PublicInbox::Search::DT());
+       my ($yyyy, $mon, $dd, $hh, $mm, $ss) = unpack('A4A2A2A2A2A2', $dt);
+       $smsg->{ds} = timegm($ss, $mm, $hh, $dd, $mon - 1, $yyyy);
+       $smsg->load_from_data($data);
+       $smsg;
+}
+
 sub xdb_remove {
        my ($self, $oid, @removed) = @_;
        my $xdb = $self->{xdb} or return;
@@ -444,10 +463,9 @@ sub xdb_remove {
                        warn "E: #$num $oid missing in Xapian\n";
                        next;
                }
-               my $smsg = bless {}, 'PublicInbox::Smsg';
-               $smsg->load_expand($doc);
-               my $blob = $smsg->{blob} // '(unset)';
-               if ($blob eq $oid) {
+               my $smsg = smsg_from_doc($doc);
+               my $blob = $smsg->{blob}; # may be undef if --skip-docdata
+               if (!defined($blob) || $blob eq $oid) {
                        $xdb->delete_document($num);
                } else {
                        warn "E: #$num $oid != $blob in Xapian\n";
@@ -457,7 +475,7 @@ sub xdb_remove {
 
 sub remove_by_oid {
        my ($self, $oid, $num) = @_;
-       die "BUG: remove_by_oid is v2-only\n" if $self->{over};
+       die "BUG: remove_by_oid is v2-only\n" if $self->{oidx};
        $self->begin_txn_lazy;
        xdb_remove($self, $oid, $num) if need_xapian($self);
 }
@@ -479,13 +497,9 @@ sub unindex_eml {
        my $nr = 0;
        my %tmp;
        for my $mid (@$mids) {
-               my @removed = eval { $self->{over}->remove_oid($oid, $mid) };
-               if ($@) {
-                       warn "E: failed to remove <$mid> from overview: $@\n";
-               } else {
-                       $nr += scalar @removed;
-                       $tmp{$_}++ for @removed;
-               }
+               my @removed = $self->{oidx}->remove_oid($oid, $mid);
+               $nr += scalar @removed;
+               $tmp{$_}++ for @removed;
        }
        if (!$nr) {
                $mids = join('> <', @$mids);
@@ -507,9 +521,9 @@ sub index_mm {
        my $mids = mids($mime);
        my $mm = $self->{mm};
        if ($sync->{reindex}) {
-               my $over = $self->{over};
+               my $oidx = $self->{oidx};
                for my $mid (@$mids) {
-                       my ($num, undef) = $over->num_mid0_for_oid($oid, $mid);
+                       my ($num, undef) = $oidx->num_mid0_for_oid($oid, $mid);
                        return $num if defined $num;
                }
                $mm->num_for($mids->[0]) // $mm->mid_insert($mids->[0]);
@@ -589,13 +603,21 @@ sub v1_checkpoint ($$;$) {
 
        $self->{mm}->{dbh}->commit;
        if ($newest && need_xapian($self)) {
-               my $cur = $self->{xdb}->get_metadata('last_commit');
+               my $xdb = $self->{xdb};
+               my $cur = $xdb->get_metadata('last_commit');
                if (need_update($self, $cur, $newest)) {
-                       $self->{xdb}->set_metadata('last_commit', $newest);
+                       $xdb->set_metadata('last_commit', $newest);
+               }
+
+               # let SearchView know a full --reindex was done so it can
+               # generate ->has_threadid-dependent links
+               if ($sync->{reindex} && !ref($sync->{reindex})) {
+                       my $n = $xdb->get_metadata('has_threadid');
+                       $xdb->set_metadata('has_threadid', '1') if $n ne '1';
                }
        }
 
-       $self->{over}->rethread_done($sync->{-opt}) if $newest; # all done
+       $self->{oidx}->rethread_done($sync->{-opt}) if $newest; # all done
        commit_txn_lazy($self);
        $self->{ibx}->git->cleanup;
        my $nr = ${$sync->{nr}};
@@ -765,7 +787,7 @@ sub _index_sync {
        my $pr = $opt->{-progress};
        my $sync = { reindex => $opt->{reindex}, -opt => $opt };
        my $xdb = $self->begin_txn_lazy;
-       $self->{over}->rethread_prepare($opt);
+       $self->{oidx}->rethread_prepare($opt);
        my $mm = _msgmap_init($self);
        if ($sync->{reindex}) {
                my $last = $mm->last_commit;
@@ -796,7 +818,7 @@ sub DESTROY {
 sub _begin_txn {
        my ($self) = @_;
        my $xdb = $self->{xdb} || idx_acquire($self);
-       $self->{over}->begin_lazy if $self->{over};
+       $self->{oidx}->begin_lazy if $self->{oidx};
        $xdb->begin_transaction if $xdb;
        $self->{txn} = 1;
        $xdb;
@@ -815,6 +837,9 @@ sub set_metadata_once {
        return if $self->{shard}; # only continue if undef or 0, not >0
        my $xdb = $self->{xdb};
 
+       if (delete($self->{-set_has_threadid_once})) {
+               $xdb->set_metadata('has_threadid', '1');
+       }
        if (delete($self->{-set_indexlevel_once})) {
                my $level = $xdb->get_metadata('indexlevel');
                if (!$level || $level ne 'medium') {
@@ -833,7 +858,7 @@ sub _commit_txn {
                set_metadata_once($self);
                $xdb->commit_transaction;
        }
-       $self->{over}->commit_lazy if $self->{over};
+       $self->{oidx}->commit_lazy if $self->{oidx};
 }
 
 sub commit_txn_lazy {