-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 apply_vmd_mod ($$) {
+ my ($doc, $vmd_mod) = @_;
+ my $updated = 0;
+ my @x = @VMD_MAP;
+ while (my ($field, $pfx) = splice(@x, 0, 2)) {
+ # field: "L" 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;
+ }
+ }
+ $updated;
+}
+
+sub add_vmd {
+ my ($self, $docid, $vmd) = @_;
+ begin_txn_lazy($self);
+ my $doc = _get_doc($self, $docid) or return;
+ my @x = @VMD_MAP;
+ my $updated = 0;
+ while (my ($field, $pfx) = splice(@x, 0, 2)) {
+ my $add = $vmd->{$field} // next;
+ $doc->add_boolean_term($pfx . $_) for @$add;
+ $updated += scalar(@$add);
+ }
+ $updated += apply_vmd_mod($doc, $vmd);
+ $self->{xdb}->replace_document($docid, $doc) if $updated;
+}
+
+sub remove_vmd {
+ my ($self, $docid, $vmd) = @_;
+ begin_txn_lazy($self);
+ my $doc = _get_doc($self, $docid) or return;
+ my $replace;
+ 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 = apply_vmd_mod($doc, $vmd_mod);
+ $self->{xdb}->replace_document($docid, $doc) if $updated;
+ $updated;