【问题标题】:slick - dynamic filtering list of optionsslick - 选项的动态过滤列表
【发布时间】:2020-04-19 06:10:34
【问题描述】:

我有一个光滑的 scala 项目。 我有两个表用户和用户设备: 表之间的关系是一对多的

CREATE TABLE "user"
(
  id      UUID    NOT NULL,
  name    VARCHAR NOT NULL,
  surname VARCHAR NOT NULL
);

CREATE TABLE user_devices
(
  id           UUID NOT NULL,
  user_id      UUID NOT NULL,
  device_name  VARCHAR,
  device_model VARCHAR
);

我需要在 slick 中做这样的查询:

SELECT * FROM audience.user as u
JOIN user_devices on(u.id=user_devices.user_id)
where (device_name = 'Samsung' and device_model = 'A20')
   OR device_name = 'Xaomi'
;

这是我的 scala 代码

object 示例扩展 App {

  case class UserRow(id: UUID, name: String, surname: String)
  case class UserDevices(id: UUID,
                         userId: UUID,
                         deviceName: Option[String],
                         deviceModel: Option[String])
  case class Params(deviceName: Option[String], deviceModel: Option[String])
  case class FilterBy(params: Option[List[Params]])

final class UserTable(tag: Tag) extends TableUserRow {

def id = column[UUID]("id")
def name = column[String]("name")
def surname = column[String]("surname")

def * = (id, name, surname).mapTo[UserRow]

}

最终类 UserDevicesTable(tag: Tag) 扩展 TableUserDevices {

def id = column[UUID]("id")
def userId = column[UUID]("user_id")
def name = column[Option[String]]("device_name")
def model = column[Option[String]]("device_model")

def * = (id, userId, name, model).mapTo[UserDevices]

}

  def filter(filterBy: FilterBy): Future[Seq[UserRow]] = {

    val query = userTable
      .joinLeft(userDevicesTable)
      .on(_.id === _.userId)
      .filterOpt(filterBy.params) {
        case ((user: UserTable, userDevice: Rep[Option[UserDevicesTable]]),
              params:Seq[Params]) =>
          ???
      }
      .map(_._1)
      .result

    db.run(query)
  }
}

如何在 Params obj 之间使用 Or 进行过滤,以及如何使用 AND 在 Param obj 中的参数之间进行过滤? 有什么建议吗?

【问题讨论】:

    标签: sql scala slick


    【解决方案1】:

    这种情况的基本思路是:

    1. 将您的List[Params] 映射到Slick 条件;然后
    2. 将所有条件与 AND 或 OR 组合在一起(通过 reduceLeft)。

    这是一个基本的例子:

    val params =
     Params(Some("Samsung"), Some("A20")) ::
     Params(Some("Xaomi"),   None) ::
     Nil
    
    // This is not quite complete, but a starting point:
    val query = userTable.filter(row =>
      params
       .map { case Params(name, model) => row.name === name && row.model === model }
       .reduceLeft(_ || _)
    )
    

    请注意,我们正在将每个 Params 转换为 Slick 条件(这里我们将两个条件与运算)。然后我们将reduceLeft 中的每个条件与|| 连接在一起。

    该查询将产生如下 SQL:

    select 
      "device_name", "device_model" 
    from 
      "devices" 
    where 
       (("device_name" = 'Samsung') and ("device_model" = 'A20')) 
    or (("device_name" = 'Xaomi')   and ("device_model" = null))
    

    由于与=== null 比较,这不太正确。有多种方法可以解决这个问题,但一种是在各种情况下进行模式匹配:

    val query =
      userTable.filter(row =>
        params
          .collect {
            case Params(Some(name), None)  => row.name === name
            case Params(None, Some(model)) => row.model === model
            case Params(Some(name), Some(model)) => row.name === name && row.model === model
          }
          .reduceLeft(_ || _)
        )
    

    我使用collect 来避免预期的非详尽模式匹配。

    该查询将产生:

    where 
        ( ("device_name" = 'Samsung') and ("device_model" = 'A20') )
     or ("device_name" = 'Xaomi')
    

    我有一个包含 AND 和 OR 示例的演示库:https://github.com/d6y/list-of-tuples/tree/master/src/main/scala

    【讨论】:

      猜你喜欢
      • 2014-12-20
      • 1970-01-01
      • 2015-03-26
      • 1970-01-01
      • 1970-01-01
      • 2017-04-12
      • 1970-01-01
      • 1970-01-01
      • 2018-04-09
      相关资源
      最近更新 更多