Skip to content

Instantly share code, notes, and snippets.

@shengc
Last active April 27, 2021 23:22
Show Gist options
  • Save shengc/2b4052ed59419e86f36f48da35812634 to your computer and use it in GitHub Desktop.
Save shengc/2b4052ed59419e86f36f48da35812634 to your computer and use it in GitHub Desktop.
import java.util.Date

import org.apache.lucene.document._
import shapeless._
import shapeless.ops.hlist._
import shapeless.ops.record._

// extends from scala.annotation.StaticAnnotation is not necessary, but it kind of reminds me this is an annotation
final class indexable() extends scala.annotation.StaticAnnotation 

final case class Person(
  id: Long
, @indexable name: String
, @indexable age: Int
, @indexable married: Boolean
, @indexable jobDesc: Option[String]
, @indexable dateofbirth: Data
, ssn: String
)

sealed trait Indexer0 extends Poly2 {
  implicit def option[K, V](implicit indexer: Case.Aux[K, V, Vector[Field]]) = at[K, Option[V]] { (k, ov) =>
    ov.fold(Vector.emtpy[Field])(indexer.apply(k, _))
  }
}
object Indexer extends Indexer0 {
  implicit def int[K <: Symbol](implicit witness: Witness.Aux[K]) = at[K, Int] { (_, int) =>
    val nm = witness.value.name
    Vector( new IntPoint(nm, int), new StoreField(nm, Int) )
  }
  implicit def date[K <: Symbol](implicit witness: Witness.Aux[K]) = at[K, Date] { (_, date) =>
    val nm = witness.value.name
    val long = date.getTime
    Vector( new LongPoint(nm, long), new StoreField(nm, long) )
  }
  implicit def bool[K <: Symbol](implicit witness: Witness.Aux[K]) = at[K, Boolean] { (_, bool) => 
    val nm = witness.value.name
    Vector( new StringField(nm, bool.toString, Field.Store.YES) )
  }
  implicit def text[K <: Symbol](implicit witness: Witness.Aux[K]) = at[K, String] { (_, text) => 
    val nm = witness.value.name
    Vector( new TextField(nm, text, Field.Store.YES) )
  }
}

object Collector extends Poly2 {
  implicit def none[K <: Symbol, V] = at[Document, (None.type, K, V)] { (doc, _) => doc }
  implicit def some[K <: Symbol, V](implicit indexer: Indexer.Case.Aux[K, V, Vector[Field]]) = 
    at[Document, (Some[indexable], K, V)] { case (doc, (_, k, v)) => 
      indexer.apply(k, v) foreach { doc.add }
      doc
    }
}

// for example
// Given a Person(123, "John Doe", 23, false, None, new Date(1995, 3, 12),"000-000-0000")
// HL would be FieldType[String("id"), Int] :: FieldType[String("name"), String] :: FieldType[String("age"), Int] :: FieldType[String("married"), Boolean] :: FieldType[String("jobDesc"), Option[String]] :: FieldType[String("dateofbirth"), Date] :: FieldType[String("ssn"), String] :: HNil
// AL would be None.type :: Some[indexable] :: Some[Indexable] :: Some[Indexable] :: Some[indexable] :: Some[indexable] :: None.type :: HNil
// KL would be String("id") :: String("name") :: String("age") :: String("married") :: String("jobDesc") :: String("dateofbirth") :: String("ssn") :: HNil
// VL would be Int :: String :: Int :: Boolean :: Option[String] :: Date :: String :: HNil
// ZL would be (None.type, String("id"), Int) :: (Some[indexable], String("name"), String) :: (Some[idexable], String("age"), Int) :: (Some[indexable], String("married"), Boolean) :: (Some[indexable], String("jobDesc"), Option[String]) :: (Some[indexable], String("dateofbirth"), Date) :: (None.type, String("ssn"), String) :: HNil
def document[HL <: HList, AL <: HList, KL <: HList, VL <: HList, ZL <: HList](person: Person)(implicit 
  iso: LabelledGeneric.Aux[Person, HL]
, annot: Annotations.Aux[indexable, Person, AL]
, keys: Keys.Aux[HL, KL]
, values: Values.Aux[HL, VL]
, zip: Zip.Aux[AL :: KL :: VL :: HNil, ZL])
, fl: LeftFolder.Aux[ZL, Document, Collector.type, Document]): Document = {
  val doc = new Document()
  zip( annot(), keys(), values(iso.to(person)) ).foldLeft(doc)(Collector)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment