gitlab-ce/lib/api
Robert Speicher 6591b594d4 Merge branch '15356-filters-should-change-issue-counts' into 'master'
Take filters in account in issuable counters

## What does this MR do?

This merge request ensure we display issuable counters that take in account all the selected filters, solving #15356.

## Are there points in the code the reviewer needs to double check?

There was an issue (#22414) in the original implementation (!4960) when more than one label was selected because calling `#count` when the ActiveRecordRelation contains a `.group` returns an OrderedHash. This merge request relies on [how Kaminari handle this case](https://github.com/amatsuda/kaminari/blob/master/lib/kaminari/models/active_record_relation_methods.rb#L24-L30).

A few things to note:
- The `COUNT` query issued by Kaminari for the pagination is now cached because it's already run by `ApplicationHelper#state_filters_text_for`, so in the end we issue one less SQL query than before;
- In the case when more than one label are selected, the `COUNT` queries return an OrderedHash in the form `{ ISSUABLE_ID => COUNT_OF_SELECTED_FILTERS }` on which `#count` is called: this drawback is already in place (for instance when loading https://gitlab.com/gitlab-org/gitlab-ce/issues?scope=all&state=all&utf8=%E2%9C%93&label_name%5B%5D=bug&label_name%5B%5D=regression) since that's how Kaminari solves this, **the difference is that now we do that two more times for the two states that are not currently selected**. I will let the ~Performance team decide if that's something acceptable or not, otherwise we will have to find another solution...
- The queries that count the # of issuable are a bit more complex than before, from:

    ```
   (0.6ms)  SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1 AND ("issues"."state" IN ('opened','reopened'))  [["project_id", 2]]
   (0.2ms)  SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1 AND ("issues"."state" IN ('closed'))  [["project_id", 2]]
   (0.2ms)  SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1  [["project_id", 2]]
    ```

    to

    ```
   (0.7ms)  SELECT COUNT(*) AS count_all, "issues"."id" AS issues_id FROM "issues" INNER JOIN "label_links" ON "label_links"."target_id" = "issues"."id" AND "label_links"."target_type" = $1 INNER JOIN "labels" ON "labels"."id" = "label_links"."label_id" WHERE "issues"."deleted_at" IS NULL AND ("issues"."state" IN ('opened','reopened')) AND "issues"."project_id" = 2 AND "labels"."title" IN ('bug', 'discussion') AND "labels"."project_id" = 2 GROUP BY "issues"."id" HAVING COUNT(DISTINCT labels.title) = 2  [["target_type", "Issue"]]
   (0.5ms)  SELECT COUNT(*) AS count_all, "issues"."id" AS issues_id FROM "issues" INNER JOIN "label_links" ON "label_links"."target_id" = "issues"."id" AND "label_links"."target_type" = $1 INNER JOIN "labels" ON "labels"."id" = "label_links"."label_id" WHERE "issues"."deleted_at" IS NULL AND ("issues"."state" IN ('closed')) AND "issues"."project_id" = 2 AND "labels"."title" IN ('bug', 'discussion') AND "labels"."project_id" = 2 GROUP BY "issues"."id" HAVING COUNT(DISTINCT labels.title) = 2  [["target_type", "Issue"]]
   (0.5ms)  SELECT COUNT(*) AS count_all, "issues"."id" AS issues_id FROM "issues" INNER JOIN "label_links" ON "label_links"."target_id" = "issues"."id" AND "label_links"."target_type" = $1 INNER JOIN "labels" ON "labels"."id" = "label_links"."label_id" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = 2 AND "labels"."title" IN ('bug', 'discussion') AND "labels"."project_id" = 2 GROUP BY "issues"."id" HAVING COUNT(DISTINCT labels.title) = 2  [["target_type", "Issue"]]
    ```
- We could cache the counters for a few minutes? The key could be `PROJECT_ID-ISSUABLE_TYPE-PARAMS`.

A few possible arguments in favor of "it's an acceptable solution":
- most of the time people filter with a single label => no performance problem here
- when filtering with more than one label, usually the result set is reduced, limiting the performance issues 

## What are the relevant issue numbers?

Closes #15356

See merge request !6496
2016-09-30 11:11:04 +00:00
..
helpers
access_requests.rb New AccessRequestsFinder 2016-09-28 08:46:59 +02:00
api.rb Keep API mounts in alphabetical order 2016-09-25 11:28:23 +03:00
api_guard.rb Remove some dead code from the Grape API 2016-09-19 12:27:36 +01:00
award_emoji.rb Fix tests for Snippets toggling awards 2016-09-19 19:53:09 +03:00
branches.rb
broadcast_messages.rb
builds.rb
commit_statuses.rb Fix an error where we were unable to create a CommitStatus for running state 2016-09-12 12:20:18 +02:00
commits.rb
deploy_keys.rb
deployments.rb
entities.rb Expose the Koding application settings in the API 2016-09-29 09:12:52 -07:00
environments.rb
files.rb Add optional 'author' param when making commits 2016-09-19 10:00:26 -07:00
groups.rb Allow to set request_access_enabled for groups and projects using API 2016-09-19 12:13:57 -03:00
helpers.rb Enable Warden for the Grape API 2016-09-19 12:27:37 +01:00
internal.rb Handle LFS token creation and retrieval in the same method, and in the same Redis connection. 2016-09-28 12:13:48 -05:00
issues.rb Fix API issues sorting 2016-09-09 16:10:01 +01:00
keys.rb
labels.rb
license_templates.rb
lint.rb Improve curl commend, remove blank lines 2016-09-07 12:54:02 +02:00
members.rb Ensure invitees are not returned in Members API 2016-09-16 17:18:26 +02:00
merge_request_diffs.rb
merge_requests.rb
milestones.rb Small improvements thanks to Robert's feedback 2016-09-30 12:02:54 +02:00
namespaces.rb
notes.rb Fix API notes endpoint when posting only emoji 2016-09-16 16:28:53 +02:00
notification_settings.rb Add notification_settings API calls 2016-09-09 17:08:06 +00:00
pipelines.rb Use PipelinesFinder in Pipelines API 2016-09-07 15:38:03 +02:00
project_hooks.rb
project_snippets.rb
projects.rb Expose project share expiration_date field on API 2016-09-28 10:12:49 -03:00
repositories.rb
runners.rb
services.rb
session.rb
settings.rb
sidekiq_metrics.rb
subscriptions.rb
system_hooks.rb
tags.rb
templates.rb
todos.rb
triggers.rb
users.rb Add User#organization to users api 2016-09-27 14:04:39 +03:00
variables.rb