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: header in Unix time (IMAP INTERNALDATE)
+ TS => 0, # Received: in Unix time (IMAP INTERNALDATE, JMAP receivedAt)
YYYYMMDD => 1, # Date: header for searching in the WWW UI
- DT => 2, # Date: YYYYMMDDHHMMSS
+ DT => 2, # Date: YYYYMMDDHHMMSS (IMAP SENT*, JMAP sentAt)
# added for public-inbox 1.6.0+
BYTES => 3, # IMAP RFC822.SIZE
# 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";
# 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') {
"$pfx:".join('..', @r).$end;
}
+sub date_parse_finalize {
+ my ($git, $to_parse) = @_;
+ # 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. argv never has NUL, though we'll need to filter it out
# if this $argv isn't from a command execution
sub query_argv_to_string {
my (undef, $git, $argv) = @_;
my $to_parse;
my $tmp = join(' ', map {;
- if (s!\b(d|rt|dt):([[:print:]]+)\z!date_parse_prepare(
+ if (s!\b(d|rt|dt):(\S+)\z!date_parse_prepare(
$to_parse //= [], $1, $2)!sge) {
$_;
+ } elsif (/\s/) {
+ s/(.*?)\b(\w+:)// ? qq{$1$2"$_"} : qq{"$_"};
} else {
- /\s/ ? (s/\A(\w+:)// ? qq{$1"$_"} : qq{"$_}) : $_
+ $_
}
} @$argv);
- # git-rev-parse can handle any number of args up to system
- # limits (around (4096*32) bytes on Linux).
- if ($to_parse) {
- my @r = $git->date_parse(@$to_parse);
- my $i;
- $tmp =~ s/\0(%[%YmdHMSs]+)([0-9\+]+)\0/strftime($1,
- gmtime($2 eq '+' ? ($r[$i]+86400) : $r[$i=$2+0]))/sge;
- }
+ date_parse_finalize($git, $to_parse, $tmp) if $to_parse;
$tmp
}
+# this is for the WWW "q=" query parameter and "lei q --stdin"
+# it can't do d:"5 days ago", but it will do d:5.days.ago
+sub query_approxidate {
+ my (undef, $git) = @_; # $_[2] = $query_string (modified in-place)
+ my $DQ = qq<"\x{201c}\x{201d}>; # Xapian can use curly quotes
+ $_[2] =~ tr/\x00/ /; # Xapian doesn't do NUL, we use it as a placeholder
+ my ($terms, $phrase, $to_parse);
+ $_[2] =~ s{([^$DQ]*)([$DQ][^$DQ]*[$DQ])?}{
+ ($terms, $phrase) = ($1, $2);
+ $terms =~ s!\b(d|rt|dt):(\S+)!
+ date_parse_prepare($to_parse //= [], $1, $2)!sge;
+ $terms.($phrase // '');
+ }sge;
+ date_parse_finalize($git, $to_parse, $_[2]) if $to_parse;
+}
+
# read-only
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);
}
# Exception: The revision being read has been discarded -
# you should call Xapian::Database::reopen()
if (ref($@) =~ /\bDatabaseModifiedError\b/) {
- warn "reopen try #$i on $@\n";
+ 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 {
sub stemmer { $X{Stem}->new($LANG) }
# read-only
-sub qparse_new ($) {
+sub qparse_new {
my ($self) = @_;
my $xdb = xdb($self);
$cb->($qp, $NVRP->new(DT, 'dt:'));
# for IMAP, undocumented for WWW and may be split off go away
- $cb->($qp, $NVRP->new(BYTES, 'bytes:'));
+ $cb->($qp, $NVRP->new(BYTES, 'z:'));
$cb->($qp, $NVRP->new(TS, 'rt:'));
$cb->($qp, $NVRP->new(UID, 'uid:'));
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;