Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
c5ef7a4
クエリの実行数を減らすため、ShiftUsecaseのGetShiftsByUserID関数で、
Nagi-azi Mar 26, 2026
7274c98
Merge branch 'feat/nagi/247-fix-backend-n-plus-one' of https://github…
taminororo Mar 30, 2026
1a8c693
feat: GetByShifts関数のN + 1問題をJOINのSQL文で解決
taminororo Mar 30, 2026
a30aa95
feat: DEBUG_SQL環境変数でSQLログ出力を制御可能にする
taminororo Mar 30, 2026
9a82937
fix: GetUsersByShiftのN+1問題を解消
taminororo Mar 30, 2026
b9fad94
test: N+1解消のベンチマークと整合性テストを追加
taminororo Mar 30, 2026
cafa967
fix: FindByNamesのSQL構文をPostgreSQL対応に修正
taminororo Apr 8, 2026
44ceb75
feat: GASスクリプトをGit管理に追加 & マニュアルスライド生成スクリプトを追加
taminororo Apr 8, 2026
f6cc23d
fix: ClaudeCode用のコピペMDファイルをコミットしないように変更
taminororo Apr 8, 2026
11484a8
fix: feat/kanba/docs-to-pptx-to-pdfで作られたけど誤って混入したgenerate_manual_slid…
taminororo Apr 15, 2026
a4b70bd
fix: ゴールデン比較み実装のベンチマークテスト、テストデータを削除
taminororo Apr 15, 2026
d18b14a
fix: SQLインジェクション対策でプレースホルダ化
taminororo Apr 16, 2026
0c9609a
fix: GASディレクトリを削除 (本PRのN+1修正スコープ外)
taminororo Apr 20, 2026
7229ac0
fix: DEBUG_SQLをopt-inに変更 (env未設定時はログ出力しない)
taminororo Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ api/lib/interface
api/env/*
api/seeds/*
web/prod/*

# Claude Code working files
.claude/
15 changes: 12 additions & 3 deletions api/lib/internals/repository/abstract/abstract_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import (
"context"
"database/sql"
"fmt"
"os"

"github.com/NUTFes/SeeFT/api/lib/externals/db"
"github.com/pkg/errors"
)

var debugSQL = os.Getenv("DEBUG_SQL") == "1"

type abstractRepository struct {
client db.Client
}
Expand All @@ -28,13 +31,17 @@ func (a abstractRepository) Read(ctx context.Context, query string) (*sql.Rows,
if err != nil {
return nil, errors.Wrapf(err, "cannot connect SQL")
}
fmt.Printf("\x1b[36m%s\n", query)
if debugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return rows, nil
}

func (a abstractRepository) ReadByID(ctx context.Context, query string) (*sql.Row, error) {
row := a.client.DB().QueryRowContext(ctx, query)
fmt.Printf("\x1b[36m%s\n", query)
if debugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return row, nil
}

Expand All @@ -43,7 +50,9 @@ func (a abstractRepository) UpdateDB(ctx context.Context, query string) error {
if err != nil {
return err
}
fmt.Printf("\x1b[36m%s\n", query)
if debugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return err
}

Expand Down
22 changes: 17 additions & 5 deletions api/lib/internals/repository/session_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package repository
import (
"context"
"database/sql"
"github.com/NUTFes/SeeFT/api/lib/externals/db"
"fmt"
"os"

"github.com/NUTFes/SeeFT/api/lib/externals/db"
)

var sessionDebugSQL = os.Getenv("DEBUG_SQL") == "1"


type sessionRepository struct {
client db.Client
Expand All @@ -30,7 +34,9 @@ func (r *sessionRepository) Create(c context.Context, userID string, accessToken
if err != nil {
return err
}
fmt.Printf("\x1b[36m%s\n", query)
if sessionDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return nil
}

Expand All @@ -42,15 +48,19 @@ func (r *sessionRepository) Delete(c context.Context, accessToken string) error
if err != nil {
return err
}
fmt.Printf("\x1b[36m%s\n", query)
if sessionDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return nil
}

// アクセストークンからセッションを取得
func (r *sessionRepository) FindSessionByAccessToken(c context.Context, accessToken string) *sql.Row {
query := "select * from session where access_token = '" + accessToken + "'"
row := r.client.DB().QueryRowContext(c, query)
fmt.Printf("\x1b[36m%s\n", query)
if sessionDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return row
}

Expand All @@ -61,6 +71,8 @@ func (r *sessionRepository) DeleteByUserID(c context.Context, userID string) err
if err != nil {
return err
}
fmt.Printf("\x1b[36m%s\n", query)
if sessionDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return nil
}
6 changes: 3 additions & 3 deletions api/lib/internals/repository/shift_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ func (b *shiftRepository) User(c context.Context, id string) (*sql.Rows, error)
return rows, nil
}

// 特定のタスクのユーザ取得
// 特定のタスクのユーザ取得(JOINでユーザー情報も一括取得)
func (b *shiftRepository) Users(c context.Context, task string, year string, date string, time string, weather string) (*sql.Rows, error) {
query := "SELECT user_id FROM shifts WHERE task_id = " + task + " AND year_id = " + year + " AND date_id = " + date + " AND time_id = " + time + " AND weather_id = " + weather
rows, err := b.client.DB().QueryContext(c, query)
query := "SELECT u.id, u.name, u.mail, u.grade_id, u.department_id, u.bureau_id, u.role_id, u.student_number, u.tel, u.created_at, u.updated_at FROM shifts s JOIN users u ON s.user_id = u.id WHERE s.task_id = $1 AND s.year_id = $2 AND s.date_id = $3 AND s.time_id = $4 AND s.weather_id = $5"
rows, err := b.client.DB().QueryContext(c, query, task, year, date, time, weather)
if err != nil {
return nil, errors.Wrapf(err, "cannot connect SQL")
}
Expand Down
25 changes: 23 additions & 2 deletions api/lib/internals/repository/task_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"context"
"database/sql"
"fmt"
"os"

"github.com/NUTFes/SeeFT/api/lib/externals/db"
"github.com/NUTFes/SeeFT/api/lib/internals/repository/abstract"
"github.com/lib/pq"
"github.com/pkg/errors"
)

var taskDebugSQL = os.Getenv("DEBUG_SQL") != "0"

type taskRepository struct {
client db.Client
crud abstract.Crud
Expand All @@ -25,6 +29,7 @@ type TaskRepository interface {
FindNewRecord(context.Context) (*sql.Row, error)
FindByName(context.Context, string) (*sql.Row, error)
FindByUserID(context.Context, string) (*sql.Rows, error)
FindByNames(context.Context, []string) (*sql.Rows, error)
}

func NewTaskRepository(c db.Client, ac abstract.Crud) TaskRepository {
Expand All @@ -50,7 +55,9 @@ func (b *taskRepository) Shift(c context.Context, name string) (*sql.Rows, error
if err != nil {
return nil, errors.Wrapf(err, "cannot connect SQL")
}
fmt.Printf("\x1b[36m%s\n", query)
if taskDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return rows, nil
}

Expand Down Expand Up @@ -92,6 +99,18 @@ func (b *taskRepository) FindByName(c context.Context, name string) (*sql.Row, e
return b.client.DB().QueryRowContext(c, query), nil
}

// 複数のタスク名から一括でタスクを取得する(N+1問題対策)
func (b *taskRepository) FindByNames(c context.Context, names []string) (*sql.Rows, error) {
if len(names) == 0 {
// 空の結果を返す
query := "SELECT * FROM tasks WHERE 1=0"
return b.client.DB().QueryContext(c, query)
}

query := "SELECT * FROM tasks WHERE task = ANY($1::text[])"
return b.client.DB().QueryContext(c, query, pq.Array(names))
}

// 指定したuserIDの全てのタスクを取得する
func (b *taskRepository) FindByUserID(c context.Context, userID string) (*sql.Rows, error) {
query := `
Expand All @@ -101,7 +120,9 @@ func (b *taskRepository) FindByUserID(c context.Context, userID string) (*sql.Ro
WHERE s.user_id = $1
ORDER BY t.task
`
fmt.Printf("\x1b[36m%s\n", query)
if taskDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}

return b.client.DB().QueryContext(c, query, userID)
}
Expand Down
21 changes: 20 additions & 1 deletion api/lib/internals/repository/user_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import (
"context"
"database/sql"
"fmt"
"os"

"github.com/NUTFes/SeeFT/api/lib/externals/db"
"github.com/NUTFes/SeeFT/api/lib/internals/repository/abstract"
"github.com/lib/pq"
)

var userDebugSQL = os.Getenv("DEBUG_SQL") != "0"

type userRepository struct {
client db.Client
crud abstract.Crud
Expand All @@ -23,6 +27,7 @@ type UserRepository interface {
Delete(context.Context, string) error
FindNewRecord(context.Context) (*sql.Row, error)
FindByName(context.Context, string) (*sql.Row, error)
FindByNames(context.Context, []string) (*sql.Rows, error)
}

func NewUserRepository(c db.Client, ac abstract.Crud) UserRepository {
Expand All @@ -45,7 +50,9 @@ func (ur *userRepository) Find(c context.Context, id string) (*sql.Row, error) {
func (ur *userRepository) FindByStudentNumber(c context.Context, studentNumber string) *sql.Row {
query := "SELECT * FROM users WHERE student_number = " + studentNumber
row := ur.client.DB().QueryRowContext(c, query)
fmt.Printf("\x1b[36m%s\n", query)
if userDebugSQL {
fmt.Printf("\x1b[36m%s\n", query)
}
return row
}

Expand Down Expand Up @@ -93,3 +100,15 @@ func (b *userRepository) FindByName(c context.Context, name string) (*sql.Row, e
query := "SELECT * FROM users WHERE name = '" + name + "'"
return b.client.DB().QueryRowContext(c, query), nil
}

// 複数のユーザー名から一括でユーザーを取得する(N+1問題対策)
func (b *userRepository) FindByNames(c context.Context, names []string) (*sql.Rows, error) {
if len(names) == 0 {
// 空の結果を返す
query := "SELECT * FROM users WHERE 1=0"
return b.client.DB().QueryContext(c, query)
}

query := "SELECT * FROM users WHERE name = ANY($1::text[])"
return b.client.DB().QueryContext(c, query, pq.Array(names))
}
Loading