2017-03-15 4 views
1

のためのすべての可能なドット表記クエリを生成:MongoDBのコレクション内のドキュメントにマッピングされている上記UserDetailsモデルと例えばモデル

import play.api.libs.json.Json 

case class UserDetails(
         username: String, 
         contact: Contact 
        ) 


object UserDetails { 

    implicit val userDetailsFormatter = Json.format[UserDetails] 

} 


case class Contact(phone: String, email: String) 


object Contact { 

    implicit val contactFormatter = Json.format[Contact] 

} 

、私は手動でドット表記で可能なすべてのクエリを一覧表示したい場合は、

username 
contact.phone 
contact.email 

これらをプログラムで生成するにはどうすればよいですか?あなたはこのために型崩れ使用することができます

+0

で型崩れする素晴らしいガイドで見ることができます。 'shapeless.LabelledGeneric'を使ってHListとしてエンコードし、あなたが望むものを得るためにラベルに' Keys'を使います。 – flavian

答えて

1

case class UserDetails(username: String, contact: Contact) 
case class Contact(phone: String, email: Email) 
case class Email(name: String, domain: String) 

// a class to store field name and subfields info 
case class FieldInfo(name: String, subfields: List[FieldInfo]) 

// our main typeclass 
// we will use shapeless to derive instance for UserDetails above 
trait FieldList[A] { 
    def fields: List[FieldInfo] 
} 

object FieldList { 
    import shapeless.{ LabelledGeneric, HList, ::, HNil, Witness, Lazy } 
    import shapeless.labelled.{ FieldType, field } 

    // helper function for creating typeclass instance 
    def createInstance[A](f: => List[FieldInfo]): FieldList[A] = new FieldList[A] { 
    def fields = f 
    } 

    // instance for String, here we just care for field name so just a dummy instance... 
    // you should add instance for other basic types (Int, Double...) if required 
    implicit val stringInstance: FieldList[String] = createInstance(Nil) 

    // define instance for base case (HNil) 
    implicit val hnilInstance: FieldList[HNil] = createInstance(Nil) 

    // define rule for HList 
    implicit def hlistInstance[K <: Symbol, H, T <: HList](
    implicit witness: Witness.Aux[K], 
    hInstance: Lazy[FieldList[H]], 
    tInstance: FieldList[T]): FieldList[FieldType[K, H] :: T] = createInstance { 
    val headFieldName: String = witness.value.name 
    val headSubFields = hInstance.value.fields 
    val tail = tInstance.fields 
    FieldInfo(headFieldName, headSubFields) :: tail 
    } 

    // rule for generic ADT 
    implicit def genericObjectInstance[A, H <: HList](
    implicit generic: LabelledGeneric.Aux[A, H], 
    hlistInstance: Lazy[FieldList[H]]): FieldList[A] = createInstance { hlistInstance.value.fields } 

    // some utils... 
    def show(field: FieldInfo): List[String] = { 
    if (field.subfields.isEmpty) List(field.name) 
    else field.subfields.flatMap(sub => show(sub).map(field.name + "." + _)) 
    } 

    def getFieldsName[A](implicit instance: FieldList[A]) = { 
    val fields = instance.fields 
    fields.flatMap(show).mkString("\n") 
    } 
} 

// ok, test it! 
object ShapelessLabelled extends App { 
    println(FieldList.getFieldsName[UserDetails]) 
} 

これをよりよく理解するには、両方の暗黙的なマクロと型崩れ型クラスは、非常に簡単に作業することができますhttps://github.com/underscoreio/shapeless-guide