Skip to content

Add descendantGroups field to Group type

What does this MR do?

Related to #334993 (closed)

To be able to query groups that are descendants of a given parent group, this MR adds the new field descendantGroups to the GraphQL GroupType.

This field uses a new resolver GroupsResolver with the include_parent_descendants argument set as true by default.

Example GraphQL query
query {
  group(fullPath: "gitlab-org") {
    name
    descendantGroups(search: "Green", owned: true) {
      edges {
        node {
          name
          fullPath
        }
      }
    }
  }
}
Example response
{
  "data": {
    "group": {
      "name": "Gitlab Org",
      "descendantGroups": {
        "edges": [
          {
            "node": {
              "name": "Green Subgroup",
              "fullPath": "gitlab-org/subgroup-a/green-subgroup"
            }
          }
        ]
      }
    }
  }
}

Additionally, this new resolver includes the arguments owned and search as they are needed for #233568 (closed).

GroupsFinder already supported the argument owned but not the search one, so this MR implements that too.

Query plan for GroupsFinder using the search param

SELECT namespaces.*
FROM namespaces
WHERE namespaces.type = 'Group'
        AND namespaces.id IN 
    (SELECT routes.source_id
    FROM routes
    WHERE routes.source_type = 'Namespace'
            AND (routes.path ILIKE '%org%'
            OR routes.name ILIKE '%org%'))
ORDER BY  namespaces.id DESC
Plan with execution
Gather Merge  (cost=566559.41..567966.74 rows=12062 width=356) (actual time=94173.924..94222.253 rows=13166 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   Buffers: shared hit=173872 read=146117 dirtied=1368
   I/O Timings: read=173039.023 write=0.000
   ->  Sort  (cost=565559.38..565574.46 rows=6031 width=356) (actual time=94165.158..94167.062 rows=4389 loops=3)
         Sort Key: namespaces.id DESC
         Sort Method: quicksort  Memory: 1584kB
         Buffers: shared hit=173872 read=146117 dirtied=1368
         I/O Timings: read=173039.023 write=0.000
         ->  Nested Loop  (cost=165848.28..565180.69 rows=6031 width=356) (actual time=50334.119..94136.898 rows=4389 loops=3)
               Buffers: shared hit=173856 read=146117 dirtied=1368
               I/O Timings: read=173039.023 write=0.000
               ->  Parallel Bitmap Heap Scan on public.routes  (cost=165847.85..462286.48 rows=51231 width=4) (actual time=50261.576..78066.873 rows=21399 loops=3)
                     Buffers: shared hit=2 read=114196 dirtied=1128
                     I/O Timings: read=128588.714 write=0.000
                     ->  BitmapAnd  (cost=165847.85..165847.85 rows=123223 width=0) (actual time=50244.804..50244.812 rows=0 loops=1)
                           Buffers: shared hit=2 read=56129
                           I/O Timings: read=47063.808 write=0.000
                           ->  BitmapOr  (cost=3589.20..3589.20 rows=337763 width=0) (actual time=1022.428..1022.434 rows=0 loops=1)
                                 Buffers: shared hit=2 read=653
                                 I/O Timings: read=890.049 write=0.000
                                 ->  Bitmap Index Scan using index_routes_on_path_trigram  (cost=0.00..1041.29 rows=92639 width=0) (actual time=545.251..545.253 rows=162512 loops=1)
                                       Index Cond: ((routes.path)::text ~~* '%org%'::text)
                                       Buffers: shared hit=1 read=257
                                       I/O Timings: read=467.653 write=0.000
                                 ->  Bitmap Index Scan using index_route_on_name_trigram  (cost=0.00..2486.43 rows=245124 width=0) (actual time=477.173..477.173 rows=186169 loops=1)
                                       Index Cond: ((routes.name)::text ~~* '%org%'::text)
                                       Buffers: shared hit=1 read=396
                                       I/O Timings: read=422.396 write=0.000
                           ->  Bitmap Index Scan using index_routes_on_source_type_and_source_id  (cost=0.00..162227.66 rows=11254280 width=0) (actual time=49203.695..49203.695 rows=11404782 loops=1)
                                 Index Cond: ((routes.source_type)::text = 'Namespace'::text)
                                 Buffers: shared read=55476
                                 I/O Timings: read=46173.759 write=0.000
               ->  Index Scan using index_namespaces_on_type_and_id_partial on public.namespaces  (cost=0.43..2.01 rows=1 width=356) (actual time=0.746..0.746 rows=0 loops=64198)
                     Index Cond: (((namespaces.type)::text = 'Group'::text) AND (namespaces.id = routes.source_id))
                     Buffers: shared hit=173854 read=31921 dirtied=240
                     I/O Timings: read=44450.309 write=0.000

Link to Postgres.ai

Screenshots (strongly suggested)

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Edited by Eugenia Grieff

Merge request reports