ラベル機能実装後、検索のテストをしていたらPG::AmbiguousColumn: ERROR

# ラベルでの検索も実装し、そのSystem Spec中にタイトルのエラーが発生

System Spec中に……と書いてあるものの、テストを書き色んなパターンを試した結果、見落としていたエラーを発見した、というのが正しいです。

## 前提

タスク管理アプリを作成中。
タスク名、ステータス（未着手、着手、完了）での検索は実装済み。
今回、タスクにラベル（タグのようなもの）を付けられるようにし、そのラベルでもタスクを検索できるように実装。
その機能のテストを書いている時に起こったエラー。

### 環境

Rails 5.2.3
Ruby 2.6.5

### テーブル構成

TaskテーブルとLabelテーブルがあって、多対多を実現するためのLabelingという中間テーブルがある。

```ruby:schema.rb（一部略）
  create_table "labelings", force: :cascade do |t|
    t.bigint "task_id"
    t.bigint "label_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["label_id"], name: "index_labelings_on_label_id"
    t.index ["task_id"], name: "index_labelings_on_task_id"
  end

  create_table "labels", force: :cascade do |t|
    t.string "name", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "tasks", force: :cascade do |t|
    t.string "name", null: false
    t.text "content", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "deadline", null: false
    t.integer "priority", default: 0, null: false
    t.integer "status", default: 0, null: false
    t.bigint "user_id"
    t.index ["name"], name: "index_tasks_on_name"
    t.index ["user_id"], name: "index_tasks_on_user_id"
  end
```

### 検索のロジック

モデルにscopeを書いて実装。

```ruby:app/models/task.rb（一部略）
  scope :default_order, -> { order(created_at: :desc) } # 作成日を降順に並べるscopeの名前を変更し分かりやすく
  scope :sort_deadline, -> { order(deadline: :desc) }
  scope :search_with_name, -> (name) {
    return if name.blank?
    where('name LIKE ?', "%#{name}%")
  }
  scope :search_with_status, -> (status) {
    return if status.blank?
    where(status: status)
  }

  scope :search_with_label, -> (label) {
    return if label.blank?
    joins(:labels).where('labels.id = ?', label) # where(id: label)じゃだめ
  }
```

```ruby:app/controllers/tasks_controller.rb
  def index
    @tasks = current_user.tasks
    
    @tasks = @tasks
    .search_with_name(params[:name])
    .search_with_status(params[:status])
    .search_with_label(params[:label])
    
    # 中略
  end
```

検索を実装してるviewのコード

```ruby:app/views/tasks/index.html.slim
= form_with(method: :get, local: true, url: tasks_path) do |f|
  = f.label :name_search, t('.name_search'), value: params[:name]
  = f.search_field :name, placeholder: t('.name_search'), class: 'form-control'

  = f.label :status_search, t('.status_search'), value: params[:status]
  = f.select :status, Task.enum_options_for_select(:status), class: 'form_control', include_blank: true, selected: ''

  = f.label :label_search, t('.label_search'), value: params[:label]
  = f.select :label, Label.pluck(:name, :id), { include_blank: true }

  = f.hidden_field :search, value: true
  div.search_button = f.submit(t('.search'), class: 'btn btn-secondary')
```

実際のページはこんな感じ

![bdee4b6507d645ce831fc2bddd61b40c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451253/a47b02b6-8230-e2d9-9598-a6e9a537b0c1.png)

ラベルでの検索も問題なく実装できていた。ように見えていた。

## System Specを書いていると……

ラベルでの検索機能のSystem Spec。ラベルのみでの検索は無事テストを書けたので、タスク名、ステータス、ラベルの3つ全てで検索をするテストを書いてみた。

```ruby:spec/system/task_spec.rb
context 'タスク名、ステータス、ラベルの全てで検索した場合' do
  before do
    fill_in 'タスク名で検索', with: "TEST_TASK"
    select '未着手', from: :status
    select '勉強', from: :label
    click_button '検索'
  end

  example 'マッチしたタスクのみが表示される' do
    within ('tbody') do
      expect(page).to have_text "TEST_TASK", "未着手", "勉強"
    end
  end
end
```

FactoryBotで2つのデータを用意している。検索した結果、このタスクのみが表示されるはずだった。

![スクリーンショット 2020-04-04 21.44.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451253/d9627314-65d3-7d56-04c5-f480f507d9df.png)

しかしここで問題発生。
テストが失敗してエラー時のスクリーンショットも見たことないような画面に……。

![failures_r_spec_example_groups_tasks_nested_nested_4_nested_マッチしたタスクのみが表示される_200.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451253/3d890783-3bb4-66ad-391f-708ec513a2ec.png)

コンソールのログを見てみると、

```bash
Failures:

  1) Tasks タスク一覧画面 タスクの検索機能 タスク名、ステータス、ラベルの全てで検索した場合 マッチしたタスクのみが表示される
     Failure/Error: - @tasks.each do |task|
     
     ActionView::Template::Error:
       PG::AmbiguousColumn: ERROR:  column reference "name" is ambiguous
       LINE 1: ...ngs"."label_id" WHERE "tasks"."user_id" = $1 AND (name LIKE ...
                                                                    ^
       : SELECT  "tasks".* FROM "tasks" INNER JOIN "labelings" ON "labelings"."task_id" = "tasks"."id" INNER JOIN "labels" ON "labels"."id" = "labelings"."label_id" WHERE "tasks"."user_id" = $1 AND (name LIKE '%TEST_TASK%') AND "tasks"."status" = $2 AND (labels.id = '1') ORDER BY "tasks"."created_at" DESC LIMIT $3 OFFSET $4
```

`PG::AmbiguousColumn: ERROR`で検索。

[ActionView::Template::Error:PG::AmbiguousColumn: ERROR…でRSpecが失敗して困った。 - Qiita](https://qiita.com/harashoo/items/3eae7395a16a55378c1c)

>Taskモデルに、TaskモデルとアソシエーションしているLabelモデルをjoinsで結合しています。
それによりorder("created_at desc")が、tasksテーブルにあるcreated_atを指しているのか、labelsテーブルにあるcreated_atを指しているのか、曖昧（ = AmbiguousCoulumn ）になっていることがエラーの原因でした。

これを見てもう一度コンソールのログを見直すと、`column reference "name" is ambiguous`と文句言われてる。そこで気付いた。

**Labelテーブル**にも**Taskテーブル**にも**nameカラムがある**ことに。
その上、検索を実装してるscopeの中身を見ると、

```ruby:app/models/task.rb
  scope :search_with_name, -> (name) {
    return if name.blank?
    where('name LIKE ?', "%#{name}%")
  }
```

と、`'name LIKE?'`だけで何のテーブルかを指定していないのが原因そうだ。

## 解決法

`where('name LIKE ?', "%#{name}%")`を、`where('tasks.name LIKE ?', "%#{name}%")`に。

何のnameかを指定してあげる。

```ruby:app/models/task.rb
scope :search_with_name, -> (name) {
    return if name.blank?
    where('tasks.name LIKE ?', "%#{name}%")
  }
```

これで無事テストも通ったし、タスク名、ステータス、ラベル全てでの検索をしても問題なく動くようになった。

関連付けしたテーブルで同じカラム名がある場合は注意する！