I am new to Slick and using Slick 3.1.1. My table looks like
import java.sql.{Blob, Timestamp}
import slick.collection.heterogeneous.HNil
import slick.driver.MySQLDriver.api._
case class AnomalyC(id: Int, serviceName: String, serviceId: String, timeUpdated: Timestamp, timestamp: Timestamp, anomalyCategoryId: Int,
userGroup:Int, riskValue: Float, activityTypeId: Int, destinationHost: String, userName: String, tenantId: Int, information:Blob, timeCreated: Timestamp, userId: Int, anomalyType:Int, anomalyValue:String, measure:Int,
userAction:Int, uniqueIdentifier:Int, similarCount:Int, trainingValue:String, state: Int, riskLevel:Int, userRiskLevel:Int,
userRiskScore: Float, response:Int)
class Anomaly(tag:Tag) extends Table[AnomalyC](tag, "Anomaly") {
def id = column[Int]("id")
def serviceName = column[String]("ServiceName")
def serviceId = column[Int]("ServiceId")
def timeUpdated = column[Timestamp]("TimeUpdated")
def timestamp = column[Timestamp]("Timestamp")
def anomalyCategoryId = column[Int]("AnomalyCategoryId")
def userGroup = column[Int]("UserGroup")
def riskValue = column[Float]("RiskValue")
def activityTypeId = column[Int]("ActivityTypeId")
def destinationHost = column[String]("DestinationHost")
def userName = column[String]("UserName")
def tenantId = column[Int]("TenantId")
def information = column[Blob]("Information")
def timeCreated = column[Timestamp]("TimeCreated")
def userId = column[Int]("UserId")
def anomalyType = column[Int]("AnomalyType")
def anomalyValue = column[String]("AnomalyValue")
def measure = column[Int]("Measure")
def userAction = column[Int]("UserAction")
def uniqueIdentifier = column[String]("UniqueIdentifier")
def similarCount = column[Int]("SimilarCount")
def trainingValue = column[String]("TrainingValue")
def state = column[Int]("State")
def riskLevel = column[Int]("RiskLevel")
def userRiskLevel = column[Int]("UserRiskLevel")
def userRiskScore = column[Float]("UserRiskScore")
def response = column[Int]("Response")
def * = (id, serviceName, serviceId, timeUpdated, timestamp, anomalyCategoryId, userGroup,
riskValue, activityTypeId, destinationHost, userName, tenantId, information, timeCreated, userId, anomalyType, anomalyValue,
measure, userAction, uniqueIdentifier, similarCount, trainingValue, state, riskLevel, userRiskLevel, userRiskScore, response)
}
When I run this, I get error as
Error:(57, 11) too many elements for tuple: 27, allowed: 22
def * = (id, serviceName, serviceId, timeUpdated, timestamp, anomalyCategoryId, userGroup,
^
What do I do?
You could use HLists. Slick has it's own implementation or for probably better interoperability you could use Shapeless. Here is an article that explains it: http://underscore.io/blog/posts/2015/08/08/slickless.html
Alternatively you can use a case class instead of a tuple.
The following worked for me based on the direction provided by #cvogt.
import java.sql.{Blob, Timestamp}
import slick.collection.heterogeneous.HNil
import slick.collection.heterogeneous.syntax._
import slick.driver.MySQLDriver.api._
class Anomaly(tag:Tag) extends Table[Int :: String :: Int :: Timestamp :: Timestamp :: Int :: Int :: Float :: Int :: String
:: String :: Int ::Blob :: Timestamp :: Int ::Int ::String ::Int ::Int ::String ::Int ::String :: Int ::Int ::Int ::
Float :: Int :: HNil ](tag, "Anomaly") {
def id = column[Int]("id")
def serviceName = column[String]("ServiceName")
def serviceId = column[Int]("ServiceId")
def timeUpdated = column[Timestamp]("TimeUpdated")
def timestamp = column[Timestamp]("Timestamp")
def anomalyCategoryId = column[Int]("AnomalyCategoryId")
def userGroup = column[Int]("UserGroup")
def riskValue = column[Float]("RiskValue")
def activityTypeId = column[Int]("ActivityTypeId")
def destinationHost = column[String]("DestinationHost")
def userName = column[String]("UserName")
def tenantId = column[Int]("TenantId")
def information = column[Blob]("Information")
def timeCreated = column[Timestamp]("TimeCreated")
def userId = column[Int]("UserId")
def anomalyType = column[Int]("AnomalyType")
def anomalyValue = column[String]("AnomalyValue")
def measure = column[Int]("Measure")
def userAction = column[Int]("UserAction")
def uniqueIdentifier = column[String]("UniqueIdentifier")
def similarCount = column[Int]("SimilarCount")
def trainingValue = column[String]("TrainingValue")
def state = column[Int]("State")
def riskLevel = column[Int]("RiskLevel")
def userRiskLevel = column[Int]("UserRiskLevel")
def userRiskScore = column[Float]("UserRiskScore")
def response = column[Int]("Response")
def * = id :: serviceName :: serviceId :: timeUpdated :: timestamp :: anomalyCategoryId :: userGroup ::
riskValue :: activityTypeId :: destinationHost :: userName :: tenantId :: information :: timeCreated :: userId :: anomalyType :: anomalyValue ::
measure :: userAction :: uniqueIdentifier :: similarCount :: trainingValue :: state :: riskLevel :: userRiskLevel :: userRiskScore :: response :: HNil
}
The build runs and test pass, however, I still see IntelliJ complains with the following error
As per answer from #insan-e, I re-wrote this and this works as well. I like this approach better however I do not understand the code in it's entirety
import java.sql.{Blob, Timestamp}
import slick.driver.MySQLDriver.api._
case class Anomaly1(id:Int, serviceName:String, serviceId: Int, timeUpdated: Timestamp, timeStamp: Timestamp,
anomalyCategoryId: Int, userGroup: Int, riskValue: Float, activityTypeId: Int, destinationHost: String, userName: String)
case class Anomaly2(tenantId: Int, information:Blob, timeCreated: Timestamp, userId: Int, anomalyType:Int, anomalyValue: String, measure: Int, userAction: Int,
uniqueIdentifier: String, similarCount: Int, trainingValue: String, state: Int, riskLevel: Int,
userRiskLevel: Int, userRiskScore: Float, response: Int)
case class AnomalyRow(anomaly1: Anomaly1, anomaly2: Anomaly2)
class Anomaly(tag:Tag) extends Table[AnomalyRow](tag, "Anomaly") {
def id = column[Int]("id")
def serviceName = column[String]("ServiceName")
def serviceId = column[Int]("ServiceId")
def timeUpdated = column[Timestamp]("TimeUpdated")
def timestamp = column[Timestamp]("Timestamp")
def anomalyCategoryId = column[Int]("AnomalyCategoryId")
def userGroup = column[Int]("UserGroup")
def riskValue = column[Float]("RiskValue")
def activityTypeId = column[Int]("ActivityTypeId")
def destinationHost = column[String]("DestinationHost")
def userName = column[String]("UserName")
def tenantId = column[Int]("TenantId")
def information = column[Blob]("Information")
def timeCreated = column[Timestamp]("TimeCreated")
def userId = column[Int]("UserId")
def anomalyType = column[Int]("AnomalyType")
def anomalyValue = column[String]("AnomalyValue")
def measure = column[Int]("Measure")
def userAction = column[Int]("UserAction")
def uniqueIdentifier = column[String]("UniqueIdentifier")
def similarCount = column[Int]("SimilarCount")
def trainingValue = column[String]("TrainingValue")
def state = column[Int]("State")
def riskLevel = column[Int]("RiskLevel")
def userRiskLevel = column[Int]("UserRiskLevel")
def userRiskScore = column[Float]("UserRiskScore")
def response = column[Int]("Response")
private type Anomaly1TupleType = (Int, String, Int, Timestamp, Timestamp, Int, Int, Float, Int, String, String)
private type Anomaly2TupleType = (Int, Blob, Timestamp, Int, Int, String, Int, Int, String, Int, String, Int, Int, Int, Float, Int)
private type AnomalyRowTupleType = (Anomaly1TupleType, Anomaly2TupleType)
private val anomalyShapedValue = (
(id, serviceName, serviceId, timeUpdated, timestamp, anomalyCategoryId, userGroup, riskValue, activityTypeId, destinationHost, userName),
(tenantId, information, timeCreated, userId, anomalyType, anomalyValue, measure, userAction, uniqueIdentifier, similarCount, trainingValue, state, riskLevel, userRiskLevel, userRiskScore, response)
).shaped[AnomalyRowTupleType]
private val toAnomalyRow: (AnomalyRowTupleType => AnomalyRow) = { anomalyTuple =>
AnomalyRow(anomaly1 = Anomaly1.tupled.apply(anomalyTuple._1), anomaly2 = Anomaly2.tupled.apply(anomalyTuple._2))
}
private val toAnomalyTuple: (AnomalyRow => Option[AnomalyRowTupleType]) = { anomalyRow =>
Some(Anomaly1.unapply(anomalyRow.anomaly1).get, Anomaly2.unapply(anomalyRow.anomaly2).get)
}
def * = anomalyShapedValue <> (toAnomalyRow, toAnomalyTuple)
}
As cvogt already said, you can use nested case classes, they are much easier to work with, see here. I know that HLists are powerful but IMHO people are forcing them too much... What's wrong with case classes? xD
You should compose your AnomalyC class from 2 or more case classes containing <= 22 fields.
As slick does not allow for more than 22 columns and complains that the tupled and unapply methods are not found. The simplest solution is to go for nested case classes if possible. Example :
https://github.com/timgent/spray-slick-template/blob/master/src/main/scala/com/timmeh/openhr/openholidays/model/LargeTableExample1.scala
With HList approach we need to change only the default projection *.
From documentation https://scala-slick.org/doc/3.3.3/cookbook.html
Related
I am still fairly new to slick and very much learning.
I an trying to create a search functionality that would be quite simple with plain SQL. However, I am hitting some obstacles when I am trying to do the same with Slick.
Trying to go by example from here: http://slick.lightbend.com/doc/3.2.1/queries.html#sorting-and-filtering
i am starting to build a function as follows:
private def schoolSearchBaseQuery(drop: Long, take: Long) = {
(for {
schools <- Schools.schools.filter(_.deletedAt.isEmpty)
} yield schools).drop(drop).take(take)
}
def search(schoolSearchCriteria: SchoolSearchCriteria, drop: Long = 0, take: Long = 100): Future[Seq[School]] = {
val q = schoolSearchBaseQuery(drop, take) filter { school =>
List(
schoolSearchCriteria.name.map(n => school.name like s"%$n%")
)
}
db.run(q.result)
}
But this seem to be not right:
[error] /Users/ShurikAg/Dev/indago/indago-api/app/dao/SchoolDao.scala:97:47: inferred type arguments [List[Option[slick.lifted.Rep[Boolean]]]] do not conform to method filter's type parameter bounds [T <: slick.lifted.Rep[_]]
[error] val q = schoolSearchBaseQuery(drop, take) filter { school =>
[error] ^
[error] /Users/ShurikAg/Dev/indago/indago-api/app/dao/SchoolDao.scala:97:63: type mismatch;
[error] found : model.Schools => List[Option[slick.lifted.Rep[Boolean]]]
[error] required: model.Schools => T
[error] val q = schoolSearchBaseQuery(drop, take) filter { school =>
Also, IntelliJ complains about this:
I think I am misunderstanding something.
For a reference
School definition related code:
package model
import driver.PGDriver.api._
import org.joda.time.DateTime
import play.api.libs.json._
import slick.lifted.Tag
import format.DateTimeFormat._
import model.media.Medias
case class School(id: Option[Int] = None,
addressId: Option[Int] = None,
name: String,
about: Option[String] = None,
numberOfStudents: Option[Int] = None,
websiteUrl: Option[String] = None,
mediaId: Option[Int] = None,
slug: String,
shortDescription: Option[String] = None,
ready: Boolean,
classrooms: Option[Int] = None,
yearEstablished: Option[String] = None,
displayCopyright: Boolean,
createdAt: DateTime = DateTime.now,
updatedAt: DateTime = DateTime.now,
deletedAt: Option[DateTime] = None,
createdBy: Option[String] = None,
updatedBy: Option[String] = None,
dliNumber: Option[String] = None)
object Schools {
val schools = TableQuery[Schools]
implicit lazy val schoolFormat: Format[School] = Json.format[School]
Json.toJson[DateTime](DateTime.now)
}
class Schools(tag: Tag) extends Table[School](tag, "school") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def addressId = column[Option[Int]]("address_id")
def name = column[String]("name", O.SqlType("character varying(255)"))
def about = column[Option[String]]("about", O.SqlType("text"))
def numberOfStudents = column[Option[Int]]("number_of_students")
def websiteUrl = column[Option[String]]("website_url", O.SqlType("character varying(100)"))
def mediaId = column[Option[Int]]("media_id")
def slug = column[String]("slug", O.SqlType("character varying(255)"))
def shortDescription = column[Option[String]]("short_description", O.SqlType("character varying(255)"))
def ready = column[Boolean]("ready")
def classrooms = column[Option[Int]]("classrooms")
def yearEstablished = column[Option[String]]("year_established", O.SqlType("character varying(4)"))
def displayCopyright = column[Boolean]("display_copyright")
def createdAt = column[DateTime]("createdat")
def updatedAt = column[DateTime]("updatedat")
def deletedAt = column[Option[DateTime]]("deletedat")
def createdBy = column[Option[String]]("createdby", O.SqlType("character varying(255)"))
def updatedBy = column[Option[String]]("updatedby", O.SqlType("character varying(255)"))
def dliNumber = column[Option[String]]("dli_number", O.SqlType("character varying(50)"))
override def * =
(
id.?,
addressId,
name,
about,
numberOfStudents,
websiteUrl,
mediaId,
slug,
shortDescription,
ready,
classrooms,
yearEstablished,
displayCopyright,
createdAt,
updatedAt,
deletedAt,
createdBy,
updatedBy,
dliNumber
) <> (School.tupled, School.unapply)
def addressIdUniqueIdx = index("school_address_id_uidx", addressId, unique = true)
def application =
foreignKey("school_address_id_fkey", addressId, Addresses.addresses)(
_.id.?,
onUpdate = ForeignKeyAction.Cascade,
onDelete = ForeignKeyAction.Restrict
)
def mediaIdUniqueIdx = index("school_media_id_uidx", mediaId, unique = true)
def logo =
foreignKey("school_media_id_fkey", mediaId, Medias.medias)(
_.id.?,
onUpdate = ForeignKeyAction.Cascade,
onDelete = ForeignKeyAction.Restrict
)
def slugUniqueIdx = index("school_slug_uidx", slug, unique = true)
}
And SchooSearchCriteria:
case class SchoolSearchCriteria(name: Option[String])
The criteria eventually going to be more complex than just a single field. I and just trying to figure out the mechanism for now.
Is it even the right direction to create search queries like that, given that the base query will eventually include more than a single table or even a single join?
So, I guess, I should answer my own question as well here, since it looks like I figured out the issue.
Apparently what I was missing from the example here: http://slick.lightbend.com/doc/3.2.1/queries.html#sorting-and-filtering
is the importance of collect part.
So eventually the way I've got it working is this:
def search(schoolSearchCriteria: SchoolSearchCriteria, drop: Long = 0, take: Long = 100): Future[Seq[School]] = {
val q = schoolSearchBaseQuery(drop, take) filter { school =>
List(
schoolSearchCriteria.name.map(n => school.name like s"%${n.toLowerCase}%")
).collect({case Some(criteria) => criteria}).reduceLeftOption(_ || _).getOrElse(true: Rep[Boolean])
}
db.run(q.result)
}
However, I am not sure for a 100% how that works :)
I hope this can help someone
This code compiles:
def search(schoolSearchCriteria: SchoolSearchCriteria, drop: Long = 0, take: Long = 100): Future[Seq[School]] = {
val q = schoolSearchBaseQuery(drop, take) filter { school =>
val n = schoolSearchCriteria.name.get
school.name like s"%$n%"
}
db.run(q.result)
}
def search(schoolSearchCriteria: SchoolSearchCriteria, drop: Long = 0, take: Long = 100): Future[Seq[School]] =
schoolSearchCriteria.name.map { n =>
val q = schoolSearchBaseQuery(drop, take) filter { school =>
school.name like s"%$n%"
}
db.run(q.result)
}.getOrElse {
Future.failed(new Exception("no name"))
// Future.successful(Seq())
}
def search(schoolSearchCriteria: SchoolSearchCriteria, drop: Long = 0, take: Long = 100): Future[Seq[School]] = {
val q0 = schoolSearchBaseQuery(drop, take)
val q1 = schoolSearchCriteria.name.map { n =>
q0 filter { school =>
school.name like s"%$n%"
}
}.getOrElse(q0)
db.run(q1.result)
}
Having a table with the columns
class Data(tag: Tag) extends Table[DataRow](tag, "data") {
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name")
def state = column[State]("state")
def price = column[Int]("price")
def * = (id.?, name, state, price) <> ((DataRow.apply _).tupled, DataRow.unapply)
}
I'd like to write a function that would select a single row, and update the columns where the supplied values are not null.
def update(id: Int, name: Option[String], state: Option[State], price: Option[Int])
eg.
update(1, None, None, Some(5)) would update only the price of the data row 1, leaving the name and state intact
update(1, Some("foo"), None, Some(6)) would update the name and price, but leave its state intact.
I guess some smart mapping could be used, but I'm having a hard time expressing it, not sure how it could spit out different length tuples depending on the inputs (wether their value is defined), since they are more or less "unrelated" classes.
def update(id: Int, name: Option[String], state: Option[State], price: Option[Int]) = {
table.fiter(_.id == id). ???? .update(name, state, price)
}
I solved it in the following way.
The implementation below works only if it is a Product object.
Execute the update statement except for None for the Option type and null for the object type.
package slick.extensions
import slick.ast._
import slick.dbio.{ Effect, NoStream }
import slick.driver.JdbcDriver
import slick.jdbc._
import slick.lifted._
import slick.relational.{ CompiledMapping, ProductResultConverter, ResultConverter, TypeMappingResultConverter }
import slick.util.{ ProductWrapper, SQLBuilder }
import scala.language.{ existentials, higherKinds, implicitConversions }
trait PatchActionExtensionMethodsSupport { driver: JdbcDriver =>
trait PatchActionImplicits {
implicit def queryPatchActionExtensionMethods[U <: Product, C[_]](
q: Query[_, U, C]
): PatchActionExtensionMethodsImpl[U] =
createPatchActionExtensionMethods(updateCompiler.run(q.toNode).tree, ())
}
///////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////// Patch Actions
///////////////////////////////////////////////////////////////////////////////////////////////
type PatchActionExtensionMethods[T <: Product] = PatchActionExtensionMethodsImpl[T]
def createPatchActionExtensionMethods[T <: Product](tree: Node, param: Any): PatchActionExtensionMethods[T] =
new PatchActionExtensionMethodsImpl[T](tree, param)
class PatchActionExtensionMethodsImpl[T <: Product](tree: Node, param: Any) {
protected[this] val ResultSetMapping(_, CompiledStatement(_, sres: SQLBuilder.Result, _),
CompiledMapping(_converter, _)) = tree
protected[this] val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
protected[this] val TypeMappingResultConverter(childConverter, toBase, toMapped) = converter
protected[this] val ProductResultConverter(elementConverters # _ *) =
childConverter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
private[this] val updateQuerySplitRegExp = """(.*)(?<=set )((?:(?= where)|.)+)(.*)?""".r
private[this] val updateQuerySetterRegExp = """[^\s]+\s*=\s*\?""".r
/** An Action that updates the data selected by this query. */
def patch(value: T): DriverAction[Int, NoStream, Effect.Write] = {
val (seq, converters) = value.productIterator.zipWithIndex.toIndexedSeq
.zip(elementConverters)
.filter {
case ((Some(_), _), _) => true
case ((None, _), _) => false
case ((null, _), _) => false
case ((_, _), _) => true
}
.unzip
val (products, indexes) = seq.unzip
val newConverters = converters.zipWithIndex
.map(c => (c._1, c._2 + 1))
.map {
case (c: BaseResultConverter[_], idx) => new BaseResultConverter(c.ti, c.name, idx)
case (c: OptionResultConverter[_], idx) => new OptionResultConverter(c.ti, idx)
case (c: DefaultingResultConverter[_], idx) => new DefaultingResultConverter(c.ti, c.default, idx)
case (c: IsDefinedResultConverter[_], idx) => new IsDefinedResultConverter(c.ti, idx)
}
val productResultConverter =
ProductResultConverter(newConverters: _*).asInstanceOf[ResultConverter[JdbcResultConverterDomain, Any]]
val newConverter = TypeMappingResultConverter(productResultConverter, (p: Product) => p, (a: Any) => toMapped(a))
val newValue: Product = new ProductWrapper(products)
val newSql = sres.sql match {
case updateQuerySplitRegExp(prefix, setter, suffix) =>
val buffer = StringBuilder.newBuilder
buffer.append(prefix)
buffer.append(
updateQuerySetterRegExp
.findAllIn(setter)
.zipWithIndex
.filter(s => indexes.contains(s._2))
.map(_._1)
.mkString(", ")
)
buffer.append(suffix)
buffer.toString()
}
new SimpleJdbcDriverAction[Int]("patch", Vector(newSql)) {
def run(ctx: Backend#Context, sql: Vector[String]): Int =
ctx.session.withPreparedStatement(sql.head) { st =>
st.clearParameters
newConverter.set(newValue, st)
sres.setter(st, newConverter.width + 1, param)
st.executeUpdate
}
}
}
}
}
Example
// Model
case class User(
id: Option[Int] = None,
name: Option[String] = None,
username: Option[String] = None,
password: Option[String] = None
)
// Table
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def username = column[String]("username")
def password = column[String]("password")
override def * = (id.?, name.?, username.?, password.?) <>(User.tupled, User.unapply)
}
// TableQuery
object Users extends TableQuery(new Users(_))
// CustomDriver
trait CustomDriver extends PostgresDriver with PatchActionExtensionMethodsSupport {
override val api: API = new API {}
trait API extends super.API with PatchActionImplicits
}
// Insert
Users += User(Some(1), Some("Test"), Some("test"), Some("1234"))
// User patch
Users.filter(_.id === 1).patch(User(name = Some("Change Name"), username = Some("")))
https://gist.github.com/bad79s/1edf9ea83ba08c46add03815059acfca
Building on JonasAnso's answer, converting that to slick v3.0+, and putting it into a transaction:
def partialUpdate(id: Int, name: Option[String], login: Option[String]): Future[Int] = {
val selectQ = users.filter(_.id === id)
val query = selectQ.result.head.flatMap { data =>
selectQ.update(data.patch(name, login))
}
db.run(query)
}
As I commented the question is similar to an existing one, but you don't seem to have any extra requirements.
The simplest approach is just SELECT + UPDATE. For example you add a patch function in your DataRow class defining how you want to update your model
def patch(name: Option[String], state: Option[State], price: Option[Int]): Data {
this.copy(name = name.getOrElse(this.name), ...)
}
And you add a partialUpdate method in your repo class
class DataRepo {
private val Datas = TableQuery[Data]
val db = ???
def partialUpdate(id: Int, name: Option[String], state: Option[State], price: Option[Int]): Future[Int] = {
val query = Datas.filter(_.id === id)
for {
data <- db.run(query.result.head)
result <- db.run(query.update(data.patch(name, state, price)))
} yield result
}
}
As you see the main problem of this solution is that there are 2 SQL statements, SELECT and UPDATE.
Other solution is to use plain SQL (http://slick.typesafe.com/doc/3.0.0/sql.html) but of course this gives other problems.
i have the next problem..
im using spray.http.DateTime, and my mapper is:
implicit val JavaUtilDateTimeMapper =
MappedColumnType.base[DateTime, Timestamp] (
d => new Timestamp(d.clicks),
d => spray.http.DateTime(d.getTime))
case class EventosModelTable(
id: Option[Long],
idCliente: Option[Long],
idPlan: Option[Long] = None,
idServicio: Option[Long] = None,
tipo: TipoEventos.Value,
fechaInicio: DateTime,
fechaFin: Option[DateTime]
) extends BaseModel
class EventosTabla(tag: Tag) extends Table[EventosModelTable](tag, "eventos") with BaseTabla{
override def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def idCliente = column[Long]("id_cliente")
def idPlan = column[Option[Long]]("id_plan")
def idServicio = column[Option[Long]]("id_servicio")
def tipo = column[TipoEventos.Value]("tipo_evento")
def fechaInicio = column[DateTime]("fecha_inicio")
def fechaFin = column[Option[DateTime]]("fecha_fin")
def * = (id.?,idCliente.?,idPlan,idServicio,tipo,fechaInicio,fechaFin) <> (EventosModelTable.tupled, EventosModelTable.unapply _)
def cliente = foreignKey("cliente",idCliente,TableQuery[ClientesTabla])(_.id)
def plan = foreignKey("plan",idPlan,TableQuery[PlanesTabla])(_.id)
}
then.. i need find Event's where fechaFin are Null or fechaFin> DateTime.now...
but the problem is that fechaFin can be Null then is a Option value, and i cant compare with a DateTime.now.. the compile error is that Some[DateTime] dont have > method.
I will appreciate any help
the query is as follows..
def getAltasBajas(id : Long) : DBIOAction[Seq[EventosModelTable],NoStream,Effect.All] = {
val q = for {
altasBajas <- tabla.filter(_.idCliente === id)
if altasBajas.fechaFin.isEmpty || (altasBajas.fechaFin.isDefined && (altasBajas.fechaFin)<(DateTime.now))
}yield altasBajas
q.result
}
I found this answer that would indicate that your implementation should work (at least as far as I can see) or you could try
val q = for {
altasBajas <- tabla.filter(_.idCliente === id)
if !altasBajas.fechaFin.isNull || altasBajas.fechaFin < DateTime.now
} yield altasBajas
I'd like to insert a new record that contains a foreign key.
For example, inserting the new record to bars, and the new record has the id that is found by the query result of the table foos.
Here is a code example:
import scala.slick.driver.H2Driver.simple._
object Test {
case class FooRecord(id:Int, str: String)
class Foos(tag: Tag) extends Table[FooRecord](tag, "users") {
def id = column[Int]("ID", O.PrimaryKey)
def str = column[String]("EMAIL", O.NotNull)
def * = (id, str) <> (FooRecord.tupled, FooRecord.unapply _)
}
val foos = TableQuery[Foos]
case class BarRecord(app_id:Int, name: String)
class Bars(tag: Tag) extends Table[BarRecord](tag, "apps") {
def foo_id = column[Int]("FOO_ID")
def str = column[String]("STR", O.NotNull)
def * = (foo_id, str) <> (BarRecord.tupled, BarRecord.unapply _)
def foo_fk = foreignKey("FOO_FK", foo_id, foos)(_.id)
}
val bars = TableQuery[Bars]
def main(args: Array[String]): Unit = {
Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
implicit session =>
foos.filter(_.str === "ABC").map { f =>
// Insert a new record that contains foo's id as a foreign key to bars.
// bars.insert(BarRecord(f.id, "DEF"))
// [error] found : scala.slick.lifted.Column[Int]
// [error] required: Int
bars.insert(BarRecord(1, "DEF")) // OK
}
}
}
}
I got a compile error. The type of the foreign key is Column[Int], but the BarRecord id type is Int.
Is there any good way to get Int value? Or is there a more elegant way that can insert the value that is from another table's query result?
Your query returns the lifted query, not the Int value:
val fooIdColumn: lifted.Query[lifted.Column[Int], Int] =
foos.filter(_.str === "ABC").map(f => f.id)
You can use run and get back a Seq[Int] (since there could be more than one result):
val fooIds: Seq[Int] =
foos.filter(_.str === "ABC").map(x => x.id).run
Or firstOption and getOrElse to get back an Int:
val fooId: Int =
foos.filter(_.str === "ABC").map(x => x.id).firstOption.getOrElse(0)
Assume we have a table
class Entry(tag :Tag) extends Table[(String, Long)](tag, "entries") {
def name = column[String]("name")
def value = column[Long]("value")
def * = (name, value)
}
val Entries = new TableQuery(new Entry(_))
and a query of type Query[(Column[String], Column[Long]), (String, Long)]. Can I somehow convert it to Query[Entry, (String, Long)]? This would be very useful in case of grouping queries such as Entries.groupBy(_.name).map(g=>(g._1, g._2.map(_.value).avg))
try this :
case class Entry(name: String,value: Long)
class Entries(tag :Tag) extends Table[Entry](tag, "entries") {
def name = column[String]("name")
def value = column[Long]("value")
def * = (name, value) <>(Entry.tupled, Entry.unapply )
}
val Entries = TableQuery[Entries]