]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/MiscIdx.pm
rename {pi_config} fields to {pi_cfg}
[public-inbox.git] / lib / PublicInbox / MiscIdx.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 # like PublicInbox::SearchIdx, but for searching for non-mail messages.
5 # Things indexed include:
6 # * inboxes themselves
7 # * epoch information
8 # * (maybe) git code repository information
9 # Expect ~100K-1M documents with no parallelism opportunities,
10 # so no sharding, here.
11 #
12 # See MiscSearch for read-only counterpart
13 package PublicInbox::MiscIdx;
14 use strict;
15 use v5.10.1;
16 use PublicInbox::InboxWritable;
17 use PublicInbox::Search; # for SWIG Xapian and Search::Xapian compat
18 use PublicInbox::SearchIdx qw(index_text term_generator add_val);
19 use PublicInbox::Spawn qw(nodatacow_dir);
20 use Carp qw(croak);
21 use File::Path ();
22 use PublicInbox::MiscSearch;
23 use PublicInbox::Config;
24
25 sub new {
26         my ($class, $eidx) = @_;
27         PublicInbox::SearchIdx::load_xapian_writable();
28         my $mi_dir = "$eidx->{xpfx}/misc";
29         File::Path::mkpath($mi_dir);
30         nodatacow_dir($mi_dir);
31         my $flags = $PublicInbox::SearchIdx::DB_CREATE_OR_OPEN;
32         $flags |= $PublicInbox::SearchIdx::DB_NO_SYNC if $eidx->{-no_fsync};
33         bless {
34                 mi_dir => $mi_dir,
35                 flags => $flags,
36                 indexlevel => 'full', # small DB, no point in medium?
37         }, $class;
38 }
39
40 sub begin_txn {
41         my ($self) = @_;
42         croak 'BUG: already in txn' if $self->{xdb}; # XXX make lazy?
43         my $wdb = $PublicInbox::Search::X{WritableDatabase};
44         my $xdb = eval { $wdb->new($self->{mi_dir}, $self->{flags}) };
45         croak "Failed opening $self->{mi_dir}: $@" if $@;
46         $self->{xdb} = $xdb;
47         $xdb->begin_transaction;
48 }
49
50 sub commit_txn {
51         my ($self) = @_;
52         croak 'BUG: not in txn' unless $self->{xdb}; # XXX make lazy?
53         delete($self->{xdb})->commit_transaction;
54 }
55
56 sub remove_eidx_key {
57         my ($self, $eidx_key) = @_;
58         my $xdb = $self->{xdb};
59         my $head = $xdb->postlist_begin('Q'.$eidx_key);
60         my $tail = $xdb->postlist_end('Q'.$eidx_key);
61         my @docids; # only one, unless we had bugs
62         for (; $head != $tail; $head++) {
63                 push @docids, $head->get_docid;
64         }
65         for my $docid (@docids) {
66                 $xdb->delete_document($docid);
67                 warn "I: remove inbox docid #$docid ($eidx_key)\n";
68         }
69 }
70
71 # adds or updates according to $eidx_key
72 sub index_ibx {
73         my ($self, $ibx) = @_;
74         my $eidx_key = $ibx->eidx_key;
75         my $xdb = $self->{xdb};
76         # Q = uniQue in Xapian terminology
77         my $head = $xdb->postlist_begin('Q'.$eidx_key);
78         my $tail = $xdb->postlist_end('Q'.$eidx_key);
79         my ($docid, @drop);
80         for (; $head != $tail; $head++) {
81                 if (defined $docid) {
82                         my $i = $head->get_docid;
83                         push @drop, $i;
84                         warn <<EOF;
85 W: multiple inboxes keyed to `$eidx_key', deleting #$i
86 EOF
87                 } else {
88                         $docid = $head->get_docid;
89                 }
90         }
91         $xdb->delete_document($_) for @drop; # just in case
92
93         my $doc = $PublicInbox::Search::X{Document}->new;
94
95         # allow sorting by modified
96         add_val($doc, $PublicInbox::MiscSearch::MODIFIED, $ibx->modified);
97
98         $doc->add_boolean_term('Q'.$eidx_key);
99         $doc->add_boolean_term('T'.'inbox');
100         term_generator($self)->set_document($doc);
101
102         # description = S/Subject (or title)
103         # address = A/Author
104         index_text($self, $ibx->description, 1, 'S');
105         my %map = (
106                 address => 'A',
107                 listid => 'XLISTID',
108                 infourl => 'XINFOURL',
109                 url => 'XURL'
110         );
111         while (my ($f, $pfx) = each %map) {
112                 for my $v (@{$ibx->{$f} // []}) {
113                         index_text($self, $v, 1, $pfx);
114                 }
115         }
116         index_text($self, $ibx->{name}, 1, 'XNAME');
117         my $data = {};
118         if (defined(my $max = $ibx->max_git_epoch)) { # v2
119                 my $desc = $ibx->description;
120                 my $pfx = "/$ibx->{name}/git/";
121                 for my $epoch (0..$max) {
122                         my $git = $ibx->git_epoch($epoch) or return;
123                         if (my $ent = $git->manifest_entry($epoch, $desc)) {
124                                 $data->{"$pfx$epoch.git"} = $ent;
125                                 $ent->{git_dir} = $git->{git_dir};
126                         }
127                         $git->cleanup; # ->modified starts cat-file --batch
128                 }
129         } elsif (my $ent = $ibx->git->manifest_entry) { # v1
130                 $ent->{git_dir} = $ibx->{inboxdir};
131                 $data->{"/$ibx->{name}"} = $ent;
132         }
133         $doc->set_data(PublicInbox::Config::json()->encode($data));
134         if (defined $docid) {
135                 $xdb->replace_document($docid, $doc);
136         } else {
137                 $xdb->add_document($doc);
138         }
139 }
140
141 1;