X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FSearch.pm;h=523003b3c269ae42e9ee210e91415b2846735daa;hb=1f1b1f0e22f7b4cbe5c8e843c1932222c0461393;hp=c7d52dafc3f4f9e37491aea50c6ac13880ee8b62;hpb=72cbfb826cd7827c92c239d8c1e81024514e211f;p=public-inbox.git diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm index c7d52daf..523003b3 100644 --- a/lib/PublicInbox/Search.pm +++ b/lib/PublicInbox/Search.pm @@ -5,16 +5,18 @@ # Read-only search interface for use by the web and NNTP interfaces package PublicInbox::Search; use strict; +use v5.10.1; use parent qw(Exporter); our @EXPORT_OK = qw(retry_reopen int_val get_pct xap_terms); use List::Util qw(max); use POSIX qw(strftime); +use Carp (); # values for searching, changing the numeric value breaks # compatibility with old indices (so don't change them it) use constant { TS => 0, # Received: in Unix time (IMAP INTERNALDATE, JMAP receivedAt) - YYYYMMDD => 1, # Date: header for searching in the WWW UI + YYYYMMDD => 1, # redundant with DT below DT => 2, # Date: YYYYMMDDHHMMSS (IMAP SENT*, JMAP sentAt) # added for public-inbox 1.6.0+ @@ -153,12 +155,9 @@ my %prob_prefix = ( our @HELP = ( 's:' => 'match within Subject e.g. s:"a quick brown fox"', 'd:' => < < 'match within message body, including text attachments', 'nq:' => 'match non-quoted text within message body', @@ -179,6 +178,9 @@ EOF 'dfpre:' => 'match pre-image git blob ID', 'dfpost:' => 'match post-image git blob ID', 'dfblob:' => 'match either pre or post-image git blob ID', + 'rt:' => <{shard})) { $self->{xpfx}; - } else { # v2 only: + } else { # v2 + extindex only: "$self->{xpfx}/$self->{shard}"; } } @@ -198,7 +200,7 @@ sub xdb_shards_flat ($) { my (@xdb, $slow_phrase); load_xapian(); $self->{qp_flags} //= $QP_FLAGS; - if ($xpfx =~ m/xapian${\SCHEMA_VERSION}\z/) { + if ($xpfx =~ m!/xapian[0-9]+\z!) { @xdb = ($X{Database}->new($xpfx)); $self->{qp_flags} |= FLAG_PHRASE() if !-f "$xpfx/iamchert"; } else { @@ -233,12 +235,12 @@ sub mset_to_artnums { sub xdb ($) { my ($self) = @_; - $self->{xdb} //= do { + $self->{xdb} // do { my @xdb = $self->xdb_shards_flat or return; $self->{nshard} = scalar(@xdb); my $xdb = shift @xdb; $xdb->add_database($_) for @xdb; - $xdb; + $self->{xdb} = $xdb; }; } @@ -246,10 +248,10 @@ sub new { my ($class, $ibx) = @_; ref $ibx or die "BUG: expected PublicInbox::Inbox object: $ibx"; my $xap = $ibx->version > 1 ? 'xap' : 'public-inbox/xapian'; - bless { - xpfx => "$ibx->{inboxdir}/$xap" . SCHEMA_VERSION, - altid => $ibx->{altid}, - }, $class; + my $xpfx = "$ibx->{inboxdir}/$xap".SCHEMA_VERSION; + my $self = bless { xpfx => $xpfx }, $class; + $self->{altid} = $ibx->{altid} if defined($ibx->{altid}); + $self; } sub reopen { @@ -275,7 +277,9 @@ sub date_parse_prepare { # expand "d:20101002" => "d:20101002..20101003" and like # n.b. git doesn't do YYYYMMDD w/o '-', it needs YYYY-MM-DD + # We upgrade "d:" to "dt:" to iff using approxidate if ($pfx eq 'd') { + my $fmt = "\0%Y%m%d"; if (!defined($r[1])) { if ($r[0] =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\z/) { push @$to_parse, "$1-$2-$3"; @@ -283,14 +287,27 @@ sub date_parse_prepare { # to parse anyways for "d+" below } else { push @$to_parse, $r[0]; + if ($r[0] !~ /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/) { + $pfx = 'dt'; + $fmt = "\0%Y%m%d%H%M%S"; + } } - $r[0] = "\0%Y%m%d$#$to_parse\0"; - $r[1] = "\0%Y%m%d+\0"; + $r[0] = "$fmt+$#$to_parse\0"; + $r[1] = "$fmt+\0"; } else { for my $x (@r) { next if $x eq '' || $x =~ /\A[0-9]{8}\z/; push @$to_parse, $x; - $x = "\0%Y%m%d$#$to_parse\0"; + if ($x !~ /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/) { + $pfx = 'dt'; + } + $x = "$fmt$#$to_parse\0"; + } + if ($pfx eq 'dt') { + for (@r) { + s/\0%Y%m%d/\0%Y%m%d%H%M%S/; + s/\A([0-9]{8})\z/${1}000000/; + } } } } elsif ($pfx eq 'dt') { @@ -316,7 +333,7 @@ sub date_parse_prepare { push @$to_parse, $x; $x = "\0%s$#$to_parse\0"; } - $r[1] //= "\0%s+\0"; + $r[1] //= "\0%s+\0"; # add 1 day } "$pfx:".join('..', @r).$end; } @@ -326,9 +343,12 @@ sub date_parse_finalize { # git-rev-parse can handle any number of args up to system # limits (around (4096*32) bytes on Linux). my @r = $git->date_parse(@$to_parse); - my $i; - $_[2] =~ s/\0(%[%YmdHMSs]+)([0-9\+]+)\0/strftime($1, - gmtime($2 eq '+' ? ($r[$i]+86400) : $r[$i=$2+0]))/sge; + # n.b. git respects TZ, times stored in SQLite/Xapian are always UTC, + # and gmtime doesn't seem to do the right thing when TZ!=UTC + my ($i, $t); + $_[2] =~ s/\0(%[%YmdHMSs]+)([0-9\+]+)\0/ + $t = $2 eq '+' ? ($r[$i]+86400) : $r[$i=$2+0]; + $1 eq '%s' ? $t : strftime($1, gmtime($t))/sge; } # n.b. argv never has NUL, though we'll need to filter it out @@ -370,7 +390,7 @@ sub query_approxidate { sub mset { my ($self, $query_string, $opts) = @_; $opts ||= {}; - my $qp = $self->{qp} //= qparse_new($self); + my $qp = $self->{qp} //= $self->qparse_new; my $query = $qp->parse_query($query_string, $self->{qp_flags}); _do_enquire($self, $query, $opts); } @@ -379,27 +399,24 @@ sub retry_reopen { my ($self, $cb, @arg) = @_; for my $i (1..10) { if (wantarray) { - my @ret; - eval { @ret = $cb->($self, @arg) }; + my @ret = eval { $cb->($self, @arg) }; return @ret unless $@; } else { - my $ret; - eval { $ret = $cb->($self, @arg) }; + my $ret = eval { $cb->($self, @arg) }; return $ret unless $@; } # Exception: The revision being read has been discarded - # you should call Xapian::Database::reopen() if (ref($@) =~ /\bDatabaseModifiedError\b/) { - warn "reopen try #$i on $@\n"; reopen($self); } else { # let caller decide how to spew, because ExtMsg queries # get wonky and trigger: # "something terrible happened at .../Xapian/Enquire.pm" - die; + Carp::croak($@); } } - die "Too many Xapian database modifications in progress\n"; + Carp::croak("Too many Xapian database modifications in progress\n"); } sub _do_enquire { @@ -463,7 +480,7 @@ sub mset_to_smsg { sub stemmer { $X{Stem}->new($LANG) } # read-only -sub qparse_new ($) { +sub qparse_new { my ($self) = @_; my $xdb = xdb($self); @@ -516,7 +533,7 @@ EOF sub help { my ($self) = @_; - $self->{qp} //= qparse_new($self); # parse altids + $self->{qp} //= $self->qparse_new; # parse altids my @ret = @HELP; if (my $user_pfx = $self->{-user_pfx}) { push @ret, @$user_pfx; @@ -541,19 +558,23 @@ sub get_pct ($) { # mset item sub xap_terms ($$;@) { my ($pfx, $xdb_or_doc, @docid) = @_; # @docid may be empty () my %ret; - eval { - my $end = $xdb_or_doc->termlist_end(@docid); - my $cur = $xdb_or_doc->termlist_begin(@docid); - for (; $cur != $end; $cur++) { - $cur->skip_to($pfx); - last if $cur == $end; - my $tn = $cur->get_termname; - if (index($tn, $pfx) == 0) { - $ret{substr($tn, length($pfx))} = undef; - } - } - }; - \%ret; + my $end = $xdb_or_doc->termlist_end(@docid); + my $cur = $xdb_or_doc->termlist_begin(@docid); + for (; $cur != $end; $cur++) { + $cur->skip_to($pfx); + last if $cur == $end; + my $tn = $cur->get_termname; + $ret{substr($tn, length($pfx))} = undef if !index($tn, $pfx); + } + wantarray ? sort(keys(%ret)) : \%ret; +} + +# get combined docid from over.num: +# (not generic Xapian, only works with our sharding scheme) +sub num2docid ($$) { + my ($self, $num) = @_; + my $nshard = $self->{nshard}; + ($num - 1) * $nshard + $num % $nshard + 1; } 1;