Skip to content

Support filtering by scoped label wildcards

What does this MR do and why?

Allows filtering issues, merge requests, and epics by scoped label wildcards using the <scope>::* syntax.

This only applies to the project / group issues, merge requests, and epic lists. Dashboard pages are not included.

I excluded the dashboard pages because the query does not perform very well in some cases on the dashboard pages because that goes through a different code path that does not use the optimization we introduced in !34503 (merged). !71020 (diffs) has extra details.

Related to #12285 (closed)

Migration output
== 20210921063924 IndexLabelsUsingVarcharPatternOps: migrating ================
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, :title, {:order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_title_varchar", :algorithm=>:concurrently})
   -> 0.0054s
-- add_index(:labels, :title, {:order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_title_varchar", :algorithm=>:concurrently})
   -> 0.0070s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, [:project_id, :title], {:where=>"labels.group_id IS NULL", :unique=>true, :order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_project_id_and_title_varchar_unique", :algorithm=>:concurrently})
   -> 0.0041s
-- add_index(:labels, [:project_id, :title], {:where=>"labels.group_id IS NULL", :unique=>true, :order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_project_id_and_title_varchar_unique", :algorithm=>:concurrently})
   -> 0.0050s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, [:group_id, :title], {:where=>"labels.project_id IS NULL", :unique=>true, :order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_group_id_and_title_varchar_unique", :algorithm=>:concurrently})
   -> 0.0048s
-- add_index(:labels, [:group_id, :title], {:where=>"labels.project_id IS NULL", :unique=>true, :order=>{:title=>:varchar_pattern_ops}, :name=>"index_labels_on_group_id_and_title_varchar_unique", :algorithm=>:concurrently})
   -> 0.0047s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, :group_id, {:name=>"index_labels_on_group_id", :algorithm=>:concurrently})
   -> 0.0044s
-- add_index(:labels, :group_id, {:name=>"index_labels_on_group_id", :algorithm=>:concurrently})
   -> 0.0053s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0052s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_title"})
   -> 0.0035s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0046s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_project_id_and_title_unique"})
   -> 0.0029s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0042s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_group_id_and_title_unique"})
   -> 0.0033s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0039s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_group_id_and_project_id_and_title"})
   -> 0.0031s
== 20210921063924 IndexLabelsUsingVarcharPatternOps: migrated (0.0872s) =======

== 20210921063924 IndexLabelsUsingVarcharPatternOps: reverting ================
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, :title, {:name=>"index_labels_on_title", :algorithm=>:concurrently})
   -> 0.0051s
-- add_index(:labels, :title, {:name=>"index_labels_on_title", :algorithm=>:concurrently})
   -> 0.0073s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, [:project_id, :title], {:where=>"labels.group_id IS NULL", :unique=>true, :name=>"index_labels_on_project_id_and_title_unique", :algorithm=>:concurrently})
   -> 0.0039s
-- add_index(:labels, [:project_id, :title], {:where=>"labels.group_id IS NULL", :unique=>true, :name=>"index_labels_on_project_id_and_title_unique", :algorithm=>:concurrently})
   -> 0.0065s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, [:group_id, :title], {:where=>"labels.project_id IS NULL", :unique=>true, :name=>"index_labels_on_group_id_and_title_unique", :algorithm=>:concurrently})
   -> 0.0045s
-- add_index(:labels, [:group_id, :title], {:where=>"labels.project_id IS NULL", :unique=>true, :name=>"index_labels_on_group_id_and_title_unique", :algorithm=>:concurrently})
   -> 0.0049s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:labels, [:group_id, :project_id, :title], {:unique=>true, :name=>"index_labels_on_group_id_and_project_id_and_title", :algorithm=>:concurrently})
   -> 0.0053s
-- add_index(:labels, [:group_id, :project_id, :title], {:unique=>true, :name=>"index_labels_on_group_id_and_project_id_and_title", :algorithm=>:concurrently})
   -> 0.0051s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0051s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_title_varchar"})
   -> 0.0035s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0045s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_project_id_and_title_varchar_unique"})
   -> 0.0029s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0042s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_group_id_and_title_varchar_unique"})
   -> 0.0033s
-- transaction_open?()
   -> 0.0000s
-- indexes(:labels)
   -> 0.0039s
-- remove_index(:labels, {:algorithm=>:concurrently, :name=>"index_labels_on_group_id"})
   -> 0.0031s
== 20210921063924 IndexLabelsUsingVarcharPatternOps: reverted (0.0887s) =======

Sample queries

  1. find_label_ids with devops::*: https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/6584/commands/23240

  2. find_label_ids with devops::* and workflow::*: https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/6584/commands/23243

Screenshots or screen recordings

Screen_Shot_2021-09-21_at_9.47.21_PM

How to set up and validate locally

  1. Create multiple scoped labels with the same scope and assign them to issues
  2. Use the filter bar in the group / project issue list to filter by the label <scope>::*

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Heinrich Lee Yu

Merge request reports