2015-03-21 17 views
7

Slick 3.0 kullanarak çoktan çoğa koleksiyon oluşturdum, ancak istediğim şekilde veri almak için mücadele ediyorum.Slick 3.0 yinelenen birleştirme olarak birleştirme ile çok çok sayıda sorgu

Olaylar ve İlgi Alanları arasında çoktan çoğa bir ilişki var. İşte benim tablolar şunlardır:

case class EventInterestDao(event: Int, interest: Int) 


class EventsInterestsTable(tag: Tag) 
    extends Table[EventInterestDao](tag, "events_interests") { 

    def eventId = column[Int]("event_id") 
    def interestId = column[Int]("interest_id") 

    def * = (
    eventId, 
    interestId) <> (EventInterestDao.tupled, EventInterestDao.unapply) 

    def eventFk = foreignKey("event_fk", eventId, EventQueries.query)(e => e.id) 
    def interestFk = foreignKey("interest_fk", interestId, InterestQueries.query)(i => i.id) 
} 


object EventInterestQueries { 
    lazy val query = TableQuery[EventsInterestsTable] 
} 

Ve nihayet İlgi:

case class InterestDao(name: String, 
         id: Option[Int] = None) 

class InterestsTable(tag: Tag) 
    extends Table[InterestDao](tag, "interests") { 

    def id = column[Int]("interest_id", O.PrimaryKey, O.AutoInc) 
    def name = column[String]("name") 
    def name_idx = index("idx_name", name, unique = true) 

    def * = (
    name, 
    id.?) <> (InterestDao.tupled, InterestDao.unapply) 

    def events = EventInterestQueries.query.filter(_.interestId === id) 
    .flatMap(_.eventFk) 
} 


object InterestQueries { 

    lazy val query = TableQuery[InterestsTable] 

    val findById = Compiled { k: Rep[Int] => 
    query.filter(_.id === k) 
    } 
} 

Ben sorgulamak ve aşağıdaki ait dizilerini (event.name, ilgi) alabilirsiniz

case class EventDao(title: String, 
        id: Option[Int] = None) 


class EventsTable(tag: Tag) 
    extends Table[EventDao](tag, "events") { 

    def id = column[Int]("event_id", O.PrimaryKey, O.AutoInc) 
    def title = column[String]("title") 

    def * = (
    title, 
    id.?) <> (EventDao.tupled, EventDao.unapply) 

    def interests = EventInterestQueries.query.filter(_.eventId === id) 
    .flatMap(_.interestFk) 
} 


object EventQueries { 

    lazy val query = TableQuery[EventsTable] 

    val findById = Compiled { k: Rep[Int] => 
    query.filter(_.id === k) 
    } 
} 

İşte EventsInterests var :

val eventInterestQuery = for { 
    event <- EventQueries.query 
    interest <- event.interests 
} yield (event.title, interest.name) 

Await.result(db.run(eventInterestQuery.result).map(println), Duration.Inf) 

Yani bu şu an sahip olduğum şey. Benim istediğim

benzeri bir vaka sınıfını doldurmamız mümkün şudur:

case class EventDao(title: String, 
       interests: Seq[InterestDao], 
       id: Option[Int] = None) 

sorun böyle benim davam sınıfını güncellemek, eğer EventsTable benim def * projeksiyonunu karıştıran biridir olmasıdır. Ayrıca, EventsTable.interests filtresini biraz çirkin olan EventsTable.interestIds gibi bir şeye yeniden adlandırmak zorunda kalacağım, ancak gerekirse yaşayabilirim.

Ayrıca, (event.name, Seq(interest.name)) değerini veren bir for sorgusu yazmanın bir yolunu bulamıyorum. Her neyse, bu sadece bana geri dönmek istediğim bir (EventDao, Seq(InterestDao)) tuple verebilmek için bir atlama taşıdır.

Bu şeyleri nasıl başarabileceğimi bilen var mı? Ayrıca, belirli sayıdaki ilgi alanlarını 'alabilmeyi' istiyorum, bu yüzden bazı sorgular için her şey iade edilebilir, ancak diğerleri için sadece ilk 3 olurdu.

val eventInterestQuery = for { 
    event <- EventQueries.query 
    interest <- event.interests 
} yield (event, interest) 

Await.result(db.run(eventInterestQuery.result 
    // convert the interests to a sequence. 
    .map { 
    _.groupBy(_._1) 
    .map { 
     case (k,v) => (k, v.map(_._2)) 
    }.toSeq 
} 
), Duration.Inf) 

cevap

6

Yani this page okuma ve posta listesinde sohbet sonra nihayet o çalışma var. Sonucu katlayabilirsin. Şu anki projem için şu yardımcımı yazdım:

def foldOneToMany[A, B](in: Seq[(A, Option[B])], eq: (A, B) => Boolean) 
         (f: (A, B) => A): Seq[A] = 
    in.foldLeft(List.empty[A]) { 
    case (head :: tail, (_, Some(rel))) if eq(head, rel) => 
     f(head, rel) :: tail 
    case (r, (el, Some(rel))) => f(el, rel) :: r 
    case (r, (el, None)) => el :: r 
    }.reverse 

Bazı sevgi ile yapabilirdi. Şimdi A, B => Boolean işlevinin B'nin A'ya ait olup olmadığını ve A, B = A'yı A'ya eklediğini belirlemek için A = B'yi kullanır.

Virtualeyes ayrıca bir noktaya sahiptir. Postgres'te db'den biraz daha az bant genişliği kullanmak için array_agg işlevini kullanabilirsiniz.

+8

Bu delilik, tüm grupta toplanmış sonuç kümesini * telin üzerine * göndermek ve sonra da istemciye gruplamak zorundasınız. Küçük sonuç kümeleri için, büyük bir anlaşma yok, ancak veritabanı düzeyinde kolayca gruplandırılabilen 100K + kayıtları gönderiyor, vay. – virtualeyes

+1

@virtualeyes: o zaman nasıl bir yaklaşım önerirsiniz? – LuGo

+1

@virtualeyes Tam soruyu okumadım ama groupBy doğrudan sorguda kullanılamıyor mu? Belgelerin açıklandığı gibi: http://slick.typesafe.com/doc/3.0.0/queries.html#aggregation – Ixx

1

GroupBy ile tek sorun siparişi kaybetmek:

İlgili konular