question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Suggest 'MultiColumn' Type

See original GitHub issue

Currently, when we want to create a mapping for a Scala type T to Slick, we have two options which depends on the number of fields the underlying representation in the database will represent (for that type T)

  1. If the type T can be sanely represented as a single field in a database, we generally use a MappedColumnType[A,T] which allows us to go from A to T. Obvious use case scenario is doing something like mapping a JodaTime DateTime to a SQL Date
  2. If the type T can’t be sanely represented as a single field, but instead 2 or more fields, we use custom extractors/constructors in the *= table definition (which is usually represented as a class extending the Table trait). An example might be representing Scala’s Either Type, we would want to represent this as 2 Not Null database fields

The problem with scenario 2, is that we haven’t really created a “proper” mapping, and this issue becomes obvious when you only want to update a select number of fields in a table. Lets say you have a User table, and you want to update only a certain number of fields, you would probably do something like this

val q = Users.filter(u => u.id === 1).map(user => user.firstName)
q.update("Bob")

However, lets assume that the User table has some custom type (which we represented in the way that point 2 describes), in this, lets assume the type is a Either[Int,String] which is called ‘someField’. We first have to represent this in table form, so we might do something like this

def someField_Left:Column[Option[Int]]("some_field_LEFT")
def someField_Right:Column[Option[String]]("some_field_RIGHT")

This issue is, if we want to update this field, like we did previously, we have to do something like this

val updateValue:Either[Int,String] // Assume this field is already defined
val (left,right) = updateValue match {
    case Left(i) => (Option(i),None)
    case Right(s) => (None,Option(s))
}
val q = Users.filter(u => u.id === 1).map(user => user.firstName,user.someField_Left,someField_Right)
q.update(("Bob",left,right))

When ideally, we should be doing this

val q = Users.filter(u => u.id === 1).map(user => user.firstName,user.someField)
q.update(("Bob",updateValue))

This issue occurs because we can’t (sanely) represent the Either[A,B] as a single column type, even though throughout our whole application code, that is how we want to represent it as. The proposal? Some way to define a MultiColumn[T]. A MultiColumn works the exact same way the standard Column would, so you can extract it as a single field when using map on a TableQuery. The difference is, you would have to specify how to represent it as multiple fields, maybe something like this

class EitherRep extends MultiColumnRepresentation[Either[Int,String]] {
    def left:ColumnAbstract[Option[Int]](this.originalFieldName + "_LEFT")
    def right:ColumnAbstract[Option[String]](this.originalFieldName + "_RIGHT")
    def * = (left,right) <> ((constructEither _).tupled,extractEither)

   private def constructEither(left:Option[Int],right:Option[String]) = {
       val either = (left,right) match {
            case (Some(i),None) => Left(i)
            case (None,Some(s)) => Right(s)
            case _ => throw SomeError // This shouldn't happen, throw some error
       }
       either
   }
   private def extractEither(either:Either[Int,String]) = {
       val (left,right) = either match {
             case Left(i) => (Option(i), None)
             case Right(s) => (None,Option(s))
       }
       Option((left,right))
   }

} 

// This is what the field would look like in our actual `Table` definition
def someField:MultiColumn[Either[Int,String]]("some_field",new EitherRep)

// So we can do stuff like this
val q = Users.filter(u => u.id === 1).map(user => user.firstName,user.someField)
// Instead of having to do this
val q = Users.filter(u => u.id === 1).map(user => user.firstName,user.someField_Left,someField_Right)

Note that in the actual database, we would still have 2 columns representing the Either, but in our application, for all intents and purposes, we treat it as a single “field”.

Issue Analytics

  • State:open
  • Created 9 years ago
  • Comments:10 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
mdedetrichcommented, Jun 9, 2015

Im giving this a bump, does anyone know if this is any easier in Slick 3.0?

0reactions
szeigercommented, Jul 3, 2014

Unless we can think of a better solution, Shapes are the way to go (and it took us long enough to come up with those, so I wouldn’t hold my breath for something better)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Try to work with multicolumn and get error - TeX
Some off-topic suggestions: for negative numbers you should use correct minus sign, so in columns with numbers I suggest to use S column...
Read more >
CSS Multiple Columns - W3Schools
The CSS multi-column layout allows easy definition of multiple columns of text ... The column-width property specifies a suggested, optimal width for the ......
Read more >
Multi-Column Question - SurveyMonkey Apply
Within the Multi-Column Question you will be able to choose between 4 different types of Columns: Dropdown; Text Response; Checkbox; Radio ...
Read more >
Setting the Import Format for Multi-Column Data Types
You specify contiguous columns by using starting and ending columns. For example, 5,10 indicate columns 5 through 10 . You specify non-contiguous columns...
Read more >
How to change multiple columns' types in pyspark?
Try this: from pyspark.sql.functions import col df = df.select([col(column).cast('double') for column in df.columns]).
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found