]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/SearchIdx.pm
update copyrights for 2021
[public-inbox.git] / lib / PublicInbox / SearchIdx.pm
index cd8f4dd763b7bc20e1ee4ca0abe11ba1386e475d..88349faa38b7a9325a11900155d4715c9faa90de 100644 (file)
@@ -1,6 +1,6 @@
-# Copyright (C) 2015-2020 all contributors <meta@public-inbox.org>
+# Copyright (C) 2015-2021 all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
-# based on notmuch, but with no concept of folders, files or flags
+# based on notmuch, but with no concept of folders, files
 #
 # Indexes mail with Xapian and our (SQLite-based) ::Msgmap for use
 # with the web and NNTP interfaces.  This index maintains thread
@@ -54,20 +54,16 @@ sub new {
                }
        }
        $ibx = PublicInbox::InboxWritable->new($ibx);
-       my $self = bless {
-               ibx => $ibx,
-               xpfx => $inboxdir, # for xpfx_init
-               -altid => $altid,
-               ibx_ver => $version,
-               indexlevel => $indexlevel,
-       }, $class;
-       $self->xpfx_init;
+       my $self = PublicInbox::Search->new($ibx);
+       bless $self, $class;
+       $self->{ibx} = $ibx;
+       $self->{-altid} = $altid;
+       $self->{indexlevel} = $indexlevel;
        $self->{-set_indexlevel_once} = 1 if $indexlevel eq 'medium';
        if ($ibx->{-skip_docdata}) {
                $self->{-set_skip_docdata_once} = 1;
                $self->{-skip_docdata} = 1;
        }
-       $ibx->umask_prepare;
        if ($version == 1) {
                $self->{lock_path} = "$inboxdir/ssoma.lock";
                my $dir = $self->xdir;
@@ -106,7 +102,6 @@ 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) |
@@ -371,7 +366,7 @@ sub eml2doc ($$$;$) {
        index_headers($self, $smsg);
 
        if (defined(my $eidx_key = $smsg->{eidx_key})) {
-               $doc->add_boolean_term('O'.$eidx_key);
+               $doc->add_boolean_term('O'.$eidx_key) if $eidx_key ne '.';
        }
        msg_iter($eml, \&index_xapian, [ $self, $doc ]);
        index_ids($self, $doc, $eml, $mids);
@@ -408,7 +403,7 @@ sub add_xapian ($$$$) {
 
 sub _msgmap_init ($) {
        my ($self) = @_;
-       die "BUG: _msgmap_init is only for v1\n" if $self->{ibx_ver} != 1;
+       die "BUG: _msgmap_init is only for v1\n" if $self->{ibx}->version != 1;
        $self->{mm} //= eval {
                require PublicInbox::Msgmap;
                my $rw = $self->{ibx}->{-no_fsync} ? 2 : 1;
@@ -467,7 +462,7 @@ sub add_eidx_info {
        begin_txn_lazy($self);
        my $doc = _get_doc($self, $docid) or return;
        term_generator($self)->set_document($doc);
-       $doc->add_boolean_term('O'.$eidx_key);
+       $doc->add_boolean_term('O'.$eidx_key) if $eidx_key ne '.';
        index_list_id($self, $doc, $eml);
        $self->{xdb}->replace_document($docid, $doc);
 }
@@ -501,17 +496,53 @@ sub remove_eidx_info {
        $self->{xdb}->replace_document($docid, $doc);
 }
 
-sub get_val ($$) {
-       my ($doc, $col) = @_;
-       sortable_unserialise($doc->get_value($col));
+sub set_keywords {
+       my ($self, $docid, @kw) = @_;
+       begin_txn_lazy($self);
+       my $doc = _get_doc($self, $docid) or return;
+       my %keep = map { $_ => 1 } @kw;
+       my %add = %keep;
+       my @rm;
+       my $end = $doc->termlist_end;
+       for (my $cur = $doc->termlist_begin; $cur != $end; $cur++) {
+               $cur->skip_to('K');
+               last if $cur == $end;
+               my $kw = $cur->get_termname;
+               $kw =~ s/\AK//s or next;
+               $keep{$kw} ? delete($add{$kw}) : push(@rm, $kw);
+       }
+       return unless (scalar(@rm) + scalar(keys %add));
+       $doc->remove_term('K'.$_) for @rm;
+       $doc->add_boolean_term('K'.$_) for (keys %add);
+       $self->{xdb}->replace_document($docid, $doc);
+}
+
+sub add_keywords {
+       my ($self, $docid, @kw) = @_;
+       begin_txn_lazy($self);
+       my $doc = _get_doc($self, $docid) or return;
+       $doc->add_boolean_term('K'.$_) for @kw;
+       $self->{xdb}->replace_document($docid, $doc);
+}
+
+sub remove_keywords {
+       my ($self, $docid, @kw) = @_;
+       begin_txn_lazy($self);
+       my $doc = _get_doc($self, $docid) or return;
+       my $replace;
+       eval {
+               $doc->remove_term('K'.$_);
+               $replace = 1
+       } for @kw;
+       $self->{xdb}->replace_document($docid, $doc) if $replace;
 }
 
 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());
+       $smsg->{ts} = int_val($doc, PublicInbox::Search::TS());
+       my $dt = int_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);
@@ -617,10 +648,12 @@ sub index_both { # git->cat_async callback
        $size += crlf_adjust($$bref);
        my $smsg = bless { bytes => $size, blob => $oid }, 'PublicInbox::Smsg';
        my $self = $sync->{sidx};
+       local $self->{current_info} = "$self->{current_info}: $oid";
        my $eml = PublicInbox::Eml->new($bref);
        $smsg->{num} = index_mm($self, $eml, $oid, $sync) or
                die "E: could not generate NNTP article number for $oid";
        add_message($self, $eml, $smsg, $sync);
+       ++$self->{nidx};
        my $cur_cmt = $sync->{cur_cmt} // die 'BUG: {cur_cmt} missing';
        ${$sync->{latest_cmt}} = $cur_cmt;
 }
@@ -628,11 +661,14 @@ sub index_both { # git->cat_async callback
 sub unindex_both { # git->cat_async callback
        my ($bref, $oid, $type, $size, $sync) = @_;
        return if is_bad_blob($oid, $type, $size, $sync->{oid});
-       unindex_eml($sync->{sidx}, $oid, PublicInbox::Eml->new($bref));
+       my $self = $sync->{sidx};
+       local $self->{current_info} = "$self->{current_info}: $oid";
+       unindex_eml($self, $oid, PublicInbox::Eml->new($bref));
        # may be undef if leftover
        if (defined(my $cur_cmt = $sync->{cur_cmt})) {
                ${$sync->{latest_cmt}} = $cur_cmt;
        }
+       ++$self->{nidx};
 }
 
 sub with_umask {
@@ -872,6 +908,7 @@ sub _index_sync {
        my ($self, $opt) = @_;
        my $tip = $opt->{ref} || 'HEAD';
        my $ibx = $self->{ibx};
+       local $self->{current_info} = "$ibx->{inboxdir}";
        $self->{batch_bytes} = $opt->{batch_size} // $BATCH_BYTES;
        $ibx->git->batch_prepare;
        my $pr = $opt->{-progress};
@@ -1008,68 +1045,4 @@ SELECT COUNT(*) FROM over WHERE num = ?
        }
 }
 
-sub reindex_xap { # git->cat_async callback
-       my ($bref, $oid, $type, $size, $ary) = @_;
-       my ($ibx_id, $oidhex, $req, $more) = @$ary;
-       my $self = $req->{self} // die 'BUG: {self} missing';
-       my $eidx = $self->{eidx} // die 'BUG: {eidx} missing';
-       my $eidx_key = $self->{-eidx_key_for}->{$ibx_id} //
-                       die "BUG: bad ibx_id=$ibx_id ($oid)";
-
-       my $docid = $req->{docid};
-       local $eidx->{current_info} = "#$docid $oid";
-       return if is_bad_blob($oid, $type, $size, $oidhex);
-       if (my $doc = $req->{doc}) { # modify existing doc
-               $req->{tg_isset} //= do { # for existing documents in {xdb}
-                       term_generator($self)->set_document($doc);
-                       1;
-               };
-               $doc->add_boolean_term('O'.$eidx_key);
-               index_list_id($self, $doc, PublicInbox::Eml->new($bref));
-       } else { # first time seeing this doc
-               my $smsg = $self->{eidx}->over->get_art($docid) //
-                       die "BUG: #$docid ($oid) not in over";
-               $smsg->{bytes} = $size + crlf_adjust($$bref);
-               $smsg->{eidx_key} = $eidx_key;
-               my $eml = PublicInbox::Eml->new($bref);
-               $req->{doc} = eml2doc($self, $eml, $smsg);
-               $req->{tg_isset} = 1; # eml2doc calls $tg->set_document
-       }
-       return if $more;
-       my $doc = delete($req->{doc}) or return; # all bad blobs!
-       $eidx->{transact_bytes} += $size;
-       $self->{xdb}->replace_document($req->{docid}, $doc);
-}
-
-sub reindex_docid {
-       my ($self, $docid) = @_;
-       my $eidx = $self->{eidx} // die 'BUG: {eidx} missing';
-       my $eidx_key_for = $self->{-eidx_key_for} //= do {
-               my %eidx_key_for = map {
-                       $_->[0] => $_->[1];
-               } @{$eidx->over->dbh->selectall_arrayref(<<'')};
-SELECT ibx_id,eidx_key FROM inboxes
-
-               \%eidx_key_for;
-       };
-
-       begin_txn_lazy($self);
-       my $doc = eval { $self->{xdb}->get_document($docid) };
-       my $req = { doc => $doc, self => $self, docid => $docid };
-       my $sth = $eidx->over->dbh->prepare_cached(<<'', undef, 1);
-SELECT ibx_id,oidbin FROM xref3 WHERE docid = ? ORDER BY ibx_id ASC
-
-       $sth->execute($docid);
-       my $rows = $sth->fetchall_arrayref;
-       while (my $row = shift(@$rows)) {
-               my ($ibx_id, $oidbin) = @$row;
-               my $oidhex = unpack('H*', $oidbin);
-               $eidx->git->cat_async($oidhex, \&reindex_xap,
-                               [ $ibx_id, $oidhex, $req, scalar(@$rows) ]);
-       }
-       if ($eidx->{transact_bytes} >= $eidx->{batch_bytes}) {
-               commit_txn_lazy($self);
-       }
-}
-
 1;