- 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";
+ 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 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;
+ };