Constructor can not be instantiated Slick Scala - sql

I was trying to convert a query from SQL into Scala code with Slick, but I have got a compiler error in filter clause: constructor cannot be instantiated to expected type.
My code in Slick:
val subquery = (for {
pit <- PassInTripTable.table
t <- TripTable.table if pit.tripNoFk === t.tripNo
} yield (pit, t))
.map{ case (pit, t) => ( pit, Case.If(t.townFrom <= t.townTo).Then(t.townFrom ++ t.townTo).Else(t.townFrom ++ t.townTo) )}
.groupBy(_._1.idPsgFk)
.filter{ case ((pit, count), group) => ( group.map(_._2).countDistinct === 1)}
.map(_._1)
val query = PassengerTable.table.filter(_.idPsg in subquery).map(_.name)
db.run(query.result)
The query in SQL itself:
select name from passenger
where id_psg in
(
select id_psg from trip t,pass_in_trip pit
where t.trip_no=pit.trip_no
group by id_psg
having count(distinct case when town_from<=town_to then town_from+town_to else town_to+town_from end)=1
)
I would be very grateful if someone helped me to find an error.

From looking at your code, it looks like the type you are matching on is not supposed to be "((pit, count), group)".
groupBy in Slick only returns a collection of Tuple2s.
http://slick.lightbend.com/doc/3.0.0/queries.html
So, the filter might look something like...
.filter{ case (pit, count) => ( count.map(_._2).countDistinct === 1)}

The problem is that Slick .groupBy requires a .map call with aggregating functions afterwards. You can find detailed information here.
So, try this:
.groupBy(_._1.idPsgFk)
.map{ case (key, group) => (key, group.map(_._2).countDistinct)}
.filter{ case (_, count) => count === 1}
.map(_._1)
P.S.
I've also found "bad smells" in your code. You get pairs as a result of for-comrehension, but it looks like standard join would be more appropriate here (and more efficient), something like:
PassInTripTable.table.join(TripTable.table).on(_.tripNoFk === _.tripNo)
.map{ case (pit, t) => ...}
And why would you use such condition:
Case.If(t.townFrom <= t.townTo).Then(t.townFrom ++ t.townTo).Else(t.townFrom ++ t.townTo)? Its branches are the same, so equals to t.townFrom ++ t.townTo.

Related

How to get result using better algorithm?

We have the list like below
List:
(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
As the number in the string may be referred by different elements, the expected result is:
("herry","0,1,2,3,6"), ("herry","4"), ("John","5")
I worked out solution using scala, but it looks complicated, is there a more clean and easy way to work out the result? Thanks in advance!
Here is my solution in scala,
val foo=List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
println(GetValue)
def GetValue()={
foo.zipWithIndex.map((tuple: ((String, String), Int)) =>{
val tuples = getrelated(tuple._1, foo)
(tuple._2, tuples)
}).map((tuple: (Int, List[(String, String)])) => tuple._2)
.map((tuples: List[(String, String)]) => (tuples.head._1,tuples.map((tuple: (String, String)) => tuple._2)))
.map((tuple: (String, List[String])) => (tuple._1, tuple._2.mkString(",").split(",").distinct.sorted.mkString(",")))
.distinct
}
def getrelated(start:(String,String),fooList:List[(String,String)]):List[(String,String)]={
val fooListWithout = fooList.filter((tuple: (String, String)) => tuple != start)
val result=fooListWithout
.filter((tuple: (String, String)) => findmatching(tuple._2,start._2))
.flatMap((tuple: (String, String)) => start :: getrelated(tuple,fooListWithout))
if (result.isEmpty)
List(start)
else
result
}
def findmatching(key1:String,key2:String)={
(key1.split(",")++key2.split(","))
.groupBy(identity)
.mapValues((strings: Array[String]) => strings.size)
.exists((tuple: (String, Int)) => tuple._2>1)
}
Let me clarify the algorithm
if the number list has the overlapping number, then group these number as one element
if the number list has no overlapping number, then consider it as independent element
for example,
input: List(("herry","0,1,2"),("herry","1,3"),("herry","7,4"),("herry","4"),("John","5"))
expected output: List(("herry","0,1,2,3" ), ("herry","4,7"), ("John","5"))
input: List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
expected: List("herry","0,1,2,3,6"), ("herry","4"), ("John","5")
input: List(("herry","0,1"),("herry","2,3"),("herry","3,6"),("herry","4"),("John","5"))
expected: List("herry","0,1"),("herry","2,3,6"), ("herry","4"), ("John","5")
My assumption is that tuples for the same key that contain multiple values should be aggregated. It is unclear what should happen in the same value appears singly and as part of a list, e.g. ("herry", "4"), ("herry, "1,4") however it should be simple enough to remove any such cases
val data = List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))// Start writing your ScalaFiddle code here
val (singles, multiples) = data.partition{case (name, list) => !list.contains(",")}
val multiplesAggregated = multiples
.groupBy{case (key, _) => key)
.map{
case (key, values) =>
key -> values.flatMap{case (_, numbers) => numbers.split(",")}.distinct.sorted.mkString(",")}.toList
println(multiplesAggregated ++ singles)
Output:
List((herry,0,1,2,3,6), (herry,4), (John,5))

Entity Framework if statement inside select

I have a problem in generating an Entity Framework query and not okay with linq style one :.....
This is my attempt:
var query = db.table
.Where(x => x.colA == 1)
.GroupBy(x => new {x.min,x.max})
.Select(y => if(y.key.min==0 && y.key.max==0)
{ " unchanged " }
else
{ y.key.min.tostring()+" "+y.key.max.tostring() })
.ToList()
I want to get "unchanged" string, if both value ofmin and max are zero, otherwise concat them
Use the conditional operator
// ...
.Select(y=> y.key.min==0 && y.key.max==0
? " unchanged "
: y.key.min.tostring()+" "+y.key.max.tostring())
// ...
Apparently all elements in your table have properties min and max
After GroupBy(x=> new {x.min,x.max}) you'll have a sequence of groups, where each group has a key {min, max}, all elements in the group have this value for their min and max properties.
After the GroupBy, you take every group, and from every group you'll select exactly one string. You get rid of the element of the group.
The string that you select depends on the key of the group: if the key = {0, 0} you select the string "unchanged", else you select the string:
y.key.min.tostring()+" "+y.key.max.tostring()
The result is a list of strings, something like:
"3 7",
"4 10,
"unchanged",
"2 8",
Are you sure you want this?
In that case you won't need the GroupBy. Distinct will be simpler and faster
List<string> result = db.table
.Where(tableRow => tableRow.colA == 1)
.Select(tableRow => tableRow.min==0 && tableRow.max==0
? " unchanged "
: tableRow.min.tostring()+" "+tableRow.max.tostring())
// from here you have a sequence of strings
// get rid of duplicates:
.Distinct()
.ToList();
For this specific case, you can use Conditional Operator (?:)
var query = db.table
.Where(x=> x.colA == 1)
.GroupBy(x=> new {x.min,x.max})
.Select(y=> (y.Key.min == 0 && y.Key.max == 0) ? " unchanged" : (y.Key.min.ToString()+" "+y.Key.max.ToString()))
.ToList();
Sorry I can't try it right now, but i think this should work
var query = db.table
.Where(x=> x.colA == 1)
.GroupBy(x=> new {x.min,x.max})
.Select(y=> {if(y.key.min==0 && y.key.max==0)
{
" unchanged "
} else
{
y.key.min.tostring()+" "+y.key.max.tostring();
} return y;})
.ToList()

Subqueries, Having and GroupBy in Slick

I am currently learning Slick. I was trying to translate this query from SQL into Scala:
SELECT name FROM Passenger
WHERE ID_psg in
(SELECT ID_psg FROM Pass_in_trip
GROUP BY place, ID_psg
HAVING count(*)>1)
But I have only managed to write something like this, and it gives compile errors:
PassengerTable.table.filter(_.idPsg in (PassInTripTable.table.map(_.idPsgFk)))
.filter(PassengerTable.table.count(_.name) > 1)
.map(_.name)
I do not really know how to apply count and having on queries in Slick.
So I would be really grateful for some help.
Try
val subquery = PassInTripTable.table.groupBy(p => (p.place, p.idPsgFk))
.map { case ((place, id), group) => (id, group.length) }
.filter { case (id, count) => count > 1 }
.map { case (id, count) => id }
val query = PassengerTable.table.filter(_.idPsg in subquery).map(_.name)
val action = query.result
db.run(action)
http://slick.lightbend.com/doc/3.0.0/sql-to-slick.html#groupby
http://slick.lightbend.com/doc/3.0.0/sql-to-slick.html#subquery
http://slick.lightbend.com/doc/3.0.0/sql-to-slick.html#having

Conditional sum in slick (scala)

I'm trying to rewrite following SQL into slick:
SELECT id
SUM(
if (spend > 0, 1, 0)
)
FROM items
GROUP by id
My current code looks similar to this:
items.groupBy(r => r.id).map {
case (id, group) => (id, group.map { r => if (r.spend > 0) 1 else 0 }.sum)
}
But I got following error:
polymorphic expression cannot be instantiated to expected type;
found : [R]slick.lifted.Rep[R]
required: Boolean
I also tried to use filter and length, but with no success. How can I achieve my goal?
Slick already provides solution for this (documentation):
items.groupBy(r => r.id).map {
case (id, group) => (id, group.map { r =>
Case If r.spend > BigDecimal(0.0) Then 1 Else 0
}.sum)
}
Scala ternary expression could not be transferred to slick syntax.
Simplest approach is just to simplify the query here to
SELECT COUNT(id)
FROM items
WHERE spend > 0
GROUP by id
Corresponding slick will be
items.filter(_.spend > 0).groupBy(_.id) map {
case (id, group) => (id, group.size)
}
Or you could try to get access to the if function using SimpleFunction:
def ifFun[T] = SimpleFunction.ternary[Boolean, T, T, T]("if")
items.groupBy(_.id) map {
case (id, group) => (id, group.map(r => ifFun(r.spend > 0, 1, 0)).sum)
}

Slick query for one to optional one (zero or one) relationship

Given tables of:
case class Person(id: Int, name: String)
case class Dead(personId: Int)
and populated with:
Person(1, "George")
Person(2, "Barack")
Dead(1)
is it possible to have a single query that would produce a list of (Person, Option[Dead]) like so?
(Person(1, "George"), Some(Dead(1)))
(Person(2, "Barack"), None)
For slick 3.0 it should be something like this:
val query = for {
(p, d) <- persons joinLeft deads on (_.id === _.personId)
} yield (p, d)
val results: Future[Seq[(Person, Option[Dead])]] = db.run(query.result)
In slick, outer joins are automatically wrapped in an Option type. You can read more about joining here: http://slick.typesafe.com/doc/3.0.0/queries.html#joining-and-zipping