railsでgemのgeocoderを使ってJOINしたときにJOIN先テーブルのSELECTがついてしまい、JOIN先のデータで上書きされてしまうというのでハマったのでメモしておきます。
結論としてはgeocoderのオプションでSELECTを指定する必要があったということでした。
定義
Groupは1対多でShopのレコードを持つようにしています。
groupモデル
class Group < ApplicationRecord
has_many :shops
end
データ
id | name |
---|---|
1 | セブンイレブン |
Shopモデル
Shopモデルにはnearメソッドを使用できるように設定しておきます。
class Shop < ApplicationRecord
geocoded_by latitude: :latitude, longitude: :longitude
end
データ
id | name | group_id | latitude | longitude |
---|---|---|---|---|
1 | セブンイレブン 那覇松山1丁目店 | 1 | 26.2177288 | 127.6799351 |
2 | セブンイレブン 那覇松山2丁目店 | 1 | 26.2186288 | 127.6804286 |
3 | セブンイレブン 那覇西2丁目店 | 1 | 26.2130606 | 127.6705822 |
実行
groupsテーブルにshopsテーブルをJOINして実行してみます(この場合はJOINを使わなくてもshopsテーブルを基準に条件を設定すれば必要な検索はできると思います)
shop = Shop.find(1)
Group
.where(id: 1)
.joins(:shops)
.merge(Shop.near([shop.latitude, shop.longitude], 0.5, units: :km))
すると、GroupのidとnameがShopのidとnameで書き換わってしまっています。
[#<Group:0x00007ff19461d338
id: 1,
name: "セブンイレブン 那覇松山1丁目店">,
#<Group:0x00007ff19461d270
id: 2,
name: "セブンイレブン 那覇松山2丁目店">]
クエリを確認してみると
SELECT
shops.*,
6371.0 * 2 * ASIN(SQRT(POWER(SIN((26.2177288 - shops.latitude) * PI() / 180 / 2), 2) + COS(26.2177288 * PI() / 180) * COS(shops.latitude * PI() / 180) * POWER(SIN((127.6799351 - shops.longitude) * PI() / 180 / 2), 2))) AS distance,
MOD(CAST((ATAN2(((shops.longitude - 127.6799351) / 57.2957795),((shops.latitude - 26.2177288) / 57.2957795)) * 57.2957795) + 360 AS decimal), 360) AS bearing
FROM
`groups`
INNER JOIN
`shops`
ON `shops`.`group_id` = `groups`.`id`
WHERE
`groups`.`id` = 1
AND (
shops.latitude BETWEEN 26.213232191970405 AND 26.222225408029594
AND shops.longitude BETWEEN 127.67492283916334 AND 127.68494736083665
AND (6371.0 * 2 * ASIN(SQRT(POWER(SIN((26.2177288 - shops.latitude) * PI() / 180 / 2), 2) + COS(26.2177288 * PI() / 180) * COS(shops.latitude * PI() / 180) * POWER(SIN((127.6799351 - shops.longitude) * PI() / 180 / 2), 2)))) BETWEEN 0.0 AND 0.5
)
ORDER BY
distance ASC
LIMIT 11
のようになっており、shops.*
がついてしまっているのが原因でした。
このSELECTはオプションで設定可能で、select: 'shops.id AS shop_id, shops.name AS shop_name'
の様に引数を渡せば設定可能です。
SELECTを設定する
Group
.where(id: 1)
.joins(:shops)
.select('groups.id, groups.name')
.merge(Shop.near([shop.latitude, shop.longitude], 0.5, units: :km, select: 'shops.id AS shop_id, shops.name AS shop_name'))
SQL
SELECT
groups.id,
groups.name,
shops.id AS shop_id,
shops.name AS shop_name,
6371.0 * 2 * ASIN(SQRT(POWER(SIN((26.2177288 - shops.latitude) * PI() / 180 / 2), 2) + COS(26.2177288 * PI() / 180) * COS(shops.latitude * PI() / 180) * POWER(SIN((127.6799351 - shops.longitude) * PI() / 180 / 2), 2))) AS distance,
MOD(CAST((ATAN2(((shops.longitude - 127.6799351) / 57.2957795),((shops.latitude - 26.2177288) / 57.2957795)) * 57.2957795) + 360 AS decimal), 360) AS bearing
FROM
`groups`
INNER JOIN
`shops`
ON `shops`.`group_id` = `groups`.`id`
WHERE
`groups`.`id` = 1
AND (
shops.latitude BETWEEN 26.213232191970405 AND 26.222225408029594
AND shops.longitude BETWEEN 127.67492283916334 AND 127.68494736083665
AND (6371.0 * 2 * ASIN(SQRT(POWER(SIN((26.2177288 - shops.latitude) * PI() / 180 / 2), 2) + COS(26.2177288 * PI() / 180) * COS(shops.latitude * PI() / 180) * POWER(SIN((127.6799351 - shops.longitude) * PI() / 180 / 2), 2)))) BETWEEN 0.0 AND 0.5
)
ORDER BY
distance ASC
LIMIT 11
結果
これで上書きされないようになりました。ちなみにShopのデータはshop_id
やshop_name
とかでアクセスすれば取得できます。
[#<Group:0x00007ff192776790 id: 1, name: "セブンイレブン">,
#<Group:0x00007ff1927766a0 id: 1, name: "セブンイレブン">]