]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Mbox.pm
www: favor reading more from SQLite, and less from Xapian
[public-inbox.git] / lib / PublicInbox / Mbox.pm
index 1b68f027157fc0d6b87f97724cdb0eece696fcea..4427ae5d86588056c6d7e9108314e8cfd6dff75f 100644 (file)
@@ -34,19 +34,17 @@ sub mb_stream {
 # called by PSGI server as body response
 sub getline {
        my ($more) = @_; # self
-       my ($ctx, $head, $tail, $db, $cur) = @$more;
-       if ($cur) {
+       my ($ctx, $id, $prev, $next, $cur) = @$more;
+       if ($cur) { # first
                pop @$more;
                return msg_str($ctx, $cur);
        }
-       for (; !defined($cur) && $head != $tail; $head++) {
-               my $smsg = PublicInbox::SearchMsg->get($head, $db, $ctx->{mid});
-               my $mref = $ctx->{-inbox}->msg_by_smsg($smsg) or next;
-               $cur = Email::Simple->new($mref);
-               $cur = msg_str($ctx, $cur);
-       }
-       $more->[1] = $head;
-       $cur;
+       $cur = $next or return;
+       my $ibx = $ctx->{-inbox};
+       $next = $ibx->search->next_by_mid($ctx->{mid}, \$id, \$prev);
+       @$more = ($ctx, $id, $prev, $next); # $next may be undef, here
+       my $mref = $ibx->msg_by_smsg($cur) or return;
+       msg_str($ctx, Email::Simple->new($mref));
 }
 
 sub close {} # noop
@@ -57,21 +55,14 @@ sub emit_raw {
        my $ibx = $ctx->{-inbox};
        my $first;
        my $more;
-       my ($head, $tail, $db);
-       my %seen;
        if (my $srch = $ibx->search) {
-               $srch->retry_reopen(sub {
-                       ($head, $tail, $db) = $srch->each_smsg_by_mid($mid);
-                       for (; !defined($first) && $head != $tail; $head++) {
-                               my @args = ($head, $db, $mid);
-                               my $smsg = PublicInbox::SearchMsg->get(@args);
-                               my $mref = $ibx->msg_by_smsg($smsg) or next;
-                               $first = Email::Simple->new($mref);
-                       }
-                       if ($head != $tail) {
-                               $more = [ $ctx, $head, $tail, $db, $first ];
-                       }
-               });
+               my ($id, $prev);
+               my $smsg = $srch->next_by_mid($mid, \$id, \$prev) or return;
+               my $mref = $ibx->msg_by_smsg($smsg) or return;
+               $first = Email::Simple->new($mref);
+               my $next = $srch->next_by_mid($mid, \$id, \$prev);
+               # $more is for ->getline
+               $more = [ $ctx, $id, $prev, $next, $first ] if $next;
        } else {
                my $mref = $ibx->msg_by_mid($mid) or return;
                $first = Email::Simple->new($mref);
@@ -138,9 +129,24 @@ sub thread_mbox {
        my ($ctx, $srch, $sfx) = @_;
        eval { require IO::Compress::Gzip };
        return sub { need_gzip(@_) } if $@;
-
-       my $cb = sub { $srch->get_thread($ctx->{mid}, @_) };
-       PublicInbox::MboxGz->response($ctx, $cb);
+       my $mid = $ctx->{mid};
+       my $msgs = $srch->get_thread($mid, 0);
+       return [404, [qw(Content-Type text/plain)], []] if !@$msgs;
+       my $prev = $msgs->[-1]->{num};
+       my $i = 0;
+       my $cb = sub {
+               while (1) {
+                       if (my $smsg = $msgs->[$i++]) {
+                               return $smsg;
+                       }
+                       # refill result set
+                       $msgs = $srch->get_thread($mid, $prev);
+                       return unless @$msgs;
+                       $prev = $msgs->[-1]->{num};
+                       $i = 0;
+               }
+       };
+       PublicInbox::MboxGz->response($ctx, $cb, $msgs->[0]->subject);
 }
 
 sub emit_range {
@@ -155,12 +161,56 @@ sub emit_range {
        mbox_all($ctx, $query);
 }
 
+sub mbox_all_ids {
+       my ($ctx) = @_;
+       my $prev = 0;
+       my $ids = $ctx->{-inbox}->mm->ids_after(\$prev) or return
+               [404, [qw(Content-Type text/plain)], ["No results found\n"]];
+       my $i = 0;
+       my $over = $ctx->{srch}->{over_ro};
+       my $cb = sub {
+               do {
+                       while ((my $num = $ids->[$i++])) {
+                               my $smsg = $over->get_art($num) or next;
+                               return $smsg;
+                       }
+                       $ids = $ctx->{-inbox}->mm->ids_after(\$prev);
+                       $i = 0;
+               } while (@$ids);
+               undef;
+       };
+       return PublicInbox::MboxGz->response($ctx, $cb, 'all');
+}
+
 sub mbox_all {
        my ($ctx, $query) = @_;
 
        eval { require IO::Compress::Gzip };
        return sub { need_gzip(@_) } if $@;
-       my $cb = sub { $ctx->{srch}->query($query, @_) };
+       return mbox_all_ids($ctx) if $query eq '';
+       my $opts = { mset => 2 };
+       my $srch = $ctx->{srch};
+       my $mset = $srch->query($query, $opts);
+       $opts->{offset} = $mset->size or
+                       return [404, [qw(Content-Type text/plain)],
+                               ["No results found\n"]];
+       my $i = 0;
+       my $cb = sub { # called by MboxGz->getline
+               while (1) {
+                       while (my $mi = (($mset->items)[$i++])) {
+                               my $doc = $mi->get_document;
+                               my $smsg = $srch->retry_reopen(sub {
+                                       PublicInbox::SearchMsg->load_doc($doc);
+                               }) or next;
+                               return $smsg;
+                       }
+                       # refill result set
+                       $mset = $srch->query($query, $opts);
+                       my $size = $mset->size or return;
+                       $opts->{offset} += $size;
+                       $i = 0;
+               }
+       };
        PublicInbox::MboxGz->response($ctx, $cb, 'results-'.$query);
 }
 
@@ -191,8 +241,6 @@ sub new {
                gz => IO::Compress::Gzip->new(\$buf, Time => 0),
                cb => $cb,
                ctx => $ctx,
-               msgs => [],
-               opts => { offset => 0 },
        }, $class;
 }
 
@@ -200,59 +248,35 @@ sub response {
        my ($class, $ctx, $cb, $fn) = @_;
        my $body = $class->new($ctx, $cb);
        # http://www.iana.org/assignments/media-types/application/gzip
-       $body->{hdr} = [ 'Content-Type', 'application/gzip' ];
-       $body->{fn} = $fn;
-       my $hdr = $body->getline; # fill in Content-Disposition filename
-       [ 200, $hdr, $body ];
-}
-
-sub set_filename ($$) {
-       my ($fn, $msg) = @_;
-       return to_filename($fn) if defined($fn);
-
-       PublicInbox::Mbox::subject_fn($msg);
+       my @h = qw(Content-Type application/gzip);
+       if ($fn) {
+               $fn = to_filename($fn);
+               push @h, 'Content-Disposition', "inline; filename=$fn.mbox.gz";
+       }
+       [ 200, \@h, $body ];
 }
 
 # called by Plack::Util::foreach or similar
 sub getline {
        my ($self) = @_;
        my $ctx = $self->{ctx} or return;
-       my $res;
-       my $ibx = $ctx->{-inbox};
-       my $gz = $self->{gz};
-       do {
-               # work on existing result set
-               while (defined(my $smsg = shift @{$self->{msgs}})) {
-                       my $msg = eval { $ibx->msg_by_smsg($smsg) } or next;
-                       $msg = Email::Simple->new($msg);
-                       $gz->write(PublicInbox::Mbox::msg_str($ctx, $msg,
-                                                               $smsg->mid));
-
-                       # use subject of first message as subject
-                       if (my $hdr = delete $self->{hdr}) {
-                               my $fn = set_filename($self->{fn}, $msg);
-                               push @$hdr, 'Content-Disposition',
-                                               "inline; filename=$fn.mbox.gz";
-                               return $hdr;
-                       }
-                       my $bref = $self->{buf};
-                       if (length($$bref) >= 8192) {
-                               my $ret = $$bref; # copy :<
-                               ${$self->{buf}} = '';
-                               return $ret;
-                       }
-
-                       # be fair to other clients on public-inbox-httpd:
-                       return '';
+       while (my $smsg = $self->{cb}->()) {
+               my $msg = $ctx->{-inbox}->msg_by_smsg($smsg) or next;
+               $msg = Email::Simple->new($msg);
+               $self->{gz}->write(PublicInbox::Mbox::msg_str($ctx, $msg,
+                               $smsg->{mid}));
+               my $bref = $self->{buf};
+               if (length($$bref) >= 8192) {
+                       my $ret = $$bref; # copy :<
+                       ${$self->{buf}} = '';
+                       return $ret;
                }
 
-               # refill result set
-               $res = $self->{cb}->($self->{opts});
-               $self->{msgs} = $res->{msgs};
-               $res = scalar @{$self->{msgs}};
-               $self->{opts}->{offset} += $res;
-       } while ($res);
-       $gz->close;
+               # be fair to other clients on public-inbox-httpd:
+               return '';
+       }
+       delete($self->{gz})->close;
+       # signal that we're done and can return undef next call:
        delete $self->{ctx};
        ${delete $self->{buf}};
 }