Efficient Data Structure for TimeZone.knownTimeZoneIdentifiers? - arrays

I have an array of Strings returned from TimeZone.knownTimeZoneIdentifiers that I would like to parse into a nested dictionary (or another data structure).
The format of the [String] is
["Africa/Abidjan", "Africa/Accra",..,"America/Araguaina","America/Argentina/Buenos_Aires", ...,"Pacific/Marquesas", "Pacific/Midway", "Pacific/Nauru"]
The end result would be something like
["Africa":["Abidjan":[], "Accra":[]], "America": ["Araguaina": [], "Argentina": ["Buenos_Aires"]], "Pacific": ["Marquesas":[], "Midway":[], "Nauru":[]]]
I will need to query all of the keys at each level of the dictionary.
There must be another way to do this functionally or with recursion, rather than split(seperator: "/") and several for loops to build the data structure manually.

Don't use a dictionary for this. Make a custom type. In particular, you are describing a tree. Let's make one (largely based on https://github.com/raywenderlich/swift-algorithm-club/tree/master/Tree, and if there is more that you need to do, modify the Node class itself to meet your needs):
class Node<T> {
var value: T
weak var parent: Node?
var children = [Node<T>]()
init(_ value: T) {
self.value = value
}
func add(_ node: Node<T>) {
children.append(node)
node.parent = self
}
}
Now it's trivial:
let ids = TimeZone.knownTimeZoneIdentifiers
let root = Node("")
for id in ids {
var node = root
let splits = id.split(separator: "/").map(String.init)
for split in splits {
if let child = node.children.first(where:{$0.value == split}) {
node = child
} else {
let newnode = Node(split)
node.add(newnode)
node = newnode
}
}
}
Okay, let's see what we've got. It will be useful to have a nice way of printing a node:
extension Node: CustomStringConvertible {
private func display(level:Int) -> String {
let offset = String(repeating: " ", count: level * 4)
var s = offset + String(describing: value)
if children.isEmpty { return s }
s += " {\n"
s += children.map { $0.display(level:level+1) }.joined(separator: ",\n")
s += "\n\(offset)}"
return s
}
var description: String { return display(level:0) }
}
Now:
root.children.forEach {print($0)}
Result:
Africa {
Abidjan,
Accra,
Addis_Ababa,
Algiers,
Asmara,
Bamako,
Bangui,
Banjul,
Bissau,
Blantyre,
Brazzaville,
Bujumbura,
Cairo,
Casablanca,
Ceuta,
Conakry,
Dakar,
Dar_es_Salaam,
Djibouti,
Douala,
El_Aaiun,
Freetown,
Gaborone,
Harare,
Johannesburg,
Juba,
Kampala,
Khartoum,
Kigali,
Kinshasa,
Lagos,
Libreville,
Lome,
Luanda,
Lubumbashi,
Lusaka,
Malabo,
Maputo,
Maseru,
Mbabane,
Mogadishu,
Monrovia,
Nairobi,
Ndjamena,
Niamey,
Nouakchott,
Ouagadougou,
Porto-Novo,
Sao_Tome,
Tripoli,
Tunis,
Windhoek
}
America {
Adak,
Anchorage,
Anguilla,
Antigua,
Araguaina,
Argentina {
Buenos_Aires,
Catamarca,
Cordoba,
Jujuy,
La_Rioja,
Mendoza,
Rio_Gallegos,
Salta,
San_Juan,
San_Luis,
Tucuman,
Ushuaia
},
Aruba,
Asuncion,
Atikokan,
Bahia,
Bahia_Banderas,
Barbados,
Belem,
Belize,
Blanc-Sablon,
Boa_Vista,
Bogota,
Boise,
Cambridge_Bay,
Campo_Grande,
Cancun,
Caracas,
Cayenne,
Cayman,
Chicago,
Chihuahua,
Costa_Rica,
Creston,
Cuiaba,
Curacao,
Danmarkshavn,
Dawson,
Dawson_Creek,
Denver,
Detroit,
Dominica,
Edmonton,
Eirunepe,
El_Salvador,
Fort_Nelson,
Fortaleza,
Glace_Bay,
Godthab,
Goose_Bay,
Grand_Turk,
Grenada,
Guadeloupe,
Guatemala,
Guayaquil,
Guyana,
Halifax,
Havana,
Hermosillo,
Indiana {
Indianapolis,
Knox,
Marengo,
Petersburg,
Tell_City,
Vevay,
Vincennes,
Winamac
},
Inuvik,
Iqaluit,
Jamaica,
Juneau,
Kentucky {
Louisville,
Monticello
},
Kralendijk,
La_Paz,
Lima,
Los_Angeles,
Lower_Princes,
Maceio,
Managua,
Manaus,
Marigot,
Martinique,
Matamoros,
Mazatlan,
Menominee,
Merida,
Metlakatla,
Mexico_City,
Miquelon,
Moncton,
Monterrey,
Montevideo,
Montreal,
Montserrat,
Nassau,
New_York,
Nipigon,
Nome,
Noronha,
North_Dakota {
Beulah,
Center,
New_Salem
},
Nuuk,
Ojinaga,
Panama,
Pangnirtung,
Paramaribo,
Phoenix,
Port-au-Prince,
Port_of_Spain,
Porto_Velho,
Puerto_Rico,
Punta_Arenas,
Rainy_River,
Rankin_Inlet,
Recife,
Regina,
Resolute,
Rio_Branco,
Santa_Isabel,
Santarem,
Santiago,
Santo_Domingo,
Sao_Paulo,
Scoresbysund,
Shiprock,
Sitka,
St_Barthelemy,
St_Johns,
St_Kitts,
St_Lucia,
St_Thomas,
St_Vincent,
Swift_Current,
Tegucigalpa,
Thule,
Thunder_Bay,
Tijuana,
Toronto,
Tortola,
Vancouver,
Whitehorse,
Winnipeg,
Yakutat,
Yellowknife
}
Antarctica {
Casey,
Davis,
DumontDUrville,
Macquarie,
Mawson,
McMurdo,
Palmer,
Rothera,
South_Pole,
Syowa,
Troll,
Vostok
}
Arctic {
Longyearbyen
}
Asia {
Aden,
Almaty,
Amman,
Anadyr,
Aqtau,
Aqtobe,
Ashgabat,
Atyrau,
Baghdad,
Bahrain,
Baku,
Bangkok,
Barnaul,
Beirut,
Bishkek,
Brunei,
Calcutta,
Chita,
Choibalsan,
Chongqing,
Colombo,
Damascus,
Dhaka,
Dili,
Dubai,
Dushanbe,
Famagusta,
Gaza,
Harbin,
Hebron,
Ho_Chi_Minh,
Hong_Kong,
Hovd,
Irkutsk,
Jakarta,
Jayapura,
Jerusalem,
Kabul,
Kamchatka,
Karachi,
Kashgar,
Kathmandu,
Katmandu,
Khandyga,
Krasnoyarsk,
Kuala_Lumpur,
Kuching,
Kuwait,
Macau,
Magadan,
Makassar,
Manila,
Muscat,
Nicosia,
Novokuznetsk,
Novosibirsk,
Omsk,
Oral,
Phnom_Penh,
Pontianak,
Pyongyang,
Qatar,
Qostanay,
Qyzylorda,
Rangoon,
Riyadh,
Sakhalin,
Samarkand,
Seoul,
Shanghai,
Singapore,
Srednekolymsk,
Taipei,
Tashkent,
Tbilisi,
Tehran,
Thimphu,
Tokyo,
Tomsk,
Ulaanbaatar,
Urumqi,
Ust-Nera,
Vientiane,
Vladivostok,
Yakutsk,
Yangon,
Yekaterinburg,
Yerevan
}
Atlantic {
Azores,
Bermuda,
Canary,
Cape_Verde,
Faroe,
Madeira,
Reykjavik,
South_Georgia,
St_Helena,
Stanley
}
Australia {
Adelaide,
Brisbane,
Broken_Hill,
Currie,
Darwin,
Eucla,
Hobart,
Lindeman,
Lord_Howe,
Melbourne,
Perth,
Sydney
}
Europe {
Amsterdam,
Andorra,
Astrakhan,
Athens,
Belgrade,
Berlin,
Bratislava,
Brussels,
Bucharest,
Budapest,
Busingen,
Chisinau,
Copenhagen,
Dublin,
Gibraltar,
Guernsey,
Helsinki,
Isle_of_Man,
Istanbul,
Jersey,
Kaliningrad,
Kiev,
Kirov,
Lisbon,
Ljubljana,
London,
Luxembourg,
Madrid,
Malta,
Mariehamn,
Minsk,
Monaco,
Moscow,
Oslo,
Paris,
Podgorica,
Prague,
Riga,
Rome,
Samara,
San_Marino,
Sarajevo,
Saratov,
Simferopol,
Skopje,
Sofia,
Stockholm,
Tallinn,
Tirane,
Ulyanovsk,
Uzhgorod,
Vaduz,
Vatican,
Vienna,
Vilnius,
Volgograd,
Warsaw,
Zagreb,
Zaporozhye,
Zurich
}
GMT
Indian {
Antananarivo,
Chagos,
Christmas,
Cocos,
Comoro,
Kerguelen,
Mahe,
Maldives,
Mauritius,
Mayotte,
Reunion
}
Pacific {
Apia,
Auckland,
Bougainville,
Chatham,
Chuuk,
Easter,
Efate,
Enderbury,
Fakaofo,
Fiji,
Funafuti,
Galapagos,
Gambier,
Guadalcanal,
Guam,
Honolulu,
Johnston,
Kiritimati,
Kosrae,
Kwajalein,
Majuro,
Marquesas,
Midway,
Nauru,
Niue,
Norfolk,
Noumea,
Pago_Pago,
Palau,
Pitcairn,
Pohnpei,
Ponape,
Port_Moresby,
Rarotonga,
Saipan,
Tahiti,
Tarawa,
Tongatapu,
Truk,
Wake,
Wallis
}

You can use reduce(into:) and populate your dictionary using Key-based subscript default value:
let dictionary: [String: [String]] = TimeZone.knownTimeZoneIdentifiers.reduce(into: [:]) {
if let index = $1.firstIndex(of: "/") {
$0[.init($1[..<index]), default: []].append(.init($1[$1.index(after: index)...]))
}
}
Then you can map your dictionary values. If you find the slash append to the array otherwise set an empty collection to the key.
let result = dictionary.mapValues { string -> [String: [String]] in
string.reduce(into: [:]) {
if let index = $1.firstIndex(of: "/") {
$0[.init($1[..<index]), default: []].append(.init($1[$1.index(after: index)...]))
} else {
$0[$1] = []
}
}
}
If you would like to do that in a single pass:
let result: [String: [String:[String]]] = TimeZone.knownTimeZoneIdentifiers.reduce(into: [:]) {
if let index = $1.firstIndex(of: "/") {
let key = String($1[..<index])
let value = String($1[$1.index(after: index)...])
if let index = value.firstIndex(of: "/") {
let country = String(value[..<index])
let city = String(value[value.index(after: index)...])
$0[key, default: [:]][country, default: []].append(city)
} else {
$0[key, default: [:]][value] = []
}
}
}
result.forEach({print("key:",$0.key, "values:", $0.value)})

Related

Trying to display name of users on support action bar

Please i am new to kotlin and i have been trying to retrive the fullname of my users in the data base after using getStringExtra so i can use each of them for the title of my support action bar but the value it returns an empty value.
val name = intent.getStringExtra("fullname")
val receiverUid = intent.getStringExtra("uid")
val senderUid = FirebaseAuth.getInstance().currentUser?.uid
mDbRef = FirebaseDatabase.getInstance().getReference()
senderRoom = receiverUid + senderUid
receiverRoom = senderUid + receiverUid
supportActionBar?.title = name
Here is how i am sending name and receiverUid to my database
sendButton.setOnClickListener{
val message = messageBox.text.toString()
val messageObject = Message(message, senderUid)
val name = name.toString()
val receiverUid = receiverUid.toString()
val chatlistObject = ModelChatList(name, message, receiverUid)
mDbRef.child("chats").child(senderRoom!!).child("messages").push()
.setValue(messageObject).addOnSuccessListener {
mDbRef.child("chats").child(receiverRoom!!).child("messages").push()
.setValue(messageObject).addOnSuccessListener {
mDbRef.child("Chatlist").push().setValue(chatlistObject)
}
}
messageBox.setText("")
But when i try to retrive my ModelChatlist object it returns only two values leaving out the receivername as shown in the ModelChatlist below
class ModelChatList {
private var receivername:String = ""
private var lastmessage:String = ""
private var uid:String = ""
constructor(){}
constructor(receivername: String, lastmessage:String, uid:String){
this.receivername = receivername
this.lastmessage = lastmessage
this.uid =uid
}
fun getReceivername(): String{
return receivername
}
fun setReceivername(receivername: String){
this.receivername= receivername
}
fun getUid(): String{
return uid
}
fun setUid(uid: String){
this.uid= uid
}
fun getLastmessage(): String{
return lastmessage
}
fun setLastmessage(lastmessage: String){
this.lastmessage= lastmessage
}
}
In case you are wondering, "fullname" is in my database as the name the users enter when they sign up. Thank You

kotlin Firebase Variable path

Quick question.
While I write away my data to my firebase database I'd like to order it.
since i'm making an Album collection list I'd love to have it like the picture underneath.. but with bands and albums tho.
the only problem is that I can't read my write string.. in this case $Uname
I can only read the data correctly if I hardcode the location in my .getReference()
my data upload code:
//values
val number = Number_tv.text.toString()
val Uname = Username_TV.text.toString()
val ref = FirebaseDatabase.getInstance().getReference("/userdata/$Uname/$number _key")
val user = User(Username_TV.text.toString(), Email_TV.text.toString(), albumimageURL)
//code
if (number.isEmpty()){
Toast.makeText(this, "Number is empty", Toast.LENGTH_SHORT).show()
}
else{
ref.setValue(user)
.addOnSuccessListener {
Log.d("mainacti", "upload succesful")
}
.addOnFailureListener {
Log.d("mainacti", "I do not work")
}}
}
my data download code:
val ref = FirebaseDatabase.getInstance().getReference("/userdata/Heinz")
ref.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
val adapter = GroupAdapter<GroupieViewHolder>()
p0.children.forEach {
Log.d("Albumlist", it.toString())
val user = it.getValue(User::class.java)
if (user != null){
adapter.add(UserItem(user))
}
}
recyclerview_album.adapter = adapter
}
override fun onCancelled(error: DatabaseError) {
}
})
}
Both codes are in different classes
here's a Log for the reference ("/userdata")
2021-04-16 13:00:42.080 6914-6914/com.example.firetrier D/Albumlist: DataSnapshot { key = Hans, value = {1 _key={albumimageURL=https://firebasestorage.googleapis.com/v0/b/fireupload-4be26.appspot.com/o/albumcover%2Fcb06a4c4-8795-4103-99ef-c2bd55498b2e?alt=media&token=7d3d305e-c266-41fb-b466-cab548fbdd35, email=hans#test.com, username=Hans}} }
2021-04-16 13:00:42.088 6914-6914/com.example.firetrier D/Albumlist: DataSnapshot { key = Heinz, value = {1 _key={albumimageURL=https://firebasestorage.googleapis.com/v0/b/fireupload-4be26.appspot.com/o/albumcover%2Ff00f6651-9ccf-4533-8e1c-af103a1a17e6?alt=media&token=b6e4ad2f-cb4f-47d5-8f00-02bdc4765c00, email=Heinz#doof.com, username=Heinz}, 5 _key={albumimageURL=https://firebasestorage.googleapis.com/v0/b/fireupload-4be26.appspot.com/o/albumcover%2Fc9bb80aa-15a2-48cb-b6f5-6b7a59b11d8d?alt=media&token=facd516d-c3e3-4b61-aa09-23ea9062ccd3, email=hans#test.com, username=Heinz}} }
is there a way to use the value instead of the key?
if I adapt the reference to one of the usernames this is the log
2021-04-16 13:05:54.028 7629-7629/com.example.firetrier D/Albumlist: DataSnapshot { key = 1 _key, value = {albumimageURL=https://firebasestorage.googleapis.com/v0/b/fireupload-4be26.appspot.com/o/albumcover%2Ff00f6651-9ccf-4533-8e1c-af103a1a17e6?alt=media&token=b6e4ad2f-cb4f-47d5-8f00-02bdc4765c00, email=Heinz#doof.com, username=Heinz} }
2021-04-16 13:05:54.040 7629-7629/com.example.firetrier D/Albumlist: DataSnapshot { key = 5 _key, value = {albumimageURL=https://firebasestorage.googleapis.com/v0/b/fireupload-4be26.appspot.com/o/albumcover%2Fc9bb80aa-15a2-48cb-b6f5-6b7a59b11d8d?alt=media&token=facd516d-c3e3-4b61-aa09-23ea9062ccd3, email=hans#test.com, username=Heinz} }

terraform database creation in athena

I am trying to create a database using terraform and this seems very complicated for a poor query...
Could you help me, please?
I have tried null_resource with local-exec and data "external" Python...
I think I am looking the wrong way
ex which doesn't works in terraform 0.12
resource "null_resource" "create-endpoint" {
provisioner "local-exec" {
query = <<EOF
{
CREATE EXTERNAL TABLE `dashboard_loading_time`(
`timestamp_iso` string,
`app_identification` struct<service:string,app_name:string,app_type:string,stage:string>,
`user` struct<api_gateway_key:struct<id:string,name:string>,mashery_key:struct<id:string,name:string>,employee:struct<id:string,name:string>>,
`action` struct<action_type:string,path:string>,
`result` struct<status:string,http_status:string,response:struct<response:string>>)
PARTITIONED BY (
`year` int)
ROW FORMAT SERDE
'org.openx.data.jsonserde.JsonSerDe'
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/dev'
}
EOF
command = "aws athena start-query-execution --query-string "query""
}
}
I would like to find the simplest way to do this using terraform.
If you wanna make it for athena, need to make glue resources.
try below code with terraform.
variable "service_name" {
default = "demo-service"
}
variable "workspace" {
default = "dev"
}
variable "columns" {
default = {
id = "int"
type = "string"
status = "int"
created_at = "timestamp"
}
}
resource "aws_glue_catalog_database" "athena" {
name = "${var.service_name}_db"
}
resource "aws_glue_catalog_table" "athena" {
name = "${var.service_name}_logs"
database_name = "${aws_glue_catalog_database.athena.name}"
table_type = "EXTERNAL_TABLE"
parameters = {
EXTERNAL = "TRUE"
}
storage_descriptor {
location = "s3://${var.service_name}-${var.workspace}-data-pipeline/log/"
input_format = "org.apache.hadoop.mapred.TextInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat"
ser_de_info {
name = "jsonserde"
serialization_library = "org.openx.data.jsonserde.JsonSerDe"
parameters = {
"serialization.format" = "1"
}
}
dynamic "columns" {
for_each = "${var.columns}"
content {
name = "${columns.key}"
type = "${columns.value}"
}
}
}
partition_keys {
name = "year"
type = "string"
}
partition_keys {
name = "month"
type = "string"
}
partition_keys {
name = "day"
type = "string"
}
partition_keys {
name = "hour"
type = "string"
}
}
refer to this repository : aws-serverless-data-pipeline-by-terraform
resource "aws_glue_catalog_table" "aws_glue_catalog_table" {
name = "mytable"
database_name = aws_glue_catalog_database.aws_glue_catalog_database.name
table_type = "EXTERNAL_TABLE"
parameters = {
"classification" = "json"
}
storage_descriptor {
location = "s3://mybucket/myprefix"
input_format = "org.apache.hadoop.mapred.TextInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"
ser_de_info {
name = "myserdeinfo"
serialization_library = "org.openx.data.jsonserde.JsonSerDe"
parameters = {
"paths" = "jsonrootname"
}
}
columns {
name = "column1"
type = "array<struct<resourcearn:string,tags:array<struct<key:string,value:string>>>>"
}
}
partition_keys {
name = "part1"
type = "string"
}
partition_keys {
name = "part2"
type = "string"
}
}

How to sort array of custom objects by customised status value in swift 3?

lets say we have a custom class named orderFile and this class contains three properties.
class orderFile {
var name = String()
var id = Int()
var status = String()
}
a lot of them stored into an array
var aOrders : Array = []
var aOrder = orderFile()
aOrder.name = "Order 1"
aOrder.id = 101
aOrder.status = "closed"
aOrders.append(aOrder)
var aOrder = orderFile()
aOrder.name = "Order 2"
aOrder.id = 101
aOrder.status = "open"
aOrders.append(aOrder)
var aOrder = orderFile()
aOrder.name = "Order 2"
aOrder.id = 101
aOrder.status = "cancelled"
aOrders.append(aOrder)
var aOrder = orderFile()
aOrder.name = "Order 2"
aOrder.id = 101
aOrder.status = "confirmed"
aOrders.append(aOrder)
Question is: How will I sort them based on status according to open, confirm, close and cancelled?
You have to provide a value that will yield the appropriate ordering when compared in the sort function.
For example:
extension orderFile
{
var statusSortOrder: Int
{ return ["open","confirmed","closed","cancelled"].index(of: status) ?? 0 }
}
let sortedOrders = aOrders.sorted{$0.statusSortOrder < $1. statusSortOrder}
In your code you should make an array to store each aOrder with aOrders.append(aOrder) at each aOrder defination.
Then sort it with below code, refor this for more.
aOrders.sorted({ $0.status > $1.status })
The answer for swift 3 is as following
Ascending:
aOrders = aOrders.sorted(by:
{(first: orderFile, second: orderFile) -> Bool in
first.status > second.status
}
)
Descending:
aOrders = aOrders.sorted(by:
{(first: orderFile, second: orderFile) -> Bool in
first.status < second.status
}
)

Slick 3 join query one to many relationship

Imagine the following relation
One book consists of many chapters, a chapter belongs to exactly one book. Classical one to many relation.
I modeled it as this:
case class Book(id: Option[Long] = None, order: Long, val title: String)
class Books(tag: Tag) extends Table[Book](tag, "books")
{
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def order = column[Long]("order")
def title = column[String]("title")
def * = (id, order, title) <> (Book.tupled, Book.unapply)
def uniqueOrder = index("order", order, unique = true)
def chapters: Query[Chapters, Chapter, Seq] = Chapters.all.filter(_.bookID === id)
}
object Books
{
lazy val all = TableQuery[Books]
val findById = Compiled {id: Rep[Long] => all.filter(_.id === id)}
def add(order: Long, title: String) = all += new Book(None, order, title)
def delete(id: Long) = all.filter(_.id === id).delete
// def withChapters(q: Query[Books, Book, Seq]) = q.join(Chapters.all).on(_.id === _.bookID)
val withChapters = for
{
(Books, Chapters) <- all join Chapters.all on (_.id === _.bookID)
} yield(Books, Chapters)
}
case class Chapter(id: Option[Long] = None, bookID: Long, order: Long, val title: String)
class Chapters(tag: Tag) extends Table[Chapter](tag, "chapters")
{
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def bookID = column[Long]("book_id")
def order = column[Long]("order")
def title = column[String]("title")
def * = (id, bookID, order, title) <> (Chapter.tupled, Chapter.unapply)
def uniqueOrder = index("order", order, unique = true)
def bookFK = foreignKey("book_fk", bookID, Books.all)(_.id.get, onUpdate = ForeignKeyAction.Cascade, onDelete = ForeignKeyAction.Restrict)
}
object Chapters
{
lazy val all = TableQuery[Chapters]
val findById = Compiled {id: Rep[Long] => all.filter(_.id === id)}
def add(bookId: Long, order: Long, title: String) = all += new Chapter(None, bookId, order, title)
def delete(id: Long) = all.filter(_.id === id).delete
}
Now what I want to do:
I want to query all or a specific book (by id) with all their chapters
Translated to plain SQL, something like:
SELECT * FROM books b JOIN chapters c ON books.id == c.book_id WHERE books.id = 10
but in Slick I can't really get this whole thing to work.
What I tried:
object Books
{
//...
def withChapters(q: Query[Books, Book, Seq]) = q.join(Chapters.all).on(_.id === _.bookID)
}
as well as:
object Books
{
//...
val withChapters = for
{
(Books, Chapters) <- all join Chapters.all on (_.id === _.bookID)
} yield(Books, Chapters)
}
but to no avail. (I use ScalaTest and I get an empty result (for def withChapters(...)) or another exception for the val withChapters = for...)
How to go on about this? I tried to keep to the documentation, but I'm doing something wrong obviously.
Also: Is there an easy way to see the actual query as a String? I only found query.selectStatement and the like, but that's not available for my joined query. Would be great for debugging to see if the actual query was wrong.
edit: My test looks like this:
class BookWithChapters extends FlatSpec with Matchers with ScalaFutures with BeforeAndAfter
{
val db = Database.forConfig("db.test.h2")
private val books = Books.all
private val chapters = Chapters.all
before { db.run(setup) }
after {db.run(tearDown)}
val setup = DBIO.seq(
(books.schema).create,
(chapters.schema).create
)
val tearDown = DBIO.seq(
(books.schema).drop,
(chapters.schema).drop
)
"Books" should "consist of chapters" in
{
db.run(
DBIO.seq
(
Books.add(0, "Book #1"),
Chapters.add(0, 0, "Chapter #1")
)
)
//whenReady(db.run(Books.withChapters(books).result)) {
whenReady(db.run(Books.withChapters(1).result)) {
result => {
// result should have length 1
print(result(0)._1)
}
}
}
}
like this I get an IndexOutOfBoundsException.
I used this as my method:
object Books
{
def withChapters(id: Long) = Books.all.filter(_.id === id) join Chapters.all on (_.id === _.bookID)
}
also:
logback.xml looks like this:
<configuration>
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG/>
</configuration>
Where can I see the logs? Or what else do I have to do to see them?
To translate your query...
SELECT * FROM books b JOIN chapters c ON books.id == c.book_id WHERE books.id = 10
...to Slick we can filter the books:
val bookTenChapters =
Books.all.filter(_.id === 10L) join Chapters.all on (_.id === _.bookID)
This will give you a query that returns Seq[(Books, Chapters)]. If you want to select different books, you can use a different filter expression.
Alternatively, you may prefer to filter on the join:
val everything =
Books.all join Chapters.all on (_.id === _.bookID)
val bookTenChapters =
everything.filter { case (book, chapter) => book.id === 10L }
That will probably be closer to your join. Check the SQL generated with the database you use to see which you prefer.
You can log the query by creating a src/main/resources/logback.xml file and set:
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG"/>
I have an example project with logging set up. You will need to change INFO to DEBUG in the xml file in, e.g., the chapter-01 folder.

Resources