diff options
-rw-r--r-- | Bugzilla.pm | 6 | ||||
-rw-r--r-- | Bugzilla/Bug.pm | 47 | ||||
-rw-r--r-- | Bugzilla/Elastic.pm | 47 | ||||
-rw-r--r-- | Bugzilla/Elastic/Indexer.pm | 29 | ||||
-rw-r--r-- | Bugzilla/Elastic/Role/HasClient.pm | 2 | ||||
-rw-r--r-- | Bugzilla/Elastic/Role/Search.pm | 16 | ||||
-rw-r--r-- | Bugzilla/Elastic/Search.pm | 425 | ||||
-rw-r--r-- | Bugzilla/Elastic/Search/FakeCGI.pm | 43 | ||||
-rw-r--r-- | Bugzilla/Search/Quicksearch.pm | 6 | ||||
-rw-r--r-- | Bugzilla/User.pm | 7 | ||||
-rw-r--r-- | Bugzilla/WebService/Constants.pm | 1 | ||||
-rw-r--r-- | Bugzilla/WebService/Elastic.pm | 59 | ||||
-rw-r--r-- | Bugzilla/WebService/Server/REST.pm | 1 | ||||
-rw-r--r-- | Bugzilla/WebService/Server/REST/Resources/Elastic.pm | 30 | ||||
-rwxr-xr-x | buglist.cgi | 95 | ||||
-rw-r--r-- | js/field.js | 6 | ||||
-rw-r--r-- | scripts/search.pl | 13 | ||||
-rw-r--r-- | scripts/suggest-user.pl | 20 | ||||
-rw-r--r-- | template/en/default/list/list.html.tmpl | 8 |
19 files changed, 78 insertions, 783 deletions
diff --git a/Bugzilla.pm b/Bugzilla.pm index ecaca9151..bd410364e 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -23,7 +23,6 @@ BEGIN { use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; use Bugzilla::CGI; -use Bugzilla::Elastic; use Bugzilla::Config; use Bugzilla::Constants; use Bugzilla::DB; @@ -787,11 +786,6 @@ sub memcached { } } -sub elastic { - my ($class) = @_; - $class->process_cache->{elastic} //= Bugzilla::Elastic->new(); -} - # Private methods # Per-process cleanup. Note that this is a plain subroutine, not a method, diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index bc099f76e..cba973863 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -303,15 +303,19 @@ with 'Bugzilla::Elastic::Role::Object'; sub ES_TYPE {'bug'} sub _bz_field { - my ($field, @fields) = @_; + my ($field, $type, $analyzer, @fields) = @_; return ( $field => { - type => 'string', - analyzer => 'bz_text_analyzer', + type => $type, + analyzer => $analyzer, fields => { + raw => { + type => 'string', + index => 'not_analyzed', + }, eq => { - type => 'string', + type => 'string', analyzer => 'bz_equals_analyzer', }, @fields, @@ -320,20 +324,32 @@ sub _bz_field { ); } +sub _bz_text_field { + my ($field) = @_; + + return _bz_field($field, 'string', 'bz_text_analyzer'); +} + +sub _bz_substring_field { + my ($field, @rest) = @_; + + return _bz_field($field, 'string', 'bz_substring_analyzer', @rest); +} + sub ES_PROPERTIES { return { - _bz_field('priority'), - _bz_field('bug_severity'), - _bz_field('bug_status'), - _bz_field('resolution'), + priority => { type => 'string', analyzer => 'keyword' }, + bug_severity => { type => 'string', analyzer => 'keyword' }, + bug_status => { type => 'string', analyzer => 'keyword' }, + resolution => { type => 'string', analyzer => 'keyword' }, + keywords => { type => 'string' }, status_whiteboard => { type => 'string', analyzer => 'whiteboard_shingle_tokens' }, delta_ts => { type => 'string', index => 'not_analyzed' }, - _bz_field('product'), - _bz_field('component'), - _bz_field('classification'), - _bz_field('short_desc'), - _bz_field('assigned_to'), - _bz_field('reporter'), + _bz_substring_field('product'), + _bz_substring_field('component'), + _bz_substring_field('classification'), + _bz_text_field('short_desc'), + _bz_substring_field('assigned_to'), }; } @@ -410,7 +426,7 @@ sub es_document { bug_id => $self->id, product => $self->product_obj->name, alias => $self->alias, - keywords => [ map { $_->name } @{$self->keyword_objects} ], + keywords => $self->keywords, priority => $self->priority, bug_status => $self->bug_status, resolution => $self->resolution, @@ -419,7 +435,6 @@ sub es_document { status_whiteboard => $self->status_whiteboard, short_desc => $self->short_desc, assigned_to => $self->assigned_to->login, - reporter => $self->reporter->login, delta_ts => $self->delta_ts, bug_severity => $self->bug_severity, }; diff --git a/Bugzilla/Elastic.pm b/Bugzilla/Elastic.pm deleted file mode 100644 index 6384269fd..000000000 --- a/Bugzilla/Elastic.pm +++ /dev/null @@ -1,47 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::Elastic; -use 5.10.1; -use Moo; - -use Bugzilla::Elastic::Search; -use Bugzilla::Util qw(trick_taint); - -with 'Bugzilla::Elastic::Role::HasClient'; -with 'Bugzilla::Elastic::Role::HasIndexName'; - -sub suggest_users { - my ($self, $text) = @_; - my $field = 'suggest_user'; - if ($text =~ /^:(.+)$/) { - $text = $1; - $field = 'suggest_nick'; - } - - my $result = eval { - $self->client->suggest( - index => $self->index_name, - body => { - $field => { - text => $text, - completion => { field => $field, size => 25 }, - } - } - ); - }; - if (defined $result) { - return [ map { $_->{payload} } @{$result->{$field}[0]{options}} ]; - } - else { - warn "suggest_users error: $@"; - my $users = Bugzilla::User::match($text, 25, 0); - return [ map { { real_name => $_->name, name => $_->login } } @$users]; - } -} - - -1; diff --git a/Bugzilla/Elastic/Indexer.pm b/Bugzilla/Elastic/Indexer.pm index dd71a7198..82f946af9 100644 --- a/Bugzilla/Elastic/Indexer.pm +++ b/Bugzilla/Elastic/Indexer.pm @@ -23,7 +23,7 @@ has 'mtime' => ( has 'shadow_dbh' => ( is => 'lazy' ); has 'debug_sql' => ( - is => 'ro', + is => 'ro', default => 0, ); @@ -40,24 +40,24 @@ sub create_index { index => $self->index_name, body => { settings => { - number_of_shards => 2, + number_of_shards => 1, analysis => { - filter => { - asciifolding_original => { - type => "asciifolding", - preserve_original => \1, - }, - }, analyzer => { folding => { + type => 'standard', tokenizer => 'standard', - filter => ['standard', 'lowercase', 'asciifolding_original'], + filter => [ 'lowercase', 'asciifolding' ] }, bz_text_analyzer => { type => 'standard', filter => ['lowercase', 'stop'], max_token_length => '20' }, + bz_substring_analyzer => { + type => 'custom', + filter => ['lowercase'], + tokenizer => 'bz_ngram_tokenizer', + }, bz_equals_analyzer => { type => 'custom', filter => ['lowercase'], @@ -71,20 +71,25 @@ sub create_index { whiteboard_shingle_words => { type => 'custom', tokenizer => 'whiteboard_words_pattern', - filter => ['stop', 'shingle', 'lowercase'] + filter => ['stop', 'shingle'] }, whiteboard_tokens => { type => 'custom', tokenizer => 'whiteboard_tokens_pattern', - filter => ['stop', 'lowercase'] + filter => ['stop'] }, whiteboard_shingle_tokens => { type => 'custom', tokenizer => 'whiteboard_tokens_pattern', - filter => ['stop', 'shingle', 'lowercase'] + filter => ['stop', 'shingle'] } }, tokenizer => { + bz_ngram_tokenizer => { + type => 'nGram', + min_ngram => 2, + max_ngram => 25, + }, whiteboard_tokens_pattern => { type => 'pattern', pattern => '\\s*([,;]*\\[|\\][\\s\\[]*|[;,])\\s*' diff --git a/Bugzilla/Elastic/Role/HasClient.pm b/Bugzilla/Elastic/Role/HasClient.pm index 8e2687880..3d52d513a 100644 --- a/Bugzilla/Elastic/Role/HasClient.pm +++ b/Bugzilla/Elastic/Role/HasClient.pm @@ -17,7 +17,7 @@ sub _build_client { my ($self) = @_; return Search::Elasticsearch->new( - nodes => [ split(/\s+/, Bugzilla->params->{elasticsearch_nodes}) ], + nodes => Bugzilla->params->{elasticsearch_nodes}, cxn_pool => 'Sniff', ); } diff --git a/Bugzilla/Elastic/Role/Search.pm b/Bugzilla/Elastic/Role/Search.pm deleted file mode 100644 index 9446e0da8..000000000 --- a/Bugzilla/Elastic/Role/Search.pm +++ /dev/null @@ -1,16 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::Elastic::Role::Search; - -use 5.10.1; -use strict; -use warnings; -use Role::Tiny; - -requires qw(data search_description invalid_order_columns order); - -1; diff --git a/Bugzilla/Elastic/Search.pm b/Bugzilla/Elastic/Search.pm deleted file mode 100644 index 5c60f2353..000000000 --- a/Bugzilla/Elastic/Search.pm +++ /dev/null @@ -1,425 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::Elastic::Search; - -use 5.10.1; -use Moo; -use Bugzilla::Search; -use Bugzilla::Search::Quicksearch; -use Bugzilla::Util qw(trick_taint); -use namespace::clean; - -use Bugzilla::Elastic::Search::FakeCGI; - - -has 'quicksearch' => ( is => 'ro' ); -has 'limit' => ( is => 'ro', predicate => 'has_limit' ); -has 'offset' => ( is => 'ro', predicate => 'has_offset' ); -has 'fields' => ( is => 'ro', isa => \&_arrayref_of_fields, default => sub { [] } ); -has 'params' => ( is => 'lazy' ); -has 'clause' => ( is => 'lazy' ); -has 'es_query' => ( is => 'lazy' ); -has 'search_description' => (is => 'lazy'); -has 'query_time' => ( is => 'rwp' ); - -has '_input_order' => ( is => 'ro', init_arg => 'order', required => 1); -has '_order' => ( is => 'lazy', init_arg => undef ); -has 'invalid_order_columns' => ( is => 'lazy' ); - -with 'Bugzilla::Elastic::Role::HasClient'; -with 'Bugzilla::Elastic::Role::HasIndexName'; -with 'Bugzilla::Elastic::Role::Search'; - -my @SUPPORTED_FIELDS = qw( - bug_id product component short_desc - priority status_whiteboard bug_status resolution - keywords alias assigned_to reporter delta_ts - longdesc cf_crash_signature classification bug_severity - commenter -); -my %IS_SUPPORTED_FIELD = map { $_ => 1 } @SUPPORTED_FIELDS; - -$IS_SUPPORTED_FIELD{relevance} = 1; - -my @NORMAL_FIELDS = qw( - priority - bug_severity - bug_status - resolution - product - component - classification - short_desc - assigned_to - reporter -); - -my %SORT_MAP = ( - bug_id => '_id', - relevance => '_score', - map { $_ => "$_.eq" } @NORMAL_FIELDS, -); - -my %EQUALS_MAP = ( - map { $_ => "$_.eq" } @NORMAL_FIELDS, -); - -sub _arrayref_of_fields { - my $f = $_; - foreach my $field (@$f) { - Bugzilla::Elastic::Search::UnsupportedField->throw(field => $field) - unless $IS_SUPPORTED_FIELD{$field}; - } -} - -sub data { - my ($self) = @_; - my $body = $self->es_query; - my $result = eval { - $self->client->search( - index => $self->index_name, - type => 'bug', - body => $body, - ); - }; - if (!$result) { - die $@; - } - $self->_set_query_time($result->{took} / 1000); - my (@ids, %hits); - my $fields = $self->fields; - foreach my $hit (@{ $result->{hits}{hits} }) { - push @ids, $hit->{_id}; - my $source = $hit->{_source}; - $source->{relevance} = $hit->{_score}; - foreach my $val (values %$source) { - next unless defined $val; - trick_taint($val); - } - trick_taint($hit->{_id}); - if ($source) { - $hits{$hit->{_id}} = [ @$source{@$fields} ]; - } - else { - $hits{$hit->{_id}} = $hit->{_id}; - } - } - my $visible_ids = Bugzilla->user->visible_bugs(\@ids); - - return [ map { $hits{$_} } @$visible_ids ]; -} - -sub _valid_order { - my ($self) = @_; - - return grep { $IS_SUPPORTED_FIELD{$_->[0]} } @{$self->_order}; -} - -sub order { - my ($self) = @_; - - return map { $_->[0] } $self->_valid_order; -} - -sub _quicksearch_to_params { - my ($quicksearch) = @_; - no warnings 'redefine'; - my $cgi = Bugzilla::Elastic::Search::FakeCGI->new; - local *Bugzilla::cgi = sub { $cgi }; - local $Bugzilla::Search::Quicksearch::ELASTIC = 1; - quicksearch($quicksearch); - - return $cgi->params; -} - -sub _build_fields { return \@SUPPORTED_FIELDS } - -sub _build__order { - my ($self) = @_; - - my @order; - foreach my $order (@{$self->_input_order}) { - if ($order =~ /^(.+)\s+(asc|desc)$/i) { - push @order, [ $1, lc $2 ]; - } - else { - push @order, [ $order ]; - } - } - return \@order; -} - -sub _build_invalid_order_columns { - my ($self) = @_; - - return [ map { $_->[0] } grep { !$IS_SUPPORTED_FIELD{$_->[0]} } @{ $self->_order } ]; -} - -sub _build_params { - my ($self) = @_; - - return _quicksearch_to_params($self->quicksearch); -} - -sub _build_clause { - my ($self) = @_; - my $search = Bugzilla::Search->new(params => $self->params); - - return $search->_params_to_data_structure; -} - -sub _build_search_description { - my ($self) = @_; - - return [_describe($self->clause)]; -} - -sub _describe { - my ($thing) = @_; - - state $class_to_func = { - 'Bugzilla::Search::Condition' => \&_describe_condition, - 'Bugzilla::Search::Clause' => \&_describe_clause - }; - - my $func = $class_to_func->{ref $thing} or die "nothing for $thing\n"; - - return $func->($thing); -} - -sub _describe_clause { - my ($clause) = @_; - - return map { _describe($_) } @{$clause->children}; -} - -sub _describe_condition { - my ($cond) = @_; - - return { field => $cond->field, type => $cond->operator, value => _describe_value($cond->value) }; -} - -sub _describe_value { - my ($val) = @_; - - return ref($val) ? join(", ", @$val) : $val; -} - -sub _build_es_query { - my ($self) = @_; - my @extra; - - if ($self->_valid_order) { - my @sort = map { - my $f = $SORT_MAP{$_->[0]} // $_->[0]; - @$_ > 1 ? { $f => lc $_[1] } : $f; - } $self->_valid_order; - push @extra, sort => \@sort; - } - if ($self->has_offset) { - push @extra, from => $self->offset; - } - my $max_limit = Bugzilla->params->{max_search_results}; - my $limit = Bugzilla->params->{default_search_limit}; - if ($self->has_limit) { - if ($self->limit) { - my $l = $self->limit; - $limit = $l < $max_limit ? $l : $max_limit; - } - else { - $limit = $max_limit; - } - } - push @extra, size => $limit; - return { - _source => @{$self->fields} ? \1 : \0, - query => _query($self->clause), - @extra, - }; -} - -sub _query { - my ($thing) = @_; - state $class_to_func = { - 'Bugzilla::Search::Condition' => \&_query_condition, - 'Bugzilla::Search::Clause' => \&_query_clause, - }; - - my $func = $class_to_func->{ref $thing} or die "nothing for $thing\n"; - - return $func->($thing); -} - -sub _query_condition { - my ($cond) = @_; - state $operator_to_es = { - equals => \&_operator_equals, - substring => \&_operator_substring, - anyexact => \&_operator_anyexact, - anywords => \&_operator_anywords, - allwords => \&_operator_allwords, - }; - - my $field = $cond->field; - my $operator = $cond->operator; - my $value = $cond->value; - - if ($field eq 'resolution') { - $value = [ map { $_ eq '---' ? '' : $_ } ref $value ? @$value : $value ]; - } - - unless ($IS_SUPPORTED_FIELD{$field}) { - Bugzilla::Elastic::Search::UnsupportedField->throw(field => $field); - } - - my $op = $operator_to_es->{$operator} - or Bugzilla::Elastic::Search::UnsupportedOperator->throw(operator => $operator); - - my $result; - if (ref $op) { - $result = $op->($field, $value); - } else { - $result = { $op => { $field => $value } }; - } - - return $result; -} - -# is equal to any of the strings -sub _operator_anyexact { - my ($field, $value) = @_; - my @values = ref $value ? @$value : split(/\s*,\s*/, $value); - if (@values == 1) { - return _operator_equals($field, $values[0]); - } - else { - return { - terms => { - $EQUALS_MAP{$field} // $field => [map { lc } @values], - minimum_should_match => 1, - }, - }; - } -} - -# contains any of the words -sub _operator_anywords { - my ($field, $value) = @_; - return { - match => { - $field => { query => $value, operator => "or" } - }, - }; -} - -# contains all of the words -sub _operator_allwords { - my ($field, $value) = @_; - return { - match => { - $field => { query => $value, operator => "and" } - }, - }; -} - -sub _operator_equals { - my ($field, $value) = @_; - return { - match => { - $EQUALS_MAP{$field} // $field => $value, - }, - }; -} - -sub _operator_substring { - my ($field, $value) = @_; - my $is_insider = Bugzilla->user->is_insider; - - if ($field eq 'longdesc') { - return { - has_child => { - type => 'comment', - query => { - bool => { - must => [ - { match => { body => { query => $value, operator => "and" } } }, - $is_insider ? () : { term => { is_private => \0 } }, - ], - }, - }, - }, - } - } - elsif ($field eq 'reporter' or $field eq 'assigned_to') { - return { - prefix => { - $EQUALS_MAP{$field} // $field => lc $value, - } - } - } - else { - return { - wildcard => { - $EQUALS_MAP{$field} // $field => lc "*$value*", - } - }; - } -} - -sub _query_clause { - my ($clause) = @_; - - state $joiner_to_func = { - AND => \&_join_and, - OR => \&_join_or, - }; - - my @children = grep { !$_->isa('Bugzilla::Search::Clause') || @{$_->children} } @{$clause->children}; - if (@children == 1) { - return _query($children[0]); - } - - return $joiner_to_func->{$clause->joiner}->([ map { _query($_) } @children ]); -} - -sub _join_and { - my ($children) = @_; - return { bool => { must => $children } }, -} - -sub _join_or { - my ($children) = @_; - return { bool => { should => $children } }; -} - -# Exceptions -BEGIN { - package Bugzilla::Elastic::Search::Redirect; - use Moo; - - with 'Throwable'; - - has 'redirect_args' => (is => 'ro', required => 1); - - package Bugzilla::Elastic::Search::UnsupportedField; - use Moo; - use overload q{""} => sub { "Unsupported field: ", $_[0]->field }, fallback => 1; - - with 'Throwable'; - - has 'field' => (is => 'ro', required => 1); - - - package Bugzilla::Elastic::Search::UnsupportedOperator; - use Moo; - - with 'Throwable'; - - has 'operator' => (is => 'ro', required => 1); -} - -1; diff --git a/Bugzilla/Elastic/Search/FakeCGI.pm b/Bugzilla/Elastic/Search/FakeCGI.pm deleted file mode 100644 index 827c96c52..000000000 --- a/Bugzilla/Elastic/Search/FakeCGI.pm +++ /dev/null @@ -1,43 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::Elastic::Search::FakeCGI; -use 5.10.1; -use Moo; -use namespace::clean; - -has 'params' => (is => 'ro', default => sub { {} }); - -# we pretend to be Bugzilla::CGI at times. -sub canonicalise_query { - return Bugzilla::CGI::canonicalise_query(@_); -} - -sub delete { - my ($self, $key) = @_; - delete $self->params->{$key}; -} - -sub redirect { - my ($self, @args) = @_; - - Bugzilla::Elastic::Search::Redirect->throw(redirect_args => \@args); -} - -sub param { - my ($self, $key, $val, @rest) = @_; - if (@_ > 3) { - $self->params->{$key} = [$val, @rest]; - } elsif (@_ == 3) { - $self->params->{$key} = $val; - } elsif (@_ == 2) { - return $self->params->{$key}; - } else { - return $self->params - } -} - -1; diff --git a/Bugzilla/Search/Quicksearch.pm b/Bugzilla/Search/Quicksearch.pm index 462a9ba85..4f11a3f54 100644 --- a/Bugzilla/Search/Quicksearch.pm +++ b/Bugzilla/Search/Quicksearch.pm @@ -127,7 +127,7 @@ use constant COMPONENT_EXCEPTIONS => ( ); # Quicksearch-wide globals for boolean charts. -our ($chart, $and, $or, $fulltext, $bug_status_set, $ELASTIC); +our ($chart, $and, $or, $fulltext, $bug_status_set); sub quicksearch { my ($searchstring) = (@_); @@ -587,8 +587,7 @@ sub _default_quicksearch_word { addChart('alias', 'substring', $word, $negate); addChart('short_desc', 'substring', $word, $negate); addChart('status_whiteboard', 'substring', $word, $negate); - addChart('longdesc', 'substring', $word, $negate) if $ELASTIC; - addChart('content', 'matches', _matches_phrase($word), $negate) if $fulltext && !$ELASTIC; + addChart('content', 'matches', _matches_phrase($word), $negate) if $fulltext; # BMO Bug 664124 - Include the crash signature (sig:) field in default quicksearches addChart('cf_crash_signature', 'substring', $word, $negate); @@ -618,7 +617,6 @@ sub _handle_urls { # Quote and escape a phrase appropriately for a "content matches" search. sub _matches_phrase { my ($phrase) = @_; - return $phrase if $ELASTIC; $phrase =~ s/"/\\"/g; return "\"$phrase\""; } diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index e8ddc0be7..69885f57c 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -128,7 +128,7 @@ with 'Bugzilla::Elastic::Role::Object'; sub ES_TYPE { 'user' } -sub ES_OBJECTS_AT_ONCE { 5000 } +sub ES_OBJECTS_AT_ONCE { 2000 } sub ES_SELECT_UPDATED_SQL { my ($class, $mtime) = @_; @@ -150,7 +150,7 @@ sub ES_SELECT_ALL_SQL { my $id = $class->ID_FIELD; my $table = $class->DB_TABLE; - return ("SELECT $id FROM $table WHERE $id > ? AND is_enabled AND NOT disabledtext ORDER BY $id", [$last_id // 0]); + return ("SELECT $id FROM $table WHERE $id > ? AND is_enabled ORDER BY $id", [$last_id // 0]); } sub ES_PROPERTIES { @@ -175,6 +175,7 @@ sub ES_PROPERTIES { sub es_document { my ( $self, $timestamp ) = @_; + my $weight = eval { $self->last_activity_ts ? datetime_from($self->last_activity_ts)->epoch : 0 } // 0; my $doc = { login => $self->login, name => $self->name, @@ -183,6 +184,7 @@ sub es_document { input => [ $self->login, $self->name ], output => $self->identity, payload => { name => $self->login, real_name => $self->name }, + weight => $weight, }, }; if ($self->name && $self->name =~ /:(\w+)/) { @@ -191,6 +193,7 @@ sub es_document { input => [ $ircnick ], output => $self->login, payload => { name => $self->login, real_name => $self->name, ircnick => $ircnick }, + weight => $weight, }; } diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm index 1399513c5..bf3a93fd5 100644 --- a/Bugzilla/WebService/Constants.pm +++ b/Bugzilla/WebService/Constants.pm @@ -290,7 +290,6 @@ sub WS_DISPATCH { 'Product' => 'Bugzilla::WebService::Product', 'Group' => 'Bugzilla::WebService::Group', 'BugUserLastVisit' => 'Bugzilla::WebService::BugUserLastVisit', - 'Elastic' => 'Bugzilla::WebService::Elastic', %hook_dispatch }; return $dispatch; diff --git a/Bugzilla/WebService/Elastic.pm b/Bugzilla/WebService/Elastic.pm deleted file mode 100644 index 3a33a1dba..000000000 --- a/Bugzilla/WebService/Elastic.pm +++ /dev/null @@ -1,59 +0,0 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- -# -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Bugzilla Bug Tracking System. -# -# Contributor(s): Marc Schumann <wurblzap@gmail.com> -# Max Kanat-Alexander <mkanat@bugzilla.org> -# Mads Bondo Dydensborg <mbd@dbc.dk> -# Noura Elhawary <nelhawar@redhat.com> - -package Bugzilla::WebService::Elastic; - -use 5.10.1; -use strict; -use warnings; -use base qw(Bugzilla::WebService); - -use Bugzilla; -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::WebService::Util qw(validate); -use Bugzilla::Util qw(trim detaint_natural trick_taint); - -use constant READ_ONLY => qw( suggest_users ); -use constant PUBLIC_METHODS => qw( suggest_users ); - -sub suggest_users { - my ($self, $params) = @_; - - Bugzilla->switch_to_shadow_db(); - - ThrowCodeError('params_required', { function => 'Elastic.suggest_users', params => ['match'] }) - unless defined $params->{match}; - - ThrowUserError('user_access_by_match_denied') - unless Bugzilla->user->id; - - trick_taint($params->{match}); - my $results = Bugzilla->elastic->suggest_users($params->{match} . ""); - my @users = map { - { - real_name => $self->type(string => $_->{real_name}), - name => $self->type(email => $_->{name}), - } - } @$results; - - return { users => \@users }; -} - -1;
\ No newline at end of file diff --git a/Bugzilla/WebService/Server/REST.pm b/Bugzilla/WebService/Server/REST.pm index 6e1944061..d9381b2c8 100644 --- a/Bugzilla/WebService/Server/REST.pm +++ b/Bugzilla/WebService/Server/REST.pm @@ -29,7 +29,6 @@ use Bugzilla::WebService::Server::REST::Resources::Group; use Bugzilla::WebService::Server::REST::Resources::Product; use Bugzilla::WebService::Server::REST::Resources::User; use Bugzilla::WebService::Server::REST::Resources::BugUserLastVisit; -use Bugzilla::WebService::Server::REST::Resources::Elastic; use List::MoreUtils qw(uniq); use Scalar::Util qw(blessed reftype); diff --git a/Bugzilla/WebService/Server/REST/Resources/Elastic.pm b/Bugzilla/WebService/Server/REST/Resources/Elastic.pm deleted file mode 100644 index 2f7c1eaa4..000000000 --- a/Bugzilla/WebService/Server/REST/Resources/Elastic.pm +++ /dev/null @@ -1,30 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::WebService::Server::REST::Resources::Elastic; - -use 5.10.1; -use strict; -use warnings; - -use Bugzilla::WebService::Constants; -use Bugzilla::WebService::Elastic; - -BEGIN { - *Bugzilla::WebService::Elastic::rest_resources = \&_rest_resources; -}; - -sub _rest_resources { - my $rest_resources = [ - qr{^/elastic/suggest_users$}, { - GET => { method => 'suggest_users' }, - }, - ]; - return $rest_resources; -} - -1; diff --git a/buglist.cgi b/buglist.cgi index fa6bb060f..850d79d01 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -687,42 +687,11 @@ if ($format->{'extension'} eq 'html' && !defined $params->param('limit')) { $vars->{'default_limited'} = 1; } -my $fallback_search = Bugzilla::Search->new(fields => [@selectcolumns], - params => scalar $params->Vars, - order => [@order_columns], - sharer => $sharer_id); - -my $search; -my $elastic = $cgi->param('elastic') // 1; -if (defined $cgi->param('elastic')) { - $vars->{was_elastic} = 1; -} -if ($elastic) { - local $SIG{__DIE__} = undef; - local $SIG{__WARN__} = undef; - my $ok = eval { - my @args = ( params => scalar $params->Vars ); - if ($searchstring) { - @args = (quicksearch => $searchstring); - } - if (defined $params->param('limit')) { - push @args, limit => scalar $params->param('limit'); - } - $search = Bugzilla::Elastic::Search->new( - fields => [@selectcolumns], - order => [@order_columns], - @args, - ); - $search->es_query; - 1; - }; - if (!$ok) { - warn "fallback from elasticsearch: $@\n"; - $search = $fallback_search; - } -} else { - $search = $fallback_search; -} +# Generate the basic SQL query that will be used to generate the bug list. +my $search = new Bugzilla::Search('fields' => \@selectcolumns, + 'params' => scalar $params->Vars, + 'order' => \@order_columns, + 'sharer' => $sharer_id); $order = join(',', $search->order); @@ -766,44 +735,25 @@ $::SIG{TERM} = 'DEFAULT'; $::SIG{PIPE} = 'DEFAULT'; # Execute the query. -my ($data, $extra_data); -do { - local $SIG{__DIE__} = undef; - local $SIG{__WARN__} = undef; - ($data, $extra_data) = eval { $search->data }; -}; - -if ($elastic && not defined $data) { - warn "fallback from elasticsearch: $@\n"; - $search = $fallback_search; - ($data, $extra_data) = $search->data; - $elastic = 0; -} - -$fulltext = 1 if $elastic; - +my ($data, $extra_data) = $search->data; $vars->{'search_description'} = $search->search_description; + if ($cgi->param('debug') && Bugzilla->params->{debug_group} && $user->in_group(Bugzilla->params->{debug_group}) ) { $vars->{'debug'} = 1; - if ($search->isa('Bugzilla::Elastic::Search')) { - $vars->{query_time} = $search->query_time; - } - else { - $vars->{'queries'} = $extra_data; - my $query_time = 0; - $query_time += $_->{'time'} foreach @$extra_data; - $vars->{'query_time'} = $query_time; - # Explains are limited to admins because you could use them to figure - # out how many hidden bugs are in a particular product (by doing - # searches and looking at the number of rows the explain says it's - # examining). - if ($user->in_group('admin')) { - foreach my $query (@$extra_data) { - $query->{explain} = $dbh->bz_explain($query->{sql}); - } + $vars->{'queries'} = $extra_data; + my $query_time = 0; + $query_time += $_->{'time'} foreach @$extra_data; + $vars->{'query_time'} = $query_time; + # Explains are limited to admins because you could use them to figure + # out how many hidden bugs are in a particular product (by doing + # searches and looking at the number of rows the explain says it's + # examining). + if ($user->in_group('admin')) { + foreach my $query (@$extra_data) { + $query->{explain} = $dbh->bz_explain($query->{sql}); } } } @@ -935,15 +885,6 @@ else { # remaining_time <= 0 # Define the variables and functions that will be passed to the UI template. -if ($vars->{elastic} = $search->isa('Bugzilla::Elastic::Search')) { - $vars->{elastic_query_time} = $search->query_time; -} -else { - my $query_time = 0; - $query_time += $_->{'time'} foreach @$extra_data; - $vars->{'query_time'} = $query_time; -} - $vars->{'bugs'} = \@bugs; $vars->{'buglist'} = \@bugidlist; $vars->{'buglist_joined'} = join(',', @bugidlist); diff --git a/js/field.js b/js/field.js index b514fa53c..349e2fae3 100644 --- a/js/field.js +++ b/js/field.js @@ -713,13 +713,15 @@ $(function() { } var options_user = { - serviceUrl: 'rest/elastic/suggest_users', + serviceUrl: 'rest/user', params: { Bugzilla_api_token: BUGZILLA.api_token, + include_fields: 'name,real_name', + limit: 100 }, paramName: 'match', deferRequestBy: 250, - minChars: 2, + minChars: 3, tabDisabled: true, autoSelectFirst: true, triggerSelectOnValidInput: false, diff --git a/scripts/search.pl b/scripts/search.pl deleted file mode 100644 index 6e0f7245d..000000000 --- a/scripts/search.pl +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use Bugzilla; -use JSON '-convert_blessed_universally'; - -print JSON->new->pretty->encode( - Bugzilla::Elastic::Search->new( - quicksearch => "@ARGV", - fields => ['bug_id', 'short_desc'], - order => ['bug_id'], - )->es_query -); diff --git a/scripts/suggest-user.pl b/scripts/suggest-user.pl deleted file mode 100644 index dcf24da87..000000000 --- a/scripts/suggest-user.pl +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use FindBin qw($RealBin); -use lib ($RealBin); -use Bugzilla; -use Search::Elasticsearch; -use Bugzilla::Elastic; - -my $elastic = Bugzilla::Elastic->new( - es_client => Search::Elasticsearch->new() -); -my $user = Bugzilla::User->check({name => 'dylan@mozilla.com'}); -Bugzilla->set_user($user); -my $users; - -for (1..4) { - $users = $elastic->suggest_users($ARGV[0]); -} -print "$_->{name}\n" for @$users; diff --git a/template/en/default/list/list.html.tmpl b/template/en/default/list/list.html.tmpl index 051382a21..5e154f5df 100644 --- a/template/en/default/list/list.html.tmpl +++ b/template/en/default/list/list.html.tmpl @@ -317,13 +317,5 @@ [% ELSE %] [% bugs.size %] [%+ terms.bugs %] found. [% END %] - [% IF elastic %] - <br> - ElasticSearch took [% elastic_query_time FILTER html %] seconds. - <a href="buglist.cgi?[% urlquerypart FILTER html %]&elastic=0">Try without ElasticSearch</a> - [% ELSIF was_elastic %] - <br> - Search took [% query_time FILTER html %] seconds. - [% END %] </span> [% END %] |