Add starring support for AI catalog items (part 2: GraphQL)

What does this MR do and why?

Adds the GraphQL layer for starring AI catalog items, building on the schema and model introduced in !228243 (merged).

This is the second in a series of MRs implementing quality signals for the AI catalog, as discussed in gitlab-org#20225 (comment 3112262413).

Introduces:

  • AiCatalogItemStar mutation: accepts a Global ID and a starred boolean, calls item.star(user) or item.unstar(user) accordingly, and returns the updated star_count. Inactive users are prevented from starring.
  • star_count and viewer_has_starred fields on AiCatalogItem
  • Specs for the mutation and the interface fields

Database query

The starred_item_ids_for_user scope generates a single batched query for all item IDs in the current request:

SELECT "ai_catalog_item_stars"."ai_catalog_item_id"
FROM "ai_catalog_item_stars"
WHERE "ai_catalog_item_stars"."user_id" = $1
  AND "ai_catalog_item_stars"."ai_catalog_item_id" IN ($2, $3, ...)
LIMIT $4

This is called once per request via BatchLoader::GraphQL, not once per item. The N+1 spec in items_spec.rb verifies that query count stays constant as the number of items grows.

Execution plan (postgres.ai, gitlab-production-main):

Limit  (cost=0.00..0.00 rows=1 width=8) (actual time=0.004..0.005 rows=0 loops=1)
  ->  Seq Scan on public.ai_catalog_item_stars  (cost=0.00..0.00 rows=1 width=8) (actual time=0.003..0.004 rows=0 loops=1)
        Filter: ((user_id = 12345) AND (ai_catalog_item_id = ANY ('{1,2,3,4,5}'::bigint[])))
        Rows Removed by Filter: 0

Timing summary:

Time: 0.491 ms
  - planning: 0.464 ms
  - execution: 0.027 ms
    - I/O read: N/A
    - I/O write: N/A

Shared buffers:
  - hits: 0 from the buffer pool
  - reads: 0 from the OS file cache, including disk I/O
  - dirtied: 0
  - writes: 0

The sequential scan reflects an empty table with no planner statistics. With real data the query will use the existing index_ai_catalog_item_stars_on_user_id (user_id) or index_ai_catalog_item_stars_on_item_and_user (ai_catalog_item_id, user_id) indexes. No new indexes are needed.

Full plan: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/50453/commands/149835

Screenshots or screen recordings

Before After

How to set up and validate locally

Use the GraphiQL explorer at /-/graphql-explorer. Replace the id value with the Global ID of an existing AI catalog item (gid://gitlab/Ai::Catalog::Item/<id>).

Star an item:

mutation {
  aiCatalogItemStar(input: { id: "gid://gitlab/Ai::Catalog::Item/1", starred: true }) {
    starCount
    errors
  }
}

Unstar an item:

mutation {
  aiCatalogItemStar(input: { id: "gid://gitlab/Ai::Catalog::Item/1", starred: false }) {
    starCount
    errors
  }
}

Verify starCount and viewerHasStarred on the item:

{
  aiCatalogItem(id: "gid://gitlab/Ai::Catalog::Item/1") {
    starCount
    viewerHasStarred
  }
}

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Mattias Michaux

Merge request reports

Loading
Loading