2014-12-26 2 views
1

이 내 프로젝트의 3 개 모델입니다 : 사용자, 게임, GameVisits 내 프론트 엔드는 AngularJS와로 작성 쿼리의 many-to-many 연관

class User < ActiveRecord::Base 
    has_many :game_visits 
    has_many :games, through: :game_visits 
end 

class Game < ActiveRecord::Base 
    has_many :game_visits 
    has_many :users, through: :game_visits 
end 

class GameVisit < ActiveRecord::Base 
    self.table_name = :users_games 
    enum status: [:visited, :not_visited, :unknown] 
    belongs_to :user 
    belongs_to :game 
end 

는, 그래서 기능을 원하는 게임 반환의 목록 사용자 이름과 상태 또는 nil을 각각 포함하는 해시 배열입니다. 데이터는 테이블, cols - players, rows - games, game - 플레이어의 존재 - 게임으로 제공됩니다.

def team_json(game_ids = nil) 
    game_ids ||= Game.pluck(:id) 
    games = Game.find(game_ids) 
    users = self.users 
    result = [] 
    games.each do |game| 
    record = {name: game.name, date: game.date } 
    users_array = [] 
    users.each do |user| 
     users_array << { 
     name: user.name, 
     status: user.game_visits.find_by_game_id(game.id) 
     } 
    end 
    record[:users] = users_array 
    result << record 
    end 
    result 
end 

출력 :

[{NAME => "게임 0": 일 => 2014년 12월 19일 (금) 세계 협정시 11시 16분 20초 +00 이는 함수의 예이다 : 00 :, users => [{: name => "Bob", : status => nil}]}, {: name => "game 1", : date => 토요일, 2014 년 12 월 20 일 11:16:20 UTC +00 : 00, : 사용자 => [{: name => "Bob", : status => nil}]} {: name => "game 2", : date => 2014 년 12 월 19 일 11 : 16 : 20 UTC +00 : 00, : 사용자 => [{: 이름 => "Bob", 상태 => nil}]}]

그러나이 함수는 GameVisits에 대해 많은 수의 sql 쿼리를 생성합니다.

(1.0ms) SELECT "games"."id" FROM "games" 
    Game Load (1.3ms) SELECT "games".* FROM "games" WHERE "games"."id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101) 
    User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."team_id" = $1 [["team_id", 1]] 
    GameVisit Load (0.6ms) SELECT "users_games".* FROM "users_games" WHERE "users_games"."user_id" = $1 AND "users_games"."game_id" = $2 LIMIT 1 [["user_id", 7], ["game_id", 1]] 
    GameVisit Load (0.6ms) SELECT "users_games".* FROM "users_games" WHERE "users_games"."user_id" = $1 AND "users_games"."game_id" = $2 LIMIT 1 [["user_id", 7], ["game_id", 2]] 
    GameVisit Load (0.5ms) SELECT "users_games".* FROM "users_games" WHERE "users_games"."user_id" = $1 AND "users_games"."game_id" = $2 LIMIT 1 [["user_id", 7], ["game_id", 3]] 
.............. 

어떻게 최적화 할 수 있습니까?

답변

1

귀하의 user.game_visits.find_by_game_id(game.id)은 모든 검색어를 실행합니다. eager-loading을 사용하여이 문제를 해결할 수 있습니다. 위의 줄을 사용

status: user.game_visits.find{ |gv| gv.game_id == game.id } 

열거 가능한의 find하지 ActiveRelation의 : 다음에 users_arraystatus의 값 할당을 변경

users = self.users.includes(:game_visits) 

:

는 다음에 users의 과제를 변경

. find_by_game_id 또는 AR의 다른 find을 사용하면 쿼리가 실행됩니다.

user_game_visits_hash = \ 
    user.game_visits.each_with_object({}) do |gv, hash| 
    hash[gv.game_id] = gv 
    end 

# more code here 

status: user_game_visits_hash[game.id] 

그리고 사소한 점 :이 같은 것을 할 주변 find 모두 얻을 수 있도록

추가 최적화 game_visits의 해시를 생성 할 수있는 경우에 당신의 방법의 주장에 game_ids은 전무 모든 게임에서 모든 게임을 가져 오기 때문에 메소드의 처음 두 행은 불필요하게 두 개의 쿼리를 실행합니다. 이를 다음과 같이 변경할 수 있습니다 :

games = game_ids ? Game.find(game_ids) : Game.all