Skip to content
This repository has been archived by the owner on Dec 8, 2017. It is now read-only.

Commit

Permalink
PERF: Fix N+1 queries when loading groups.
Browse files Browse the repository at this point in the history
  • Loading branch information
tgxworld committed Nov 25, 2016
1 parent 712ff01 commit 5794f16
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import NotificationsButton from 'discourse/components/notifications-button';

export default NotificationsButton.extend({
classNames: ['notification-options', 'group-notification-menu'],
notificationLevel: Em.computed.alias('group.notification_level'),
notificationLevel: Em.computed.alias('group.group_user.notification_level'),
i18nPrefix: 'groups.notifications',

clicked(id) {
Expand Down
14 changes: 11 additions & 3 deletions app/assets/javascripts/discourse/controllers/group.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,16 @@ export default Ember.Controller.extend({
});
},

@computed('model.is_member')
getTabs(isMember) {
return this.get('tabs').filter(t => isMember || !t.get('requiresMembership'));
@computed('model.is_group_user')
getTabs(isGroupUser) {
return this.get('tabs').filter(t => {
let isMember = false;

if (this.currentUser) {
isMember = this.currentUser.admin || isGroupUser;
}

return isMember || !t.get('requiresMembership');
});
}
});
2 changes: 1 addition & 1 deletion app/assets/javascripts/discourse/models/group.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const Group = Discourse.Model.extend({
},

setNotification(notification_level) {
this.set("notification_level", notification_level);
this.set("group_user.notification_level", notification_level);
return ajax(`/groups/${this.get("name")}/notifications`, {
data: { notification_level },
type: "POST"
Expand Down
10 changes: 9 additions & 1 deletion app/assets/javascripts/discourse/models/user.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,15 @@ const User = RestModel.extend({
}

if (!Em.isEmpty(json.user.groups)) {
json.user.groups = json.user.groups.map(g => Group.create(g));
const groups = [];

for(let i = 0; i < json.user.groups.length; i++) {
const group = Group.create(json.user.groups[i]);
group.group_user = json.user.group_users[i];
groups.push(group);
}

json.user.groups = groups;
}

if (json.user.invited_by) {
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/admin/groups_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Admin::GroupsController < Admin::AdminController

def index
groups = Group.order(:name).where("id <> ?", Group::AUTO_GROUPS[:everyone])
groups = Group.order(:name).where("groups.id <> ?", Group::AUTO_GROUPS[:everyone])

if search = params[:search].to_s
groups = groups.where("name ILIKE ?", "%#{search}%")
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/groups_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class GroupsController < ApplicationController
skip_before_filter :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed]

def show
render_serialized(find_group(:id), BasicGroupSerializer)
render_serialized(find_group(:id), GroupShowSerializer, root: 'basic_group')
end

def counts
Expand Down
29 changes: 0 additions & 29 deletions app/serializers/basic_group_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,12 @@ class BasicGroupSerializer < ApplicationSerializer
:title,
:grant_trust_level,
:incoming_email,
:notification_level,
:has_messages,
:is_member,
:mentionable,
:flair_url,
:flair_bg_color,
:flair_color

def include_incoming_email?
scope.is_staff?
end

def notification_level
fetch_group_user&.notification_level
end

def include_notification_level?
scope.authenticated?
end

def mentionable
object.mentionable?(scope.user, object.id)
end

def is_member
scope.is_admin? || fetch_group_user.present?
end

def include_is_member?
scope.authenticated?
end

private

def fetch_group_user
@group_user ||= object.group_users.find_by(user_id: scope.user.id)
end
end
3 changes: 3 additions & 0 deletions app/serializers/basic_group_user_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class BasicGroupUserSerializer < ApplicationSerializer
attributes :group_id, :user_id, :notification_level
end
11 changes: 11 additions & 0 deletions app/serializers/group_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class GroupSerializer < BasicGroupSerializer
attributes :mentionable

def mentionable
object.mentionable?(scope.user, object.id)
end

def include_mentionable?
authenticated?
end
end
11 changes: 11 additions & 0 deletions app/serializers/group_show_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class GroupShowSerializer < BasicGroupSerializer
attributes :is_group_user

def include_is_group_user?
scope.authenticated?
end

def is_group_user
object.users.include?(scope.user)
end
end
11 changes: 9 additions & 2 deletions app/serializers/user_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def self.untrusted_attributes(*attrs)

has_one :invited_by, embed: :object, serializer: BasicUserSerializer
has_many :groups, embed: :object, serializer: BasicGroupSerializer
has_many :group_users, embed: :object, serializer: BasicGroupUserSerializer
has_many :featured_user_badges, embed: :ids, serializer: UserBadgeSerializer, root: :user_badges
has_one :card_badge, embed: :object, serializer: BadgeSerializer
has_one :user_option, embed: :object, serializer: UserOptionSerializer
Expand Down Expand Up @@ -127,13 +128,19 @@ def mailing_list_posts_per_day
end

def groups
groups = object.groups.order(:id)

if scope.is_admin? || object.id == scope.user.try(:id)
object.groups
groups
else
object.groups.where(visible: true)
groups.where(visible: true)
end
end

def group_users
object.group_users.order(:group_id)
end

def include_email?
object.id && object.id == scope.user.try(:id)
end
Expand Down
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@
get "posts/:username/flagged" => "posts#flagged_posts", constraints: {username: USERNAME_ROUTE_FORMAT}

get "groups/:id.json" => 'groups#show', constraints: {id: USERNAME_ROUTE_FORMAT}, defaults: {format: 'json'}

resources :groups, id: USERNAME_ROUTE_FORMAT do
get "posts.rss" => "groups#posts_feed", format: :rss
get "mentions.rss" => "groups#mentions_feed", format: :rss
Expand Down
3 changes: 0 additions & 3 deletions spec/controllers/admin/groups_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@
"primary_group"=>false,
"grant_trust_level"=>nil,
"incoming_email"=>nil,
"notification_level"=>2,
"has_messages"=>false,
"is_member"=>true,
"mentionable"=>false,
"flair_url"=>nil,
"flair_bg_color"=>nil,
"flair_color"=>nil
Expand Down
15 changes: 14 additions & 1 deletion test/javascripts/acceptance/groups-test.js.es6
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { acceptance } from "helpers/qunit-helpers";
import { acceptance, logIn } from "helpers/qunit-helpers";

acceptance("Groups");

test("Browsing Groups", () => {
Expand All @@ -24,6 +25,18 @@ test("Browsing Groups", () => {

visit("/groups/discourse/messages");
andThen(() => {
ok($('.action-list li').length === 4, 'it should not show messages tab');
ok(count('.user-stream .item') > 0, "it lists stream items");
});
});

test("Messages tab", () => {
logIn();
Discourse.reset();

visit("/groups/discourse");

andThen(() => {
ok($('.action-list li').length === 5, 'it should show messages tab if user is admin');
});
});

0 comments on commit 5794f16

Please sign in to comment.