diff --git a/core/src/main/scala/pink/cozydev/protosearch/Hit.scala b/core/src/main/scala/pink/cozydev/protosearch/Hit.scala index 34da1f10..0cfad5a3 100644 --- a/core/src/main/scala/pink/cozydev/protosearch/Hit.scala +++ b/core/src/main/scala/pink/cozydev/protosearch/Hit.scala @@ -26,4 +26,5 @@ case class Hit( val id: Int, val score: Double, val fields: Map[String, String], + val highlight: String, ) diff --git a/core/src/main/scala/pink/cozydev/protosearch/MultiIndex.scala b/core/src/main/scala/pink/cozydev/protosearch/MultiIndex.scala index fc97d4ac..ae3c2d3c 100644 --- a/core/src/main/scala/pink/cozydev/protosearch/MultiIndex.scala +++ b/core/src/main/scala/pink/cozydev/protosearch/MultiIndex.scala @@ -17,6 +17,7 @@ package pink.cozydev.protosearch import pink.cozydev.lucille.Query +import pink.cozydev.protosearch.highlight.{FirstMatchHighlighter, FragmentFormatter} case class MultiIndex( indexes: Map[String, Index], @@ -26,6 +27,7 @@ case class MultiIndex( private val indexSearcher = IndexSearcher(this, schema.defaultOR) private val scorer = Scorer(this, schema.defaultOR) + private val highlighter = FirstMatchHighlighter(FragmentFormatter(100, "", "")) val queryAnalyzer = schema.queryAnalyzer(schema.defaultField) /** Search the index with a `Query`. Results are sorted by descending score. @@ -33,11 +35,14 @@ case class MultiIndex( * @param q The `Query` to search * @return A list of `Hit`s or error */ - def search(q: Query): Either[String, List[Hit]] = { + def search(rawQStr: String, q: Query): Either[String, List[Hit]] = { val docs = indexSearcher.search(q).flatMap(ds => scorer.score(q, ds)) val lstb = List.newBuilder[Hit] docs.map(_.foreach { case (docId, score) => - lstb += Hit(docId, score, fields.map { case (k, v) => (k, v(docId)) }) + val docFields = fields.map { case (k, v) => (k, v(docId)) } + val highlight = + docFields.get("body").map(b => highlighter.highlight(b, rawQStr)).getOrElse("") + lstb += Hit(docId, score, docFields, highlight) }) docs.map(_ => lstb.result()) } @@ -48,7 +53,7 @@ case class MultiIndex( * @return A list of `Hit`s or error */ def search(q: String): Either[String, List[Hit]] = - queryAnalyzer.parse(q).flatMap(search) + queryAnalyzer.parse(q).flatMap(pq => search(q, pq)) /** Search the index with a possibly incomplete query. Meant for use in a "search as your type" * scenario. The last term, which is possibly incomplete, is rewritten to be a prefix. @@ -59,7 +64,7 @@ case class MultiIndex( def searchInteractive(partialQuery: String): Either[String, List[Hit]] = { val rewriteQ = queryAnalyzer.parse(partialQuery).map(mq => mq.mapLastTerm(LastTermRewrite.termToPrefix)) - rewriteQ.flatMap(search) + rewriteQ.flatMap(newq => search(partialQuery, newq)) } } object MultiIndex { diff --git a/core/src/test/scala/pink/cozydev/protosearch/highlight/FirstMatchHighlighterSuite.scala b/core/src/test/scala/pink/cozydev/protosearch/highlight/FirstMatchHighlighterSuite.scala index 936051c8..4bb7a798 100644 --- a/core/src/test/scala/pink/cozydev/protosearch/highlight/FirstMatchHighlighterSuite.scala +++ b/core/src/test/scala/pink/cozydev/protosearch/highlight/FirstMatchHighlighterSuite.scala @@ -17,7 +17,7 @@ package pink.cozydev.protosearch.highlight class FirstMatchHighlighterSuite extends munit.FunSuite { - val formatter = FragmentFormatter(36, "", "") + val formatter = FragmentFormatter(60, "", "") val highlighter = FirstMatchHighlighter(formatter) test("no highlight on no match") { diff --git a/jsinterop/src/main/scala/pink/cozydev/protosearch/JsInterop.scala b/jsinterop/src/main/scala/pink/cozydev/protosearch/JsInterop.scala index 582f1780..cc0f22fe 100644 --- a/jsinterop/src/main/scala/pink/cozydev/protosearch/JsInterop.scala +++ b/jsinterop/src/main/scala/pink/cozydev/protosearch/JsInterop.scala @@ -26,6 +26,7 @@ class JsHit( val id: Int, val score: Double, val fields: js.Dictionary[String], + val highlight: String, ) extends js.Object @JSExportTopLevel("Querier") @@ -40,7 +41,7 @@ class Querier(val mIndex: MultiIndex) { err => { println(err); Nil }, identity, ) - .map(h => new JsHit(h.id, h.score, h.fields.toJSDictionary)) + .map(h => new JsHit(h.id, h.score, h.fields.toJSDictionary, h.highlight)) hits.toJSArray } } diff --git a/laikaIO/src/main/resources/pink/cozydev/protosearch/sbt/search.js b/laikaIO/src/main/resources/pink/cozydev/protosearch/sbt/search.js index 21c5ff4b..d10b51b9 100644 --- a/laikaIO/src/main/resources/pink/cozydev/protosearch/sbt/search.js +++ b/laikaIO/src/main/resources/pink/cozydev/protosearch/sbt/search.js @@ -2,7 +2,7 @@ function renderDoc(hit) { const path = hit.fields.path const link = "../" + hit.fields.path.replace(".txt", ".html") const title = hit.fields.title - const preview = hit.fields.body.slice(0, 150) + "..." + const preview = hit.highlight return ( `