私はdbにさまざまなタイプのエンティティを持っており、ある種の条件でタイプセーフな方法でそれらをフィルタリングできるようにしたいと考えています。私はdbアクセスのためにSlickを使用していますが、それは本当に問題ではありません。Scalaで型安全なフィルタリングを作成するには?
たとえば、ユーザーエンティティと投稿エンティティがあるとします。ユーザーにはidフィールドとemailフィールドがあり、Postにはidフィールドとtitleフィールドがあります。
今はADTを使用してフィルタを表しています。次のようになります。
sealed trait Filter {
def byId(id: Long): Filter = and(ById(id))
def byEmail(email: String): Filter = and(ByEmail(email))
def byTitle(title: String): Filter = and(ByTitle(title))
def and(other: Filter): Filter = And(this, other)
}
object Filter {
def apply(): Filter = NoFilter
}
case class ById(id: Long) extends Filter
case class ByEmail(email: String) extends Filter
case class ByTitle(title: String) extends Filter
case class And(a: Filter, b: Filter) extends Filter
case object NoFilter extends Filter
UserDaoでそれを解釈します。 PostDaoは同じになります。
trait Dao[A] {
def find(filter: Filter, offset: Int, limit: Int):Seq[A]
}
object UserDao extends Dao[User]{
override def find(filter: Filter, offset: Int, limit: Int): Seq[Any] = {
filterTable(filter).drop(offset).take(limit).result
}
private def filterTable(table: Query[UserTable, User, Seq], filter: Filter): Query[UserTable, User, Seq] =
filter match {
case ById(id) => table.filter(_.id === id)
case ByEmail(email) => table.filter(_.email === email)
case And(a, b) => filterTable(filterTable(table, a), b)
case NoFilter => table
case other =>
log.warn(s"Filter not supported $other")
table
}
}
そして汎用サービスにより使用されます。あなたはそれが動作参照が、それは安全で入力していないとして
class Service[A](dao: Dao[A]) {
def find(filter: Filter): Seq[A] = {
// do some stuff
dao.find(filter, offset = 0, limit = 100)
// do some other stuff
}
}
。タイトルによるユーザーのフィルタリングは失敗しませんが、それは意味をなさないし、バグの原因となる可能性があります。私は別のエンティティのためにADTの異なるセットを作成するかもしれないと思ったが、その後、私は非常に似ているフィルタを複製する必要があるだろう(例えばIDフィルタ)。要約すると、私はフィルターを持っています:
- タイプセーフです。あなたはユーザー あなたがそうで
- 用フィルター
ByTitle
を作成することはできません。
ById
、
And
、
Or
、
In
などの一般的なフィルタを複製する必要はありません
どうすればいいですか?たぶんADTは私が必要としているものではありませんか?
ありがとうございました!それは非常に興味深いアプローチであり、うまくいくかもしれません。私は本当に私のドメインオブジェクトにそのような特性を追加したくないが、私は回避策があると思う。私を悩ますことの1つは、網羅的でないパターンマッチです。このアプローチには網羅的なパターンマッチがありますか?私は今日それを試してみると、うまくいけば私はあなたの答えを受け入れる。再度、感謝します。 –
まあ、 'フィルター'形質を封印すると、完全に一致するはずです。 typeclassesを使用してドメインオブジェクトに特性を追加する必要がなくなりますが、(1)それはより多くのタイプ入力であり、さらに多くの "魔法"がリーダーやコードのために行われるため、通常はありません他のオプションがある限り(つまり、あなたのドメインオブジェクト定義を管理している限り)、(2)そのような場合には完全に一致させる方法は考えられません。 – Dima