Transfer locations API should prioritize exact paths in search
I'm trying to transfer a https://gitlab.com/fulfillment-group into the gitlab-org
group, but https://gitlab.com/api/v4/groups/:id/transfer_locations?search=gitlab-org
is providing me an endless list of irrelevant projects:
It looks like AcceptingGroupTransfersFinder
in https://gitlab.com/gitlab-org/gitlab/-/blob/02c11f82e950c247c627f5d63eaa10db5a37dd36/app/finders/groups/accepting_group_transfers_finder.rb#L17 calls items.by_search
.
When I tested bundle exec rspec spec/finders/groups/accepting_group_transfers_finder_spec.rb
locally, I see this SQL query that uses a bunch of ILIKE
queries:
[10, 19] in /Users/stanhu/gdk-ee/gitlab/app/finders/groups/base.rb
10:
11: def by_search(items)
12: return items if params[:search].blank?
13:
14: byebug
15:
=> 16: items.search(params[:search], include_parents: true)
17: end
18: end
19: end
(byebug) items
#<ActiveRecord::Relation [#<Group id:2 @great-grandparent-group>, #<Group id:7 @owner-access-group>, #<Group id:8 @owner-access-group/group5>, #<Group id:11 @group8>, #<Group id:12 @group8/group9>]>
(byebug) items.method(:search)
#<Method: Namespace::ActiveRecord_Relation#search(*)>
(byebug) ActiveRecord::Base.logger = Logger.new(STDOUT)
#<Logger:0x0000000161e97098 @level=0, @progname=nil, @default_formatter=#<Logger::Formatter:0x0000000161e96ff8 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x0000000161e96f08 @shift_period_suffix=nil, @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDOUT>>, @binmode=false, @mon_data=#<Monitor:0x0000000161e96ee0>, @mon_data_owner_object_id=1030420>>
(byebug) items.method(:search)
#<Method: Namespace::ActiveRecord_Relation#search(*)>
(byebug) items.search(params[:search], include_parents: true)
D, [2023-08-31T22:20:14.238521 #42495] DEBUG -- : Namespace Load (3.5ms) SELECT "namespaces".* FROM ((WITH "descendants_base_cte" AS MATERIALIZED (SELECT "namespaces"."id", "namespaces"."traversal_ids" FROM "namespaces" INNER JOIN "members" ON "namespaces"."id" = "members"."source_id" WHERE "members"."type" = 'GroupMember' AND "members"."source_type" = 'Namespace' AND "namespaces"."type" = 'Group' AND "members"."user_id" = 1 AND "members"."requested_at" IS NULL AND (access_level >= 10) AND (members.access_level >= 50)), "superset" AS (SELECT d1.traversal_ids
FROM descendants_base_cte d1
WHERE NOT EXISTS (
SELECT 1
FROM descendants_base_cte d2
WHERE d2.id = ANY(d1.traversal_ids)
AND d2.id <> d1.id
)
) SELECT DISTINCT "namespaces".* FROM "superset", "namespaces" WHERE "namespaces"."type" = 'Group' AND next_traversal_ids_sibling("superset"."traversal_ids") > "namespaces"."traversal_ids" AND "superset"."traversal_ids" <= "namespaces"."traversal_ids" AND "namespaces"."id" NOT IN (4, 5, 6, 3) /* allow_cross_joins_across_databases */)
UNION
(WITH "descendants_base_cte" AS MATERIALIZED (SELECT "namespaces"."id", "namespaces"."traversal_ids" FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND "namespaces"."id" IN (SELECT "group_group_links"."shared_group_id" FROM "group_group_links" WHERE "group_group_links"."group_access" = 50 AND "group_group_links"."shared_with_group_id" IN (SELECT "namespaces"."id" FROM "namespaces" INNER JOIN "members" ON "namespaces"."id" = "members"."source_id" WHERE "members"."type" = 'GroupMember' AND "members"."source_type" = 'Namespace' AND "namespaces"."type" = 'Group' AND "members"."user_id" = 1 AND "members"."requested_at" IS NULL AND (access_level >= 10) AND "members"."access_level" = 50))), "superset" AS (SELECT d1.traversal_ids
FROM descendants_base_cte d1
WHERE NOT EXISTS (
SELECT 1
FROM descendants_base_cte d2
WHERE d2.id = ANY(d1.traversal_ids)
AND d2.id <> d1.id
)
) SELECT DISTINCT "namespaces".* FROM "superset", "namespaces" WHERE "namespaces"."type" = 'Group' AND next_traversal_ids_sibling("superset"."traversal_ids") > "namespaces"."traversal_ids" AND "superset"."traversal_ids" <= "namespaces"."traversal_ids" AND "namespaces"."id" NOT IN (4, 5, 6, 3))) namespaces WHERE "namespaces"."type" != 'Project' AND "namespaces"."id" IN (SELECT "routes"."source_id" FROM "routes" WHERE "routes"."source_type" = 'Namespace' AND ("routes"."path" ILIKE '%great%' AND "routes"."path" ILIKE '%grandparent%' AND "routes"."path" ILIKE '%group%' OR "routes"."name" ILIKE '%great%' AND "routes"."name" ILIKE '%grandparent%' AND "routes"."name" ILIKE '%group%') /* allow_cross_joins_across_databases */) /* loading for inspect */ LIMIT 11 /*application:test,correlation_id:17b582581370811aef6072ecca0055ea,db_config_name:main,line:/app/finders/groups/base.rb:16:in `by_search'*/
D, [2023-08-31T22:20:14.239126 #42495] DEBUG -- : ↳ app/finders/groups/base.rb:16:in `by_search'
D, [2023-08-31T22:20:14.243921 #42495] DEBUG -- : Route Load (0.3ms) SELECT "routes".* FROM "routes" WHERE "routes"."source_id" = 2 AND "routes"."source_type" = 'Namespace' LIMIT 1 /*application:test,correlation_id:17b582581370811aef6072ecca0055ea,db_config_name:main,line:/app/models/concerns/routable.rb:155:in `full_attribute'*/
D, [2023-08-31T22:20:14.244135 #42495] DEBUG -- : ↳ app/models/concerns/routable.rb:155:in `full_attribute'
#<ActiveRecord::Relation [#<Group id:2 @great-grandparent-group>]>
Edited by Stan Hu