Skip to content

Commit

Permalink
Support highlighting in top hits aggregation (#3083)
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippus committed Jun 7, 2024
1 parent 86228a3 commit 9da2e0c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.sksamuel.elastic4s.requests.common.FetchSourceContext
import com.sksamuel.elastic4s.requests.script.Script
import com.sksamuel.elastic4s.requests.searches.sort.Sort
import com.sksamuel.elastic4s.ext.OptionImplicits._
import com.sksamuel.elastic4s.requests.searches.{Highlight, HighlightField, HighlightOptions}

case class TopHitsAggregation(name: String,
explain: Option[Boolean] = None,
Expand All @@ -16,7 +17,8 @@ case class TopHitsAggregation(name: String,
scripts: Map[String, Script] = Map.empty,
storedFields: Seq[String] = Nil,
subaggs: Seq[AbstractAggregation] = Nil,
metadata: Map[String, AnyRef] = Map.empty)
metadata: Map[String, AnyRef] = Map.empty,
highlight: Option[Highlight] = None)
extends Aggregation {

type T = TopHitsAggregation
Expand Down Expand Up @@ -45,6 +47,18 @@ case class TopHitsAggregation(name: String,

def script(name: String, script: Script): T = copy(scripts = scripts + (name -> script))

def highlighting(first: HighlightField, rest: HighlightField*): TopHitsAggregation =
highlighting(HighlightOptions(), first +: rest)

def highlighting(fields: Iterable[HighlightField]): TopHitsAggregation =
highlighting(HighlightOptions(), fields)

def highlighting(options: HighlightOptions, first: HighlightField, rest: HighlightField*): TopHitsAggregation =
highlighting(options, first +: rest)

def highlighting(options: HighlightOptions, fields: Iterable[HighlightField]): TopHitsAggregation =
copy(highlight = Highlight(options, fields).some)

override def subAggregations(aggs: Iterable[AbstractAggregation]): T =
sys.error("Top Hits does not support sub aggregations")
override def metadata(map: Map[String, AnyRef]): T = copy(metadata = map)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sksamuel.elastic4s.requests.searches.aggs.builders

import com.sksamuel.elastic4s.handlers.common.FetchSourceContextBuilderFn
import com.sksamuel.elastic4s.handlers.searches.HighlightBuilderFn
import com.sksamuel.elastic4s.handlers.searches.queries.sort.SortBuilderFn
import com.sksamuel.elastic4s.json.{XContentBuilder, XContentFactory}
import com.sksamuel.elastic4s.requests.searches.aggs.TopHitsAggregation
Expand All @@ -25,6 +26,11 @@ object TopHitsAggregationBuilder {
agg.fetchSource.foreach(FetchSourceContextBuilderFn(builder, _))

agg.explain.foreach(builder.field("explain", _))

agg.highlight.foreach { highlight =>
builder.rawField("highlight", HighlightBuilderFn(highlight))
}

agg.version.foreach(builder.field("version", _))

builder.endObject().endObject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class TopHitsAggregationBuilderTest extends AnyFunSuite with Matchers {
test("top hits aggregation should generate expected json") {
test("top hits aggregation builder should generate expected json") {
val q = TopHitsAggregation("top_items")
.size(5)
.from(10)
Expand All @@ -17,4 +17,11 @@ class TopHitsAggregationBuilderTest extends AnyFunSuite with Matchers {
TopHitsAggregationBuilder(q).string shouldBe
"""{"top_hits":{"size":5,"from":10,"sort":[{"price":{"mode":"median","order":"asc"}}],"explain":false,"version":true}}"""
}

test("top hits aggregation builder should support highlighting") {
val q = TopHitsAggregation("top_items")
.highlighting(HighlightField("name"))
TopHitsAggregationBuilder(q).string shouldBe
"""{"top_hits":{"highlight":{"fields":{"name":{}}}}}"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ case class TopHit(@JsonProperty("_index") index: String,
@JsonProperty("_id") id: String,
@JsonProperty("_score") score: Option[Double],
sort: Seq[String],
@JsonProperty("_source") source: Map[String, Any]) extends Transformable {
@JsonProperty("_source") source: Map[String, Any],
highlight: Map[String, Any]) extends Transformable {

@deprecated("types are deprecated in elasticsearch", "7.7")
def ref: DocumentRef = DocumentRef(index, `type`, id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.sksamuel.elastic4s.requests.searches.aggs

import com.sksamuel.elastic4s.AggReader
import com.sksamuel.elastic4s.requests.common.RefreshPolicy
import com.sksamuel.elastic4s.requests.searches.Total
import com.sksamuel.elastic4s.requests.searches.{HighlightField, Total}
import com.sksamuel.elastic4s.testkit.DockerTests
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers
Expand Down Expand Up @@ -75,7 +75,19 @@ class TopHitsAggregationTest extends AnyFreeSpec with DockerTests with Matchers
val agg = resp.aggs.terms("agg1")
val tophits = agg.buckets.find(_.key == "london").get.tophits("agg2")
tophits.hits.head.safeTo[String].get shouldBe "{\"name\":\"buckingham palace\",\"location\":\"london\"}"
}

"should support highlighting" in {
val resp = client.execute {
search("tophits").matchQuery("name", "palace").aggs {
termsAgg("agg1", "location").addSubagg(
topHitsAgg("agg2").sortBy(fieldSort("name")).highlighting(HighlightField("name"))
)
}
}.await.result

val tophits = resp.aggs.terms("agg1").buckets.find(_.key == "london").get.tophits("agg2")
tophits.hits.head.highlight shouldBe Map("name" -> List("buckingham <em>palace</em>"))
}
}
}

0 comments on commit 9da2e0c

Please sign in to comment.