From 9c440e416bd4714b72d5ee9cfb7af998d1593469 Mon Sep 17 00:00:00 2001 From: Zeke Gabrielse Date: Mon, 18 Mar 2024 16:24:02 -0500 Subject: [PATCH] fix slow query for product users --- .../relationships/users_controller.rb | 8 +++++-- app/models/user.rb | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/products/relationships/users_controller.rb b/app/controllers/api/v1/products/relationships/users_controller.rb index 227a38daa9..871acab149 100644 --- a/app/controllers/api/v1/products/relationships/users_controller.rb +++ b/app/controllers/api/v1/products/relationships/users_controller.rb @@ -12,7 +12,7 @@ class UsersController < Api::V1::BaseController authorize :product def index - users = apply_pagination(authorized_scope(apply_scopes(product.users)).preload(:role)) + users = apply_pagination(authorized_scope(apply_scopes(product_users)).preload(:role)) authorize! users, with: Products::UserPolicy @@ -20,7 +20,7 @@ def index end def show - user = FindByAliasService.call(product.users, id: params[:id], aliases: :email) + user = FindByAliasService.call(product_users, id: params[:id], aliases: :email) authorize! user, with: Products::UserPolicy @@ -31,6 +31,10 @@ def show attr_reader :product + # FIXME(ezekg) Uses a more optimized query for large accounts. This should + # be considered a bug in union_of. + def product_users = current_account.users.for_product(product) + def set_product scoped_products = authorized_scope(current_account.products) diff --git a/app/models/user.rb b/app/models/user.rb index 05e3fe9133..6694af5b79 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -309,7 +309,27 @@ def owned = where(owner: proxy_association.owner) end } - scope :for_product, -> id { joins(:licenses).where(licenses: { product_id: id }).distinct } + scope :for_product, -> id { + license_users = LicenseUser.arel_table + licenses = License.arel_table + users = User.arel_table + + # More optimized union query for this particular association + left_outer_joins(:license_users) + .joins( + Arel::Nodes::InnerJoin.new( + licenses, + Arel::Nodes::On.new( + licenses[:user_id].eq(users[:id]).or( + licenses[:id].eq(license_users[:license_id]), + ) + ), + ), + ) + .where( + licenses: { product_id: id }, + ) + } scope :for_license, -> id { joins(:licenses).where(licenses: { id: id }).distinct } scope :for_group_owner, -> id { joins(group: :owners).where(group: { group_owners: { user_id: id } }).distinct } scope :for_user, -> id {