ThaDon
ThaDon

Reputation: 8068

Compose update query based on optional values

The below function has a UUID param and then two optional parameters. Depending on the state of the two optional parameters will determine whether or not they are included in the update query.

    def updateChunkState(chunkId: UUID, state: ChunkState, comment: Option[String], sourceLanguage: Option[String]): Either[ChunkNotFoundError, Unit] = {
      
      val result = (comment, sourceLanguage) match {
        case (c: Some[String], s: Some[String]) => {
          val query = ChunkTable.query.filter(_.id === chunkId.toString).map(c => (c.state, c.comment, c.sourceLanguage))
          exec(query.update(state, comment, sourceLanguage)) 
        }
        case (c: Some[String], None) => { 
          val query = ChunkTable.query.filter(_.id === chunkId.toString).map(c => (c.state, c.comment))
          exec(query.update(state, comment)) 
        }
        case (None, s: Some[String]) => {
          val query = ChunkTable.query.filter(_.id === chunkId.toString).map(c => (c.state, c.sourceLanguage))
          exec(query.update(state, sourceLanguage)) 
        }
        case (None, None) => {
          val query = ChunkTable.query.filter(_.id === chunkId.toString).map(c => (c.state))
          exec(query.update(state)) 

        }
      }
      result match {
        case 0 => Left(ChunkNotFoundError(chunkId))
        case n if n > 0 => Right(())
      } 

    }

I believe there should be a way to compose my query/update a bit more elegantly than my brute force attempt above and I would be grateful if someone could enlighten me.

Upvotes: 0

Views: 69

Answers (1)

SergeiAK
SergeiAK

Reputation: 51

I think the way you are doing it atm is fine. There is no way out of the 4 matches. Maybe a way of making it more elegant would be to collect the duplicate code to only one place, ie. instead of calling exec(query.update(...)) in 4 places, you could call it in one. Here I just call it as a list of strings, i dont know how the function takes the variable number of arguments. And instead of filtering it in 4 different places you could filter it once, before you start the pattern match. Just makes it a bit easier to read, as it is not a large block of code.

And if you are not using the tuple variables you match on, there is no need for giving them names, c and s that is. And also the type is not needed, you know it is a string from the funtions signature. I prefer Some(_) in that case (where i dont need the variable), and if you match the correct way, from (Some, Some) to (None, None), use _ (wildcard) for the None cases, and _ for the (None, None) case

I made a list of all the arguments that you use for query.update() and put them in a list. Wrapped state in a Some() and used flatten to remove the Nones. Then applied these to the function with _* operator.

I think the code is fine as is, but you could cut off 10 lines like this.

def updateChunkState(chunkId: UUID, state: ChunkState, comment: Option[String], sourceLanguage: Option[String]): Either[ChunkNotFoundError, Unit] = {

    val updateParams = List(Some(state), comment, sourceLanguage).flatten

    val queryFiltered = ChunkTable.query.filter(_.id === chunkId.toString)

    val query = (comment, sourceLanguage) match {
      case (Some(_), Some(_)) => queryFiltered.map(c => (c.state, c.comment, c.sourceLanguage))
      case (Some(_), _) => queryFiltered.map(c => (c.state, c.comment))
      case (_, Some(_)) => queryFiltered.map(c => (c.state, c.sourceLanguage))
      case _ => queryFiltered.map(c => (c.state))
    }

    exec(query.update(updateParams:_*)) match {
      case 0 => Left(ChunkNotFoundError(chunkId))
      case n if n > 0 => Right(())
    }

Upvotes: 2

Related Questions