debian-mirror-gitlab/lib/event_filter.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

227 lines
7.5 KiB
Ruby
Raw Normal View History

2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2014-09-02 18:07:02 +05:30
2022-06-21 17:19:12 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2018-12-05 23:21:45 +05:30
class EventFilter
2020-07-28 23:09:34 +05:30
include Gitlab::Utils::StrongMemoize
2018-12-05 23:21:45 +05:30
attr_accessor :filter
ALL = 'all'
PUSH = 'push'
MERGED = 'merged'
ISSUE = 'issue'
COMMENTS = 'comments'
TEAM = 'team'
2020-04-22 19:07:51 +05:30
WIKI = 'wiki'
2020-07-28 23:09:34 +05:30
DESIGNS = 'designs'
2018-12-05 23:21:45 +05:30
2022-07-16 23:28:13 +05:30
PROJECT_ONLY_EVENT_TYPES = [PUSH, MERGED, TEAM, ISSUE, DESIGNS].freeze
2018-12-05 23:21:45 +05:30
def initialize(filter)
# Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
filter = filter.to_s.split(',')[0].to_s
2019-12-21 20:55:43 +05:30
@filter = filters.include?(filter) ? filter : ALL
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
def active?(key)
filter == key.to_s
2014-09-02 18:07:02 +05:30
end
2015-04-26 12:48:37 +05:30
def apply_filter(events)
2018-12-05 23:21:45 +05:30
case filter
when PUSH
2020-06-23 00:09:42 +05:30
events.pushed_action
2018-12-05 23:21:45 +05:30
when MERGED
2020-06-23 00:09:42 +05:30
events.merged_action
2018-12-05 23:21:45 +05:30
when COMMENTS
2020-06-23 00:09:42 +05:30
events.commented_action
2018-12-05 23:21:45 +05:30
when TEAM
2022-06-21 17:19:12 +05:30
events.where(action: Event::TEAM_ACTIONS)
2018-12-05 23:21:45 +05:30
when ISSUE
2022-06-21 17:19:12 +05:30
events.where(action: Event::ISSUE_ACTIONS, target_type: 'Issue')
2020-04-22 19:07:51 +05:30
when WIKI
wiki_events(events)
2020-07-28 23:09:34 +05:30
when DESIGNS
design_events(events)
2017-08-17 22:00:37 +05:30
else
2018-12-05 23:21:45 +05:30
events
2017-08-17 22:00:37 +05:30
end
2014-09-02 18:07:02 +05:30
end
2022-06-21 17:19:12 +05:30
# rubocop: disable Metrics/CyclomaticComplexity
# This method build specialized in-operator optimized queries based on different
# filter parameters. All queries will benefit from the index covering the following columns:
2022-07-16 23:28:13 +05:30
# * author_id target_type action id
# * project_id target_type action id
# * group_id target_type action id
2022-06-21 17:19:12 +05:30
#
# More context: https://docs.gitlab.com/ee/development/database/efficient_in_operator_queries.html#the-inoperatoroptimization-module
2022-07-16 23:28:13 +05:30
def in_operator_query_builder_params(array_data)
2022-06-21 17:19:12 +05:30
case filter
when ALL
2022-07-16 23:28:13 +05:30
in_operator_params(array_data: array_data)
2022-06-21 17:19:12 +05:30
when PUSH
# Here we need to add an order hint column to force the correct index usage.
# Without the order hint, the following conditions will use the `index_events_on_author_id_and_id`
# index which is not as efficient as the `index_events_for_followed_users` index.
# > target_type IS NULL AND action = 5 AND author_id = X ORDER BY id DESC
#
# The order hint adds an extra order by column which doesn't affect the result but forces the planner
# to use the correct index:
# > target_type IS NULL AND action = 5 AND author_id = X ORDER BY target_type DESC, id DESC
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.where(target_type: nil).pushed_action,
order_hint_column: :target_type
)
when MERGED
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.where(target_type: MergeRequest.to_s).merged_action
)
when COMMENTS
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.commented_action,
in_column: :target_type,
in_values: [Note, *Note.descendants].map(&:name) # To make the query efficient we need to list all Note classes
)
when TEAM
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.where(target_type: nil),
order_hint_column: :target_type,
in_column: :action,
in_values: Event.actions.values_at(*Event::TEAM_ACTIONS)
)
when ISSUE
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.where(target_type: Issue.name),
in_column: :action,
in_values: Event.actions.values_at(*Event::ISSUE_ACTIONS)
)
when WIKI
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.for_wiki_page,
in_column: :action,
in_values: Event.actions.values_at(*Event::WIKI_ACTIONS)
)
when DESIGNS
in_operator_params(
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
scope: Event.for_design,
in_column: :action,
in_values: Event.actions.values_at(*Event::DESIGN_ACTIONS)
)
else
2022-07-16 23:28:13 +05:30
in_operator_params(array_data: array_data)
2022-06-21 17:19:12 +05:30
end
end
# rubocop: enable Metrics/CyclomaticComplexity
2019-12-21 20:55:43 +05:30
private
2022-07-16 23:28:13 +05:30
def in_operator_params(array_data:, scope: nil, in_column: nil, in_values: nil, order_hint_column: nil)
2022-06-21 17:19:12 +05:30
base_scope = Event.all
base_scope = base_scope.merge(scope) if scope
order = { id: :desc }
finder_query = -> (id_expression) { Event.where(Event.arel_table[:id].eq(id_expression)) }
if order_hint_column.present?
2022-10-11 01:57:18 +05:30
order = Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: order_hint_column,
order_expression: Event.arel_table[order_hint_column].desc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Event.arel_table[:id].desc
)
])
2022-06-21 17:19:12 +05:30
finder_query = -> (_order_hint, id_expression) { Event.where(Event.arel_table[:id].eq(id_expression)) }
end
base_scope = base_scope.reorder(order)
array_params = in_operator_array_params(
scope: base_scope,
2022-07-16 23:28:13 +05:30
array_data: array_data,
2022-06-21 17:19:12 +05:30
in_column: in_column,
in_values: in_values
)
array_params.merge(
scope: base_scope,
finder_query: finder_query
)
end
# This method builds the array_ parameters
# without in_column parameter: uses one IN filter: author_id
# with in_column: two IN filters: author_id, (target_type OR action)
2022-07-16 23:28:13 +05:30
# @param array_data [Hash] Must contain the scope_ids, scope_model, mapping_column keys
def in_operator_array_params(scope:, array_data:, in_column: nil, in_values: nil)
array_scope_ids = array_data[:scope_ids]
array_scope_model = array_data[:scope_model]
array_mapping_column = array_data[:mapping_column]
# Adding non-existent record to generate valid SQL if array_scope_ids is empty
array_scope_ids << 0 if array_scope_ids.empty?
2022-06-21 17:19:12 +05:30
if in_column
2022-07-16 23:28:13 +05:30
# Builds Cartesian product of the in_values and the array_scope_ids (in this case: user_ids).
2022-06-21 17:19:12 +05:30
# The process is described here: https://docs.gitlab.com/ee/development/database/efficient_in_operator_queries.html#multiple-in-queries
# VALUES ((array_scope_ids[0], in_values[0]), (array_scope_ids[1], in_values[0]) ...)
cartesian = array_scope_ids.product(in_values)
2022-07-16 23:28:13 +05:30
column_list = Arel::Nodes::ValuesList.new(cartesian)
2022-06-21 17:19:12 +05:30
as = "array_ids(id, #{Event.connection.quote_column_name(in_column)})"
2022-07-16 23:28:13 +05:30
from = Arel::Nodes::Grouping.new(column_list).as(as)
2022-06-21 17:19:12 +05:30
{
2022-07-16 23:28:13 +05:30
array_scope: array_scope_model.select(:id, in_column).from(from),
array_mapping_scope: -> (primary_id_expression, in_column_expression) do
2022-06-21 17:19:12 +05:30
Event
.merge(scope)
2022-07-16 23:28:13 +05:30
.where(Event.arel_table[array_mapping_column].eq(primary_id_expression))
2022-06-21 17:19:12 +05:30
.where(Event.arel_table[in_column].eq(in_column_expression))
end
}
else
# Builds a simple query to represent the array_scope_ids
# VALUES ((array_scope_ids[0]), (array_scope_ids[2])...)
array_ids_list = Arel::Nodes::ValuesList.new(array_scope_ids.map { |id| [id] })
from = Arel::Nodes::Grouping.new(array_ids_list).as('array_ids(id)')
{
2022-07-16 23:28:13 +05:30
array_scope: array_scope_model.select(:id).from(from),
array_mapping_scope: -> (primary_id_expression) do
2022-06-21 17:19:12 +05:30
Event
.merge(scope)
2022-07-16 23:28:13 +05:30
.where(Event.arel_table[array_mapping_column].eq(primary_id_expression))
2022-06-21 17:19:12 +05:30
end
}
end
end
2020-04-22 19:07:51 +05:30
def wiki_events(events)
events.for_wiki_page
end
2020-07-28 23:09:34 +05:30
def design_events(events)
events.for_design
end
2019-12-21 20:55:43 +05:30
def filters
2020-07-28 23:09:34 +05:30
[ALL, PUSH, MERGED, ISSUE, COMMENTS, TEAM, WIKI, DESIGNS]
2019-12-21 20:55:43 +05:30
end
2014-09-02 18:07:02 +05:30
end
2022-06-21 17:19:12 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2019-12-21 20:55:43 +05:30
2021-06-08 01:23:25 +05:30
EventFilter.prepend_mod_with('EventFilter')