Project

General

Profile

Patch #33431 » time_query_performace_v4.patch

Alexander Meindl, 2022-11-24 12:23

View differences:

app/controllers/timelog_controller.rb
49 49

  
50 50
    respond_to do |format|
51 51
      format.html do
52
        @entry_count = scope.count
52
        @entry_count = @query.query_count
53 53
        @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
54 54
        @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a
55 55

  
56 56
        render :layout => !request.xhr?
57 57
      end
58 58
      format.api do
59
        @entry_count = scope.count
59
        @entry_count = @query.query_count
60 60
        @offset, @limit = api_offset_and_limit
61 61
        @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).to_a
62 62
      end
app/models/time_entry_query.rb
141 141
    end
142 142
  end
143 143

  
144
  def query_count
145
    base_count_scope.count
146
  rescue ::ActiveRecord::StatementInvalid => e
147
    raise StatementInvalid.new(e.message)
148
  end
149

  
150
  def base_group_scope
151
    base_result_scope.
152
      joins(joins_for_order_statement(group_by_statement)).
153
      group(group_by_statement)
154
  end
155

  
156
  def total_for(column)
157
    total_with_scope(column, base_result_scope)
158
  end
159

  
144 160
  def base_scope
145
    TimeEntry.visible.
146
      joins(:project, :user).
147
      includes(:activity).
148
      references(:activity).
149
      left_join_issue.
150
      where(statement)
161
    TimeEntry.visible.where(statement)
162
  end
163

  
164
  def base_result_scope
165
    base_scope.joins(:activity, :user)
166
              .includes(:activity)
167
              .left_join_issue
168
              .where("#{Enumeration.table_name}.project_id = #{TimeEntry.table_name}.project_id " \
169
                     "AND #{Enumeration.table_name}.project_id = #{Project.table_name}.id " \
170
                     "OR #{Enumeration.table_name}.project_id IS NULL")
171
  end
172

  
173
  def base_count_scope
174
    scope = base_scope
175
    scope = scope.joins(:activity) if base_count_with_activity?
176
    scope = scope.joins(:user) if base_count_with_user_join?
177
    scope = scope.left_join_issue if base_count_with_issues?
178
    scope
179
  end
180

  
181
  def base_count_with_activity?
182
    has_filter?('activity_id') || group_by_column&.name == :activity
183
  end
184

  
185
  def base_count_with_user_join?
186
    has_filter?('user') || has_filter?('author') || group_by_column&.name == :user
187
  end
188

  
189
  def base_count_with_issues?
190
    filters&.map(&:first)&.detect { |f| f.start_with?('issue.') } || group_by_column&.name == :issue
151 191
  end
152 192

  
153 193
  def results_scope(options={})
154 194
    order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?)
155 195

  
156 196
    order_option << "#{TimeEntry.table_name}.id ASC"
157
    base_scope.
197
    base_result_scope.
158 198
      order(order_option).
159 199
      joins(joins_for_order_statement(order_option.join(',')))
160 200
  end
test/unit/time_entry_query_test.rb
143 143
    time_entry_ids = q.results_scope.pluck(:id)
144 144
    paginated_time_entry_ids = []
145 145
    # Test with a maximum of 2 records per page.
146
    ((q.results_scope.count / 2) + 1).times do |i|
146
    ((q.query_count / 2) + 1).times do |i|
147 147
      paginated_time_entry_ids += q.results_scope.offset((i * 2)).limit(2).pluck(:id)
148 148
    end
149 149

  
150 150
    # Non-paginated time entry ids and paginated time entry ids should be in the same order.
151 151
    assert_equal time_entry_ids, paginated_time_entry_ids
152 152
  end
153

  
154
  def test_query_count
155
    q = TimeEntryQuery.new(:name => '_')
156
    entry_count = q.query_count
157
    assert_equal q.results_scope.size, entry_count
158
  end
153 159
end
(4-4/5)