changedafter => \&_long_desc_changedbefore_after,
_default => \&_long_desc,
},
+ 'longdescs.count' => {
+ changedby => \&_long_desc_changedby,
+ changedbefore => \&_long_desc_changedbefore_after,
+ changedafter => \&_long_desc_changedbefore_after,
+ changedfrom => \&_invalid_combination,
+ changedto => \&_invalid_combination,
+ _default => \&_long_descs_count,
+ },
'longdescs.isprivate' => {
_default => \&_longdescs_isprivate,
},
to => 'id',
},
},
+ 'longdescs.count' => {
+ table => 'longdescs',
+ join => 'INNER',
+ },
};
# This constant defines the columns that can be selected in a query
. $dbh->sql_string_concat('map_flagtypes.name', 'map_flags.status')),
'keywords' => $dbh->sql_group_concat('DISTINCT map_keyworddefs.name'),
+
+ 'longdescs.count' => 'COUNT(DISTINCT map_longdescs_count.comment_id)',
);
# Backward-compatibility for old field names. Goes new_name => old_name.
bug_id
flagtypes.name
keywords
+ longdescs.count
percentage_complete
);
COLUMNS->{'relevance'}->{name} = $select_term;
}
-sub _long_desc {
+sub _join_longdescs {
my ($self, $args) = @_;
my ($chart_id, $joins) = @$args{qw(chart_id joins)};
as => $table,
extra => $extra,
};
+ # We only want to do an INNER JOIN if we're not checking isprivate.
+ # Otherwise we'd exclude all bugs with only private comments from
+ # the search entirely.
+ $join->{join} = 'INNER' if $self->_user->is_insider;
push(@$joins, $join);
+ return $table;
+}
+
+sub _long_desc {
+ my ($self, $args) = @_;
+ my $table = $self->_join_longdescs($args);
$args->{full_field} = "$table.thetext";
}
-sub _longdescs_isprivate {
+sub _long_descs_count {
my ($self, $args) = @_;
my ($chart_id, $joins) = @$args{qw(chart_id joins)};
-
- my $table = "longdescs_$chart_id";
- my $extra = $self->_user->is_insider ? [] : ["$table.isprivate = 0"];
+ my $table = "longdescs_count_$chart_id";
+ my $extra = $self->_user->is_insider ? "" : "WHERE isprivate = 0";
my $join = {
- table => 'longdescs',
+ table => "(SELECT bug_id, COUNT(*) AS num"
+ . " FROM longdescs $extra GROUP BY bug_id)",
as => $table,
- extra => $extra,
};
push(@$joins, $join);
+ $args->{full_field} = "${table}.num";
+}
+
+sub _longdescs_isprivate {
+ my ($self, $args) = @_;
+ my $table = $self->_join_longdescs($args);
$args->{full_field} = "$table.isprivate";
}
estimated_time => { contains => [1] },
'flagtypes.name' => { contains => [1] },
keywords => { contains => [1] },
+ 'longdescs.count' => { search => 1 },
work_time => { contains => [1] },
FIELD_TYPE_MULTI_SELECT, { contains => [1] },
);
# For changedfrom and changedto.
use constant CHANGED_FROM_TO_BROKEN_NOT => (
+ 'longdescs.count' => { search => 1 },
"bug_group" => { contains => [1] },
"cc" => { contains => [1] },
"cf_multi_select" => { contains => [1] },
cclist_accessible => { value => '^1' },
reporter_accessible => { value => '^1' },
everconfirmed => { value => '^1' },
+ 'longdescs.count' => { value => '^3' },
'longdescs.isprivate' => { value => '^1' },
creation_ts => { value => '^2037-01-01' },
delta_ts => { value => '^2037-01-01' },
# For anyexact and anywordssubstr
use constant ANY_OVERRIDE => (
+ 'longdescs.count' => { contains => [1,2,3,4] },
'work_time' => { value => '1.0,2.0' },
dependson => { value => '<1>,<3>', contains => [1,3] },
MULTI_BOOLEAN_OVERRIDE,
'attachments.ispatch' => { value => 1, contains => [2,3,4] },
cclist_accessible => { value => 1, contains => [2,3,4,5] },
reporter_accessible => { value => 1, contains => [2,3,4,5] },
+ 'longdescs.count' => { value => 3, contains => [2,3,4,5] },
'longdescs.isprivate' => { value => 1, contains => [2,3,4,5] },
everconfirmed => { value => 1, contains => [2,3,4,5] },
creation_ts => { value => '2037-01-02', contains => [1,5] },
'attachments.isprivate' => { value => 0, contains => [2,3,4] },
cclist_accessible => { value => 0, contains => [2,3,4,5] },
reporter_accessible => { value => 0, contains => [2,3,4,5] },
+ 'longdescs.count' => { value => 2, contains => [2,3,4,5] },
'longdescs.isprivate' => { value => 0, contains => [2,3,4,5] },
everconfirmed => { value => 0, contains => [2,3,4,5] },
blocked => { contains => [1,2] },
'attachments.isprivate' => { value => 0, contains => [1] },
cclist_accessible => { value => 0, contains => [1] },
reporter_accessible => { value => 0, contains => [1] },
+ 'longdescs.count' => { value => 2, contains => [1] },
'longdescs.isprivate' => { value => 0, contains => [1] },
everconfirmed => { value => 0, contains => [1] },
'flagtypes.name' => { value => 2, contains => [2,3,4] },
'attachments.isprivate' => { value => 1, contains => [1] },
cclist_accessible => { value => 1, contains => [1] },
reporter_accessible => { value => 1, contains => [1] },
+ 'longdescs.count' => { value => 3, contains => [1] },
'longdescs.isprivate' => { value => 1, contains => [1] },
everconfirmed => { value => 1, contains => [1] },
dependson => { value => '<3>', contains => [1,3] },
override => {
MULTI_BOOLEAN_OVERRIDE,
dependson => { value => '<1> <3>', contains => [1,3] },
+ 'longdescs.count' => { contains => [1,2,3,4] },
work_time => { value => '1.0 2.0' },
},
},
blocked => { contains => [1,2] },
dependson => { contains => [1,3] },
longdesc => { contains => [1,5] },
+ 'longdescs.count' => { contains => [1,5] },
}
},
],
FIELD_TYPE_BUG_ID, { db_skip => ['Pg'] },
FIELD_TYPE_DATETIME, { db_skip => ['Pg'] },
owner_idle_time => { search => 1 },
+ 'longdescs.count' => {
+ search => 1,
+ db_skip => ['Pg'],
+ operator_ok => [qw(allwordssubstr anywordssubstr casesubstring
+ changedbefore changedafter greaterthan greaterthaneq
+ lessthan lessthaneq notregexp notsubstring
+ nowordssubstr regexp substring anywords
+ notequals nowords)],
+ },
keywords => {
search => 1,
operator_ok => [qw(allwordssubstr anywordssubstr casesubstring