scala slick मेथड मैं अब तक समझ नहीं पाया


89

मैं कुछ चालाक कार्यों को समझने की कोशिश करता हूं और इसकी आवश्यकता है।

यहाँ यह एक उदाहरण है:

package models

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

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  // This is the primary key column
  def name = column[String]("name")

  // Every table needs a * projection with the same type as the table's type parameter
  def * = id.? ~ name <>(Bar, Bar.unapply _)
}

क्या कोई मुझे समझा सकता है कि *यहाँ विधि का उद्देश्य क्या है <>, क्यों है unapply? और प्रोजेक्शन क्या है - विधि ~'का उदाहरण देता है Projection2?

जवाबों:


198

[अद्यतन] - समझ पर एक और (अभी तक) स्पष्टीकरण जोड़ा गयाfor

  1. *विधि:

    यह डिफ़ॉल्ट प्रोजेक्शन लौटाता है - जो आप का वर्णन करता है:

    'सभी कॉलम (या गणना किए गए मान) मुझे आमतौर पर दिलचस्पी है'।

    आपकी तालिका में कई फ़ील्ड हो सकते हैं; आपको केवल अपने डिफ़ॉल्ट प्रक्षेपण के लिए सबसेट की आवश्यकता है। डिफ़ॉल्ट प्रक्षेपण को तालिका के प्रकार के मापदंडों से मेल खाना चाहिए।

    आइए इसे एक बार में लें। <>सामान के बिना , बस *:

    // First take: Only the Table Defintion, no case class:
    
    object Bars extends Table[(Int, String)]("bar") {
      def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
      def name = column[String]("name")
    
      def * = id ~ name // Note: Just a simple projection, not using .? etc
    }
    
    // Note that the case class 'Bar' is not to be found. This is 
    // an example without it (with only the table definition)
    

    इस तरह बस एक तालिका परिभाषा आपको प्रश्न बनाने देगी:

    implicit val session: Session = // ... a db session obtained from somewhere
    
    // A simple select-all:
    val result = Query(Bars).list   // result is a List[(Int, String)]
    

    इन जैसे सरल प्रश्नों के लिए डिफ़ॉल्ट प्रक्षेपण (Int, String)होता है List[(Int, String)]

    // SELECT b.name, 1 FROM bars b WHERE b.id = 42;
    val q = 
       for (b <- Bars if b.id === 42) 
         yield (b.name ~ 1)
         // yield (b.name, 1) // this is also allowed: 
                              // tuples are lifted to the equivalent projection.
    

    किस प्रकार का है q? यह Queryप्रक्षेपण के साथ है (String, Int)। जब आह्वान किया जाता है, तो यह प्रक्षेपण के अनुसार टुपल्स Listकी वापसी करता है (String, Int)

     val result: List[(String, Int)] = q.list
    

    इस मामले में, आपने उस प्रक्षेपण को परिभाषित किया है जिसे आप समझ के yieldखंड में चाहते हैं for

  2. अब के बारे में <>और Bar.unapply

    यह प्रदान करता है जिसे मैप्ड प्रोजेक्ट कहा जाता है ।

    अब तक हमने देखा है कि स्केला आपको स्केला में प्रश्नों को व्यक्त करने की अनुमति देता है जो स्तंभों (या गणना मूल्यों) का एक प्रक्षेपण लौटाते हैं ; इसलिए इन प्रश्नों को निष्पादित करते समय आपको एक क्वेरी की परिणाम पंक्ति को स्कूप टपल के रूप में सोचना होगा । टपल का प्रकार उस प्रोजेक्शन से मेल खाएगा जिसे परिभाषित किया गया है ( forडिफ़ॉल्ट उदाहरण के अनुसार, पिछले उदाहरण में आपकी समझ से *)। यही कारण है कि जहां का प्रकार है और का प्रकार है, field1 ~ field2का एक प्रक्षेपण देता है ।Projection2[A, B]Afield1Bfield2

    q.list.map {
      case (name, n) =>  // do something with name:String and n:Int
    }
    
    Queury(Bars).list.map {
      case (id, name) =>  // do something with id:Int and name:String 
    }
    

    यदि हम बहुत अधिक कॉलम रखते हैं, तो हम tuples के साथ काम कर रहे हैं, जो बोझिल हो सकता है। हम परिणामों के बारे में नहीं सोचना चाहते हैं TupleNबल्कि नामित क्षेत्रों के साथ कुछ ऑब्जेक्ट के रूप में ।

    (id ~ name)  // A projection
    
    // Assuming you have a Bar case class:
    case class Bar(id: Int, name: String) // For now, using a plain Int instead
                                          // of Option[Int] - for simplicity
    
    (id ~ name <> (Bar, Bar.unapply _)) // A MAPPED projection
    
    // Which lets you do:
    Query(Bars).list.map ( b.name ) 
    // instead of
    // Query(Bars).list.map { case (_, name) => name }
    
    // Note that I use list.map instead of mapResult just for explanation's sake.
    

    यह कैसे काम करता है? <>एक प्रक्षेपण लेता है Projection2[Int, String]और प्रकार पर एक मानचित्रित प्रक्षेपण देता है Bar। दो तर्कों में Bar, Bar.unapply _ यह बताया गया है कि कैसे इस (Int, String)प्रक्षेपण को केस क्लास में मैप किया जाना चाहिए।

    यह एक दो तरफा मानचित्रण है; Barकेस क्लास कंस्ट्रक्टर है, इसीलिए (id: Int, name: String)ए से जाने के लिए जरूरी जानकारी है Bar। और unapply अगर आपने अनुमान लगाया है, तो यह उल्टा है।

    कहाँ unapplyसे आता है? यह किसी भी साधारण मामला वर्ग के लिए एक मानक स्काला विधि उपलब्ध है - बस को परिभाषित Barआप एक देता है Bar.unapplyजो एक है निकालने कि वापस पाने के लिए इस्तेमाल किया जा सकता idऔर nameउस Barके साथ बनाया गया था:

    val bar1 = Bar(1, "one")
    // later
    val Bar(id, name) = bar1  // id will be an Int bound to 1,
                              // name a String bound to "one"
    // Or in pattern matching
    val bars: List[Bar] = // gotten from somewhere
    val barNames = bars.map {
      case Bar(_, name) => name
    }
    
    val x = Bar.unapply(bar1)  // x is an Option[(String, Int)]
    

    तो आपके डिफ़ॉल्ट प्रोजेक्शन को उस केस क्लास में मैप किया जा सकता है जिसका आप सबसे अधिक उपयोग करने की उम्मीद करते हैं:

    object Bars extends Table[Bar]("bar") {
      def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
      def name = column[String]("name")
      def * = id ~ name <>(Bar, Bar.unapply _)
    }
    

    या आप इसे प्रति-क्वेरी भी कर सकते हैं:

    case class Baz(name: String, num: Int)
    
    // SELECT b.name, 1 FROM bars b WHERE b.id = 42;
    val q1 = 
       for (b <- Bars if b.id === 42) 
         yield (b.name ~ 1 <> (Baz, Baz.unapply _))
    

    यहाँ के प्रकार के q1 एक है Queryएक साथ मैप किया गया करने के लिए प्रक्षेपण Baz। जब आह्वान किया जाता है, तो यह वस्तुओं Listकी वापसी करता है Baz:

     val result: List[Baz] = q1.list
    
  3. अंत में, एक तरफ के रूप में, .?ऑफर ऑप्शन लिफ्टिंग - मानों से निपटने का स्काला तरीका जो नहीं हो सकता है।

     (id ~ name)   // Projection2[Int, String] // this is just for illustration
     (id.? ~ name) // Projection2[Option[Int], String]
    

    जो, लपेटकर, आपकी मूल परिभाषा के साथ अच्छी तरह से काम करेगा Bar:

    case class Bar(id: Option[Int] = None, name: String)
    
    // SELECT b.id, b.name FROM bars b WHERE b.id = 42;
    val q0 = 
       for (b <- Bars if b.id === 42) 
         yield (b.id.? ~ b.name <> (Bar, Bar.unapply _))
    
    
    q0.list // returns a List[Bar]
    
  4. स्लिक कैसे forसमझ का उपयोग करता है पर टिप्पणी के जवाब में :

    किसी तरह, साधु हमेशा दिखाने का प्रबंधन करते हैं और स्पष्टीकरण का हिस्सा बनने की मांग करते हैं ...

    समझ के लिए केवल संग्रह के लिए विशिष्ट नहीं हैं। उनका उपयोग किसी भी प्रकार के मोनाड पर किया जा सकता है , और संग्रह स्काला में उपलब्ध कई प्रकार के मोनाड प्रकारों में से एक है।

    लेकिन जैसा कि संग्रह परिचित हैं, वे स्पष्टीकरण के लिए एक अच्छा प्रारंभिक बिंदु बनाते हैं:

    val ns = 1 to 100 toList; // Lists for familiarity
    val result = 
      for { i <- ns if i*i % 2 == 0 } 
        yield (i*i)
    // result is a List[Int], List(4, 16, 36, ...)
    

    स्काला में, एक समझ के लिए विधि के लिए वाक्य रचना चीनी है (संभवतः नेस्टेड) ​​विधि कॉल: उपरोक्त कोड इसके बराबर (अधिक या कम) है:

    ns.filter(i => i*i % 2 == 0).map(i => i*i)
    

    मूल रूप से, के साथ कुछ भी filter, map, flatMap तरीकों (दूसरे शब्दों में, एक इकाई ) एक में इस्तेमाल किया जा सकता forसमझ के स्थान पर ns। एक अच्छा उदाहरण विकल्प मोनाड है । यहाँ पिछला उदाहरण है जहाँ एक ही forकथन दोनों और Listसाथ ही साथ काम करता है Option:

    // (1)
    val result = 
      for { 
        i <- ns          // ns is a List monad
        i2 <- Some(i*i)  // Some(i*i) is Option
          if i2 % 2 == 0 // filter
      } yield i2
    
    // Slightly more contrived example:
    def evenSqr(n: Int) = { // return the square of a number 
      val sqr = n*n         // only when the square is even
      if (sqr % 2 == 0) Some (sqr)
      else None
    }
    
    // (2)
    result = 
      for { 
        i <- ns  
        i2 <- evenSqr(i) // i2 may/maynot be defined for i!
      } yield i2
    

    पिछले उदाहरण में, रूपांतरण शायद इस तरह दिखेगा:

    // 1st example
    val result = 
      ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
    
    // Or for the 2nd example
    result = 
      ns.flatMap(i => evenSqr(i)) 
    

    स्लिक में, प्रश्नों monadic हैं - वे के साथ सिर्फ वस्तुओं रहे हैं map, flatMapऔर filterतरीकों। तो forसमझ ( *विधि के स्पष्टीकरण में दिखाया गया है ) बस करने के लिए अनुवाद:

    val q = 
      Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
    // Type of q is Query[(String, Int)]
    
    val r: List[(String, Int)] = q.list // Actually run the query
    

    आप देख सकते हैं, flatMap, mapऔर filterएक उत्पन्न करने के लिए उपयोग किया जाता है Queryके बार-बार परिवर्तन द्वारा Query(Bars) से प्रत्येक मंगलाचरण के साथ filterऔर map। संग्रह के मामले में ये विधियां वास्तव में संग्रह को पुन: व्यवस्थित और फ़िल्टर करती हैं लेकिन स्लिक में इनका उपयोग SQL उत्पन्न करने के लिए किया जाता है। अधिक जानकारी यहाँ: कैसे स्लैक स्लैक कोड को जेडडीबीसी में अनुवाद करता है?


'1' स्पष्टीकरण ब्लॉक में: यह स्पष्ट नहीं है कि 'Val q =' WrappingQuery है, यह कोड को पढ़ते समय एक सूची <प्रोजेक्शन 2> की तरह दिखता है। यह कैसे संभव है कि यह क्वेरी में बदल जाए ..? (मैं अभी भी आपके स्पष्टीकरण के साथ खेल रहा हूं यह समझने के लिए कि यह कैसे काम करता है। इसके लिए धन्यवाद!)
ses

@ses - इस बारे में (थोड़ा लंबा) स्पष्टीकरण जोड़ा ... इसके अलावा, stackoverflow.com/questions/13454347/monads-with-java-8/… पर देखें - मुझे एहसास हुआ कि यह लगभग एक ही सामग्री है।
फैज

रहस्यमय संकलन त्रुटियों का सामना करने वालों पर ध्यान दें, फू का उपयोग करें? विकल्प [T] कॉलम के लिए या आपको टाइप बेमेल पढ़ने में मुश्किल होगी। धन्यवाद, फैज़!
sventechie

1
यह एक शानदार जवाब है ... यह बहुत अच्छा होगा, हालांकि अगर इसे स्लिक 3.0 के लिए अपडेट किया जा सकता है
Ixx

6

चूंकि किसी और ने उत्तर नहीं दिया है, इसलिए यह आपको आरंभ करने में मदद कर सकता है। मैं बहुत अच्छी तरह से चालाक नहीं जानता।

से स्लिक प्रलेखन :

लिफ्टिंग एंबेडिंग:

प्रत्येक तालिका को एक डिफ़ॉल्ट प्रक्षेपण के लिए एक * विधि की आवश्यकता होती है। यह बताता है कि क्वेरी से पंक्तियाँ (तालिका ऑब्जेक्ट के रूप में) वापस करने पर आपको क्या मिलता है। स्लिक के * प्रोजेक्शन का डेटाबेस में एक से मिलान नहीं होता है। आप नए कॉलम जोड़ सकते हैं (जैसे कि गणना किए गए मानों के साथ) या आप की तरह कुछ कॉलमों को छोड़ दें। * प्रक्षेपण के अनुरूप गैर-उठा हुआ प्रकार तालिका के एक प्रकार के पैरामीटर के रूप में दिया गया है। सरल, गैर-मैप की गई तालिकाओं के लिए, यह एक एकल स्तंभ प्रकार या स्तंभ प्रकारों का एक प्रकार होगा।

दूसरे शब्दों में, डेटाबेस से लौटी हुई पंक्ति से निपटने के लिए चालाक को यह जानना होगा। आपके द्वारा परिभाषित विधि उनके पार्सर कॉम्बिनेटर फ़ंक्शन का उपयोग आपके स्तंभ परिभाषाओं को किसी ऐसी चीज़ में संयोजित करने के लिए करती है जो किसी पंक्ति में उपयोग की जा सकती हैं।


ook। और प्रोजेक्शन स्तंभों का एक प्रतिनिधित्व है .. जैसे: अंतिम वर्ग प्रोजेक्शन 2 [T1, T2] (ओवरराइड वैल _1: कॉलम [T1], ओवरराइड वैल _2: कॉलम [T2]) प्रोजेक्शन के साथ Tuple2 (_1, _2) का विस्तार करता है ( T1, T2)] {..
ses

अब .. कैसे आना है: बार '' अनपेक्षित 'विधि है?
ऋषि

2
अहा .. - सभी मामले कक्षाएं उत्पाद विशेषता को लागू करती हैं, और अनपेक्षित रूप से उत्पाद की विधि है। जादू।
Ses
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.