]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/Isearch.pm
isearch: emulate per-inbox search with ->ALL
[public-inbox.git] / lib / PublicInbox / Isearch.pm
1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # Provides everything the PublicInbox::Search object does;
5 # but uses global ExtSearch (->ALL) with an eidx_key query to
6 # emulate per-Inbox search using ->ALL.
7 package PublicInbox::Isearch;
8 use strict;
9 use v5.10.1;
10 use PublicInbox::ExtSearch;
11 use PublicInbox::Search;
12
13 sub new {
14         my (undef, $ibx, $es) = @_;
15         bless { es => $es, eidx_key => $ibx->eidx_key }, __PACKAGE__;
16 }
17
18 sub mset {
19         my ($self, $str, $opt) = @_;
20         $self->{es}->mset($str, { $opt ? %$opt : (),
21                                 eidx_key => $self->{eidx_key} });
22 }
23
24 sub _ibx_id ($) {
25         my ($self) = @_;
26         my $sth = $self->{es}->over->dbh->prepare_cached(<<'', undef, 1);
27 SELECT ibx_id FROM inboxes WHERE eidx_key = ? LIMIT 1
28
29         $sth->execute($self->{eidx_key});
30         $sth->fetchrow_array //
31                 die "E: `$self->{eidx_key}' not in $self->{es}->{topdir}\n";
32 }
33
34 sub mset_to_artnums {
35         my ($self, $mset) = @_;
36         my $docids = PublicInbox::Search::mset_to_artnums($self->{es}, $mset);
37         my $ibx_id = $self->{-ibx_id} //= _ibx_id($self);
38         my $qmarks = join(',', map { '?' } @$docids);
39         my $rows = $self->{es}->over->dbh->
40                         selectall_arrayref(<<"", undef, $ibx_id, @$docids);
41 SELECT docid,xnum FROM xref3 WHERE ibx_id = ? AND docid IN ($qmarks)
42
43         my $i = -1;
44         my %order = map { $_ => ++$i } @$docids;
45         my @xnums;
46         for my $row (@$rows) { # @row = ($docid, $xnum)
47                 my $idx = delete($order{$row->[0]}) // next;
48                 $xnums[$idx] = $row->[1];
49         }
50         if (scalar keys %order) {
51                 warn "W: $self->{es}->{topdir} #",
52                         join(', #', sort keys %order),
53                         " not mapped to `$self->{eidx_key}'\n";
54                 warn "W: $self->{es}->{topdir} may need to be reindexed\n";
55                 @xnums = grep { defined } @xnums;
56         }
57         \@xnums;
58 }
59
60 sub mset_to_smsg {
61         my ($self, $ibx, $mset) = @_; # $ibx is a real inbox, not eidx
62         my $xnums = mset_to_artnums($self, $mset);
63         my $i = -1;
64         my %order = map { $_ => ++$i } @$xnums;
65         my $unordered = $ibx->over->get_all(@$xnums);
66         my @msgs;
67         for my $smsg (@$unordered) {
68                 my $idx = delete($order{$smsg->{num}}) // do {
69                         warn "W: $ibx->{inboxdir} #$smsg->{num}\n";
70                         next;
71                 };
72                 $msgs[$idx] = $smsg;
73         }
74         if (scalar keys %order) {
75                 warn "W: $ibx->{inboxdir} #",
76                         join(', #', sort keys %order),
77                         " no longer valid\n";
78                 warn "W: $self->{es}->{topdir} may need to be reindexed\n";
79         }
80         wantarray ? ($mset->get_matches_estimated, \@msgs) : \@msgs;
81 }
82
83 sub has_threadid { 1 }
84
85 sub help { $_[0]->{es}->help }
86
87 1;