Skip to content

Instantly share code, notes, and snippets.

@anthonynsimon
Created March 1, 2019 10:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anthonynsimon/da07607480d1013c2201fb47a18e0176 to your computer and use it in GitHub Desktop.
Save anthonynsimon/da07607480d1013c2201fb47a18e0176 to your computer and use it in GitHub Desktop.
Elastic Search DSL
import scala.collection.JavaConverters._
import com.fasterxml.jackson.databind.node._
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import scala.language.implicitConversions
object JacksonDSL {
object ObjectNode {
def apply(props: (String, JsonNode)*): ObjectNode = {
val obj = new ObjectNode(JsonNodeFactory.instance)
props.foreach { case (k, v) => obj.set(k, v) }
obj
}
def parse(json: String): JsonNode = new ObjectMapper().readTree(json)
}
object ArrayNode {
def apply(elems: JsonNode*): ArrayNode = {
val arr = new ArrayNode(JsonNodeFactory.instance)
elems.foreach(arr.add)
arr
}
}
// Implicit conversions between basic types and their JsonNode equivalents
implicit def toJson(v: Int): IntNode = IntNode.valueOf(v)
implicit def toJson(v: Boolean): BooleanNode = BooleanNode.valueOf(v)
implicit def toJson(v: String): TextNode = TextNode.valueOf(v)
implicit def toJson(v: Double): DoubleNode = DoubleNode.valueOf(v)
// Extensions methods to map MissingNode to Option
implicit class PimpedJsonNode(val node: JsonNode) extends AnyVal {
def toOption: Option[JsonNode] = if (node.isMissingNode) None else Some(node)
def maybeAt(path: String): Option[JsonNode] = node.at(path).toOption
}
}
sealed trait Expr
case class Search(e: Expr*) extends Expr
case class Query(e: Expr*) extends Expr
case class Bool(e: Expr*) extends Expr
case class From(n: Int) extends Expr
case class Size(n: Int) extends Expr
case class Sort(field: String, order: String) extends Expr
case class Filter(e: Expr*) extends Expr
case class Must(e: Expr*) extends Expr
case class MustNot(e: Expr*) extends Expr
case class Should(e: Expr*) extends Expr
case class ShouldNot(e: Expr*) extends Expr
case class Match(field: String, value: String) extends Expr
case class StringTerm(field: String, value: String) extends Expr
case class IntTerm(field: String, value: Int) extends Expr
case class BooleanTerm(field: String, value: Boolean) extends Expr
case class DoubleTerm(field: String, value: Double) extends Expr
def asJson(expression: Expr): ObjectNode = {
import JacksonDSL._
def mergeNodes(nodes: Seq[ObjectNode]): ObjectNode = {
val result = ObjectNode()
nodes.foreach { node =>
node.fieldNames().asScala.foreach { field =>
val value = node.get(field)
result.set(field, value)
}
}
result
}
expression match {
case search: Search => mergeNodes(search.e.map(ex => asJson(ex)))
case query: Query => ObjectNode(
"query" -> mergeNodes(query.e.map(ex => asJson(ex)))
)
case bool: Bool => ObjectNode(
"bool" -> mergeNodes(bool.e.map(ex => asJson(ex)))
)
case filter: Filter => ObjectNode(
"filter" -> mergeNodes(filter.e.map(ex => asJson(ex)))
)
case must: Must => ObjectNode(
"must" -> ArrayNode(must.e.map(ex => asJson(ex)): _*)
)
case mustNot: MustNot => ObjectNode(
"must_not" -> ArrayNode(mustNot.e.map(ex => asJson(ex)): _*)
)
case should: Should => ObjectNode(
"should" -> ArrayNode(should.e.map(ex => asJson(ex)): _*)
)
case shouldNot: ShouldNot => ObjectNode(
"should_not" -> ArrayNode(shouldNot.e.map(ex => asJson(ex)): _*)
)
case matched: Match => ObjectNode(
"match" -> ObjectNode(
matched.field -> matched.value
)
)
case term: StringTerm => ObjectNode(
"term" -> ObjectNode(
term.field -> term.value
)
)
case term: BooleanTerm => ObjectNode(
"term" -> ObjectNode(
term.field -> term.value
)
)
case term: DoubleTerm => ObjectNode(
"term" -> ObjectNode(
term.field -> term.value
)
)
case term: IntTerm => ObjectNode(
"term" -> ObjectNode(
term.field -> term.value
)
)
case from: From => ObjectNode("from" -> from.n)
case size: Size => ObjectNode("size" -> size.n)
case sort: Sort => ObjectNode("sort" -> ObjectNode(
sort.field -> ObjectNode(
"order" -> sort.order
)
))
}
}
val query = Search(
From(0),
Size(90),
Sort("rating", "desc"),
Query(
Bool(
Must(
StringTerm("genre", "drama"),
StringTerm("locale", "en-US"),
),
MustNot(
BooleanTerm("featured", true)
)
)
),
)
asJson(query).toString
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment