発端
「新規で作成する期間が作成済みの期間と被らないようにバリデーションつけといて〜」
こんな頼みが上司からあり、すぐ実装できるだろうと思ったのですが・・・
なかなか苦戦したので、その備忘録です。
最初に苦戦した点を話すと、
作成済みの開始〜終了の間の期間に新規作成すると検索に引っかからない点です。
その解決策と、Railsでの実装方法について書いていきます。
試したこと
今回は、例として予約の期間が被らないようにする、Reservationモデルで試します。
最初は、効率がよくないですが、こちらで試しました。
Reservation.where(started_at: self.started_at..self.finished_at) or
Reservation.where(finished_at: self.started_at..self.finished_at)
しかし、問題点が・・・
しかし、上記の方法では、問題点が浮かび上がりました。
対象期間 2019/08/05 ~ 2019/10/15
期間① 2019/07/01 ~ 2019/07/30
期間② 2019/11/01 ~ 2019/11/30
期間③ 2019/07/30 ~ 2019/08/15
期間④ 2019/09/15 ~ 2019/10/30
期間⑤ 2019/09/01 ~ 2019/09/30
①〜②は対象外のため引っかからない、③〜④は正常に引っかかるのですが、
問題は、⑤が引っかからないのです。
仕様にもよるかもしれませんが、期間が完全に重複しているのに、検索で引っかかってくれないのは大問題です!
解決策
調べたところ、解決策がありました!
期間が重複しているかを判定する条件式の導出方法がとても参考になりました。
puts 'code with syntax'
(対象終了日付 > 比較開始日付) AND (比較終了日付 > 対象開始日付)
これをActiveRecordで書き直すと、
Reservation.where('finished_at > ? and ? > started_at', self.started_at, self.finished_at)
これで既存の開始〜終了の間の期間に、作ろうとした新規予約を弾くことができるようになりました!
感想
アルゴリズムの大切さを実感しました。
まず該当しない場合を考え、逆にして該当する場合を導き出すド・モルガンの法則は、今後も使う場面がありそうです!
参考
以下の記事を参考にさせていただきました。ありがとうございました。