private def getPagesWithNextAndPreviousHighlights()

in backend/app/services/index/ElasticsearchPages.scala [166:251]


  private def getPagesWithNextAndPreviousHighlights(indexName: String, uri: Uri, highlightQuery: Option[String], highlightFields: List[HighlightField], pagesInViewport: List[Page]): Attempt[List[Page]] = {
    highlightQuery match {
      case None =>
        Attempt.Right(List.empty)

      case Some(q) =>
        val firstPageInViewport = pagesInViewport.minBy(_.page).page
        val lastPageInViewport = pagesInViewport.maxBy(_.page).page

        val query = buildQuery(q)
        val documentFilter = termQuery(PagesFields.resourceId, uri.value)

        execute {
          multi(
            // The first page to contain highlights
            search(indexName)
              .sortByFieldAsc(PagesFields.page)
              .highlighting(highlightFields)
              .size(1)
              .query(
                // NB this is a "must" rather than a "should" so we only return pages containing highlights
                must(query).filter(documentFilter)
              ),
            // The last page to contain highlights PRIOR to the FIRST page in the viewport
            search(indexName)
              .sortByFieldDesc(PagesFields.page)
              .highlighting(highlightFields)
              .size(1)
              .query(
                must(query).filter(
                  documentFilter,
                  rangeQuery(PagesFields.page).lt(firstPageInViewport)
                )
              ),
            // The first page to contain highlights AFTER the LAST page in the viewport
            search(indexName)
              .sortByFieldAsc(PagesFields.page)
              .highlighting(highlightFields)
              .size(1)
              .query(
                must(query).filter(
                  documentFilter,
                  rangeQuery(PagesFields.page).gt(lastPageInViewport)
                )
              ),
            // The last page to contain highlights
            search(indexName)
              .sortByFieldDesc(PagesFields.page)
              .highlighting(highlightFields)
              .size(1)
              .query(
                must(query).filter(documentFilter)
              )
          )
        }.flatMap { response =>
          val results = response.items.collect { case MultisearchResponseItem(_, _, Right(result)) => result }

          val errors = response.items.collect { case MultisearchResponseItem(_, status, Left(err)) =>
            ElasticSearchQueryFailure(new IllegalStateException(err.toString), status, None)
          }

          if(errors.nonEmpty) {
            Attempt.Left(MultipleFailures(errors.toList))
          } else {
            val pointers = results.flatMap(_.hits.hits).map(_.to[Page]).sortBy(_.page)

            if(pointers.isEmpty) {
              Attempt.Right(List.empty)
            } else {
              val min = pointers.head
              val max = pointers.last

              val maybePrior = pointers.reverse.find(_.page < firstPageInViewport)
              val maybeAfter = pointers.find(_.page > lastPageInViewport)

              // The first and last are used if you need to wrap around, e.g. you're on highlight 0 and you press "previous"
              // you should be taken to the last highlight in the entire document
              val prior = maybePrior.getOrElse(max)
              val after = maybeAfter.getOrElse(min)

              Attempt.Right(distinctByPageNumber(List(after, prior)))
            }
          }
        }
    }
  }