为模型生成所有可能的点表示法查询

时间:2017-03-15 16:19:03

标签: mongodb scala playframework

例如:

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]

}

将上述UserDetails模型映射到mongodb集合中的文档,如果我想以点表示法手动列出所有可能的查询,那么它们就是:

username
contact.phone
contact.email

如何以编程方式生成这些?

1 个答案:

答案 0 :(得分:1)

你可以使用shapeless:

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

看一下无形的伟大指南