【问题标题】:How to filter on a Option[Boolean] column in slick如何过滤 slick 中的 Option[Boolean] 列
【发布时间】:2018-03-24 04:49:36
【问题描述】:

我的数据库中有以下列,这是一个布尔值,但也接受 NULL,所以 true、false 和 NULL 都是有效的:

def rtb = column[Option[Boolean]]("rtb")

并从我想过滤的客户端获得以下可选输入:

rtbFromClient: Option[Boolean] = ... 

我有以下内容(基于this answer on how to do queries in slick:https://stackoverflow.com/a/40888918/5300930):

val query = userTable.
      filter(row => 
          if (rtbFromClient.isDefined) 
              row.rtb.get === rtbFromClient.get 
          else 
              LiteralColumn(true)
      )

但在代码运行时出现此错误:

Caught exception while computing default value for Rep[Option[_]].getOrElse -- This cannot be done lazily when the value is needed on the database side

我认为这可能是因为 row.rtb.get 在调用 get 时抛出异常,因为 db 中的值为 null,所以尝试将其更改为 row.rtb.getOrElse(null) 和 row.rtb.getOrElse(无)但这些都不起作用)

还尝试了以下方法:

if (rtbFromClient.isDefined) {
    val query = query.filter(_.rtb.isDefined).filter(_.rtb.get === rtbFromClient.get)
}

但这也会在运行时抛出同样的错误:

Caught exception while computing default value for Rep[Option[_]].getOrElse -- This cannot be done lazily when the value is needed on the database side

总结一下:

  • 我的数据库中有一个 Option[Boolean] 列,它可以包含 true、false 或 NULL(实际的 mysql 类型是 tinyint(1),它映射到一个光滑的 Option[Boolean])
  • 我有一个用户提供的可选过滤器 rtbFromClient,如果存在我想对其进行过滤。如果存在,这将是真或假
  • 我有其他可选过滤器(此处未显示)具有类似行为,因此我希望能够轻松组合它们

【问题讨论】:

    标签: scala slick slick-3.0


    【解决方案1】:

    我遇到了同样的问题。我的解决方案是(使用 Slick 3.3.x 测试):

    val query = usersTable.filterOpt(rtbFromClient)(_.rtb === _)
    

    情况一(rtbFromClient为空时)对应的SQL如下:

    select * from users;
    

    情况2(定义了rtbFromClient):

    select * from users where rtb = ?;
    

    【讨论】:

      【解决方案2】:

      首先检查该列是否为空,然后继续进行另一个比较。确保rtbFromClient 不是一个选项。

      val query = userTable.filter(row => !row.rtb.isEmpty &&
                                          row.rtb === rtbFromClient)
      

      第一个条件确保过滤掉空值,第二个条件检查值以防列值不为空。

      如果您有可选值,那么下面的代码会有所帮助。

      def query(value: Option[Boolean]) = value match {
         case Some(userGivenRtbFromClient) =>
          userTable.filter(row => !row.rtbFromClient.isNull &&
               row.rtbFromClient === userGivenRtbFromClient)
         case None => userTable.filter(row => row.rtbFromClient.isNull)
      }
      

      更清洁的版本在这里。

      rtbFromClient: Option[Boolean] 
      

      rtbFromClient 用户给定可选值以与光滑列进行比较。

      userTable.filter(row => rtbFromClient.map(value => row.rtb === Some(value): Option[Boolean]).getOrElse(row.rtb.isEmpty))
      

      【讨论】:

      • 这在 rtbFromClient 为 None 的情况下不起作用,在这种情况下不应执行过滤 - 请参阅原始问题中的 else LiteralColumn(true)
      • @Rory 简单的改变会有所帮助,当不存在时。编辑了答案
      • 如果我有多个可选过滤器怎么办?我不能这样链接它们 - filter(...).filter(...)
      • @Rory query 返回一个查询,之后您可以使用mapfilter 等进行链接
      • @Rory 使用这个userTable.filter(row => optional.map(_ === row.rtbFromClient).getOrElse(row.rtbFromClient.isNull))。这好多了
      【解决方案3】:
      var query = userTable.drop(page.skip).take(page.top)
      
      if (rtbFromClient.isDefined) {
          query = query.filter(row => row.rtb.isDefined && row.rtb === rtbFromClient)
      }
      
      // More if blocks with optional filters here
      ...
      
      dbProvider.db.run(query.result)
      

      【讨论】:

        【解决方案4】:

        试试这个

        val query = userTable.filter(row => rtbFromClient.map(x => Option(x) === row.rtb).getOrElse(row.rtb.isEmpty.?))
        

        【讨论】:

        • 您能否进一步解释一下,以便其他人可以从中学习?
        • 这是对所提问题的直接回答。至少它对我很有效(我有同样的问题)
        • 好吧,当您将其发布为答案时,我想这应该是 :) 但是请对该代码添加一些解释:它有什么作用?请记住,人们来这里是为了从这些答案中学习,而不仅仅是复制和粘贴它们
        【解决方案5】:

        我遇到了同样的问题。玩了一圈后发现可以直接按选项过滤,所以如果我们有专栏

        def rtb = column[Option[Boolean]]("rtb")
        

        我们正在尝试过滤

        rtbFromClient: Option[Boolean] = ... 
        

        那么我们可以这样做:

        val query: DBIO[Seq[TableElementType]] = 
          if (rtbFromClient.isDefined)
            userTable.filter(_.rtb === rtbFromClient).result
          else
            userTable.result
        

        【讨论】:

          猜你喜欢
          • 2016-10-04
          • 1970-01-01
          • 2014-12-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-04
          • 2012-04-23
          相关资源
          最近更新 更多