llgruff
llgruff

Reputation: 69

Multiple OR filtering with Slick

Here I filter the table by 1 column(keyId) and 1 value(keyId), and result type is Optional, it's correct.

override def findByKeyId(keyId: String): Future[Option[Event]] =
    db.run(events.filter(_.keyId === keyId).result.headOption)

Now I need filter by 1 column(keyId) and multiple values.

override def findByKeyIdBulk(keyIdSeq: Seq[Option[String]]): Future[Seq[Option[Event]]] = {

    db.run(
      events.filter { event =>
        keyIdSeq
          .map(_.map(keyId => event.keyId === keyId))
          .collect({ case Some(criteria) => criteria })
          .reduceLeftOption(_ || _)
          .getOrElse(true: Rep[Boolean])
      }.result
    ) // Future[Seq[Event]]

  }

In this code expression of type Future[Seq[Event]] doesn't conform to Future[Seq[Option[Event]]]

How to fix?

Upvotes: 1

Views: 709

Answers (2)

Andrey Patseev
Andrey Patseev

Reputation: 524

I think you misunderstand the point of having Option[T] as a return value. Returning Seq[Option[T]] is anti-pattern and here is why:

Option[T] makes total sense when there is no guarantee that you'll find anything. FindById is a great example where it is useful - we either find something and get Some(value) or we don't and get None.

But if you are planning to find by several ids and want a Seq of found values, then there are two cases: either Seq is empty and nothing was found or Seq contains some elements. There is no point in having Option[T] inside Seq because there won't be any Nones inside the sequence. You'll be forced to use an extra pattern matching or extra map which is totally pointless.

I think you will be better off changing type signature to Future[Seq[Event]]

Upvotes: 2

Krzysztof Atłasik
Krzysztof Atłasik

Reputation: 22625

Calling events.result will result in returning Future[Seq[Event]]. Using filter won't change that, so the resulting type is still Future[Seq[Event]]. In order to change it to Future[Seq[Option[Event]]] you'd have to lift values to Option[Event]:

db.run(
  events.filter { event =>
    keyIdSeq
      .map(_.map(keyId => event.keyId === keyId))
      .collect({ case Some(criteria) => criteria })
      .reduceLeftOption(_ || _)
      .getOrElse(true: Rep[Boolean])
  }
  .map(_.map(Option(_))) //lifting Event to Option[Event]
  .result
) // Future[Seq[Option[Event]]]

Upvotes: 1

Related Questions