]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/SearchIdx.pm
lei mark: command for (un)setting keywords and labels
[public-inbox.git] / lib / PublicInbox / SearchIdx.pm
index 772f5a644a5b0f580e531c78d3c4971d6915d2bf..7d46489c00202d19cde1d203e18c5c34f26559b6 100644 (file)
@@ -11,6 +11,7 @@ use strict;
 use v5.10.1;
 use parent qw(PublicInbox::Search PublicInbox::Lock Exporter);
 use PublicInbox::Eml;
+use PublicInbox::Search qw(xap_terms);
 use PublicInbox::InboxWritable;
 use PublicInbox::MID qw(mids_for_index mids);
 use PublicInbox::MsgIter;
@@ -34,6 +35,7 @@ use constant DEBUG => !!$ENV{DEBUG};
 my $xapianlevels = qr/\A(?:full|medium)\z/;
 my $hex = '[a-f0-9]';
 my $OID = $hex .'{40,}';
+my @VMD_MAP = (kw => 'K', label => 'L');
 our $INDEXLEVELS = qr/\A(?:full|medium|basic)\z/;
 
 sub new {
@@ -428,7 +430,15 @@ sub eml2doc ($$$;$) {
 sub add_xapian ($$$$) {
        my ($self, $eml, $smsg, $mids) = @_;
        begin_txn_lazy($self);
+       my $merge_vmd = delete $smsg->{-merge_vmd};
        my $doc = eml2doc($self, $eml, $smsg, $mids);
+       if (my $old = $merge_vmd ? _get_doc($self, $smsg->{num}) : undef) {
+               my @x = @VMD_MAP;
+               while (my ($field, $pfx) = splice(@x, 0, 2)) {
+                       my $vals = xap_terms($pfx, $old);
+                       $doc->add_boolean_term($pfx.$_) for keys %$vals;
+               }
+       }
        $self->{xdb}->replace_document($smsg->{num}, $doc);
 }
 
@@ -494,7 +504,10 @@ sub add_eidx_info {
        begin_txn_lazy($self);
        my $doc = _get_doc($self, $docid) or return;
        term_generator($self)->set_document($doc);
+
+       # '.' is special for lei_store
        $doc->add_boolean_term('O'.$eidx_key) if $eidx_key ne '.';
+
        index_list_id($self, $doc, $eml);
        $self->{xdb}->replace_document($docid, $doc);
 }
@@ -528,47 +541,85 @@ sub remove_eidx_info {
        $self->{xdb}->replace_document($docid, $doc);
 }
 
-sub set_keywords {
-       my ($self, $docid, @kw) = @_;
+sub set_vmd {
+       my ($self, $docid, $vmd) = @_;
        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);
+       my ($end, @rm, @add);
+       my @x = @VMD_MAP;
+       while (my ($field, $pfx) = splice(@x, 0, 2)) {
+               my $set = $vmd->{$field} // next;
+               my %keep = map { $_ => 1 } @$set;
+               my %add = %keep;
+               $end //= $doc->termlist_end;
+               for (my $cur = $doc->termlist_begin; $cur != $end; $cur++) {
+                       $cur->skip_to($pfx);
+                       last if $cur == $end;
+                       my $v = $cur->get_termname;
+                       $v =~ s/\A$pfx//s or next;
+                       $keep{$v} ? delete($add{$v}) : push(@rm, $pfx.$v);
+               }
+               push(@add, map { $pfx.$_ } keys %add);
+       }
+       return unless scalar(@rm) || scalar(@add);
+       $doc->remove_term($_) for @rm;
+       $doc->add_boolean_term($_) for @add;
        $self->{xdb}->replace_document($docid, $doc);
 }
 
-sub add_keywords {
-       my ($self, $docid, @kw) = @_;
+sub add_vmd {
+       my ($self, $docid, $vmd) = @_;
        begin_txn_lazy($self);
        my $doc = _get_doc($self, $docid) or return;
-       $doc->add_boolean_term('K'.$_) for @kw;
+       my @x = @VMD_MAP;
+       while (my ($field, $pfx) = splice(@x, 0, 2)) {
+               my $add = $vmd->{$field} // next;
+               $doc->add_boolean_term($pfx . $_) for @$add;
+       }
        $self->{xdb}->replace_document($docid, $doc);
 }
 
-sub remove_keywords {
-       my ($self, $docid, @kw) = @_;
+sub remove_vmd {
+       my ($self, $docid, $vmd) = @_;
        begin_txn_lazy($self);
        my $doc = _get_doc($self, $docid) or return;
        my $replace;
-       eval {
-               $doc->remove_term('K'.$_);
-               $replace = 1
-       } for @kw;
+       my @x = @VMD_MAP;
+       while (my ($field, $pfx) = splice(@x, 0, 2)) {
+               my $rm = $vmd->{$field} // next;
+               for (@$rm) {
+                       eval {
+                               $doc->remove_term($pfx . $_);
+                               $replace = 1;
+                       };
+               }
+       }
        $self->{xdb}->replace_document($docid, $doc) if $replace;
 }
 
+sub update_vmd {
+       my ($self, $docid, $vmd_mod) = @_;
+       begin_txn_lazy($self);
+       my $doc = _get_doc($self, $docid) or return;
+       my $updated = 0;
+       my @x = @VMD_MAP;
+       while (my ($field, $pfx) = splice(@x, 0, 2)) {
+               # field: "label" or "kw"
+               for my $val (@{$vmd_mod->{"-$field"} // []}) {
+                       eval {
+                               $doc->remove_term($pfx . $val);
+                               ++$updated;
+                       };
+               }
+               for my $val (@{$vmd_mod->{"+$field"} // []}) {
+                       $doc->add_boolean_term($pfx . $val);
+                       ++$updated;
+               }
+       }
+       $self->{xdb}->replace_document($docid, $doc) if $updated;
+       $updated;
+}
+
 sub xdb_remove {
        my ($self, @docids) = @_;
        $self->begin_txn_lazy;