HitoHana(ひとはな)の舞台裏

HitoHana(ひとはな)運営の舞台裏をご紹介いたします!

Rails MySQLハイフン付きの値をソートして取り出す

あけまして

おめでとうございます。新人の田中です。 数ヶ月放置していたブログも再開です。今回は年明け一発目に少し困ったことについて書こうと思います。

レンタルグリーン

この度ひとはなで観葉植物のレンタルサービスをはじめました!植物のメンテナンスから、必要であればおしゃれで置き場にあう観葉植物の選定まで承ります。ぜひ一度ご覧になってみてください。

hitohana.tokyo

で本題なんですが、こちらのページに「商品ラインナップに観葉植物の商品写真を決まった順序で掲載してほしい」との要望がありました。 そんなん楽勝じゃんと当初は思っていたのですが。。。

Rails+MySQLでカラムの値を使ってソートする

のはそんなに難しくなくって下記の記事に記載してある通り、MySQLのfield関数を使ってソートすればいいんですよね。ここまではいいんですよ。

ids = [3, 1, 2]
YourModel.where(id: ids).order("field(id, #{ids.join(',')})")

qiita.com

商品スラグ(商品コード)で並べればいいじゃん

ひとはなではパキラ、ウンベラータなどの商品は一意の商品スラグ(商品コード)で管理しています。この商品スラグのネーミングルールは以下の通り。

  • 1つ以上のキーワードと1つの数字を組み合わせる
  • キーワード同士、キーワードと数字はハイフンでつなげる

ですので、「pachira-1, pachira-2, umbellata-10」みたいな感じです。この値が一意なので以下のようにコードを書いてみました。

slugs = %w(pachira-125 umbellata-92 olive-110)
Product.where(slug: slugs).order("field(slug, #{slugs.join(',')})")

はい。見事に失敗しました。

原因は

ハイフン付きの値でした。発行されたSQLを見てみると

SELECT
    `products`.*
FROM
    `products`
WHERE
    `products`.`slug` IN ('pachira-125', 'umbellata-92', 'olive-110')
ORDER BY
    field(slug, pachira-125, umbellata-92, olive-110)

はてなブログのシンタックスハイライトがあるとわかりやすいですね。

field関数内部では引数として渡されたpachira-125が文字列ではなく、pachiraマイナス125という数式で評価されてしまっているため、構文エラーを起こしていました。

じゃあどうすんの?

field関数を使ってハイフン付きの値でソートしたい。でもハイフン付きの値を直接field関数に渡すと数式として評価されてしまう。

それならfield関数に渡す引数をいっそ別の数式にしちゃって、その数式の評価結果がハイフン付きの文字列になってればいいじゃん

という考えで以下のようにしてみました。

slugs = %w(pachira-125 umbellata-92 olive-110)
convert = ->(slug){"concat_ws('-', '#{slug.split('-').join('\',\'')}')"}
Product.where(slug: slugs).order("field(slug, #{slugs.map(&convert).join(',')})")

上手くうごいてくれました。発行されたSQLは以下です。

SELECT
    `products`.*
FROM
    `products`
WHERE
    `products`.`slug` IN ('pachira-125', 'umbellata-92', 'olive-110')
ORDER BY
    field(slug, concat_ws('-', 'pachira', '125'), concat_ws('-', 'umbellata', '92'), concat_ws('-', 'olive', '110'))

最初楽勝だと思ってんですが、こんなに手こずるとは思いませんでした。。。

おわりに

HitoHana(ひとはな)では、仕事が大好きで個性的なエンジニアを大募集しています!

www.wantedly.com