]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Search.pm
search: support downloading mboxes results with full thread
[public-inbox.git] / lib / PublicInbox / Search.pm
index 593040a8a285340a69465d8bd27fba357387f28a..bc820b648a46205d36f9fc8567e4e687483169de 100644 (file)
@@ -5,6 +5,8 @@
 # Read-only search interface for use by the web and NNTP interfaces
 package PublicInbox::Search;
 use strict;
+use parent qw(Exporter);
+our @EXPORT_OK = qw(mdocid);
 
 # values for searching, changing the numeric value breaks
 # compatibility with old indices (so don't change them it)
@@ -16,9 +18,9 @@ use constant {
        # added for public-inbox 1.6.0+
        BYTES => 3, # IMAP RFC822.SIZE
        UID => 4, # IMAP UID == NNTP article number == Xapian docid
+       THREADID => 5, # RFC 8474, RFC 8621
 
        # TODO
-       # THREADID => ?
        # REPLYCNT => ?, # IMAP ANSWERED
 
        # SCHEMA_VERSION history
@@ -45,7 +47,7 @@ use constant {
        #      public-inbox v1.5.0 adds (still SCHEMA_VERSION=15):
        #      * "lid:" and "l:" for List-Id searches
        #
-       #      v1.6.0 adds BYTES and UID values
+       #      v1.6.0 adds BYTES, UID and THREADID values
        SCHEMA_VERSION => 15,
 };
 
@@ -217,6 +219,12 @@ sub mdocid {
        int(($docid - 1) / $nshard) + 1;
 }
 
+sub mset_to_artnums {
+       my ($self, $mset) = @_;
+       my $nshard = $self->{nshard} // 1;
+       [ map { mdocid($nshard, $_) } $mset->items ];
+}
+
 sub xdb ($) {
        my ($self) = @_;
        $self->{xdb} ||= do {
@@ -263,7 +271,7 @@ sub query {
        if ($query_string eq '' && !$opts->{mset}) {
                $self->{over_ro}->recent($opts);
        } else {
-               my $qp = qp($self);
+               my $qp = $self->{qp} //= qparse_new($self);
                my $qp_flags = $self->{qp_flags};
                my $query = $qp->parse_query($query_string, $qp_flags);
                $opts->{relevance} = 1 unless exists $opts->{relevance};
@@ -318,31 +326,35 @@ sub _enquire_once { # retry_reopen callback
        } else {
                $enquire->set_sort_by_value_then_relevance(TS, $desc);
        }
+
+       # `mairix -t / --threads' or JMAP collapseThreads
+       $enquire->set_collapse_key(THREADID) if $opts->{thread};
+
        my $offset = $opts->{offset} || 0;
        my $limit = $opts->{limit} || 50;
        my $mset = $enquire->get_mset($offset, $limit);
        return $mset if $opts->{mset};
-       my @msgs = map { PublicInbox::Smsg::from_mitem($_) } $mset->items;
-       return \@msgs unless wantarray;
-
-       ($mset->get_matches_estimated, \@msgs)
+       my $nshard = $self->{nshard} // 1;
+       my $i = 0;
+       my %order = map { mdocid($nshard, $_) => ++$i } $mset->items;
+       my @msgs = sort {
+               $order{$a->{num}} <=> $order{$b->{num}}
+       } @{$self->{over_ro}->get_all(keys %order)};
+       wantarray ? ($mset->get_matches_estimated, \@msgs) : \@msgs;
 }
 
 # read-write
 sub stemmer { $X{Stem}->new($LANG) }
 
 # read-only
-sub qp {
+sub qparse_new ($) {
        my ($self) = @_;
 
-       my $qp = $self->{query_parser};
-       return $qp if $qp;
        my $xdb = xdb($self);
-       # new parser
-       $qp = $X{QueryParser}->new;
+       my $qp = $X{QueryParser}->new;
        $qp->set_default_op(OP_AND());
        $qp->set_database($xdb);
-       $qp->set_stemmer($self->stemmer);
+       $qp->set_stemmer(stemmer($self));
        $qp->set_stemming_strategy(STEM_SOME());
        $qp->set_max_wildcard_expansion(100);
        my $nvrp = $X{NumberValueRangeProcessor};
@@ -380,13 +392,12 @@ EOF
        while (my ($name, $prefix) = each %prob_prefix) {
                $qp->add_prefix($name, $_) foreach split(/ /, $prefix);
        }
-
-       $self->{query_parser} = $qp;
+       $qp;
 }
 
 sub help {
        my ($self) = @_;
-       $self->qp; # parse altids
+       $self->{qp} //= qparse_new($self); # parse altids
        my @ret = @HELP;
        if (my $user_pfx = $self->{-user_pfx}) {
                push @ret, @$user_pfx;