diff --git a/app/models/ElasticRetriever.scala b/app/models/ElasticRetriever.scala index 7051251c..645b0650 100644 --- a/app/models/ElasticRetriever.scala +++ b/app/models/ElasticRetriever.scala @@ -24,6 +24,7 @@ import scala.concurrent.Future import scala.util.Try import com.sksamuel.elastic4s.requests.searches.sort.FieldSort import com.sksamuel.elastic4s.requests.searches.term.TermQuery +import com.sksamuel.elastic4s.handlers.index.Search class ElasticRetriever @Inject() ( client: ElasticClient, @@ -557,6 +558,27 @@ class ElasticRetriever @Inject() ( val filterQueries = boolQuery().must() :: Nil val fnQueries = boolQuery().should(Seq(fuzzyQueryFn) ++ exactQueryFn) :: Nil val mainQuery = boolQuery().must(fnQueries ::: filterQueries) + val aggQuery = termsAgg("categories", "category.keyword").size(1000) + + val searchFacetCategories = client + .execute { + val aggregations = search(esIndices) + .aggs(aggQuery) + .trackTotalHits(true) + logger.trace(client.show(aggregations)) + aggregations + } + .map { case (aggregations) => + val aggs = (Json.parse(aggregations.body.get) \ "aggregations" \ "categories" \ "buckets") + .validate[Seq[SearchFacetsCategory]] match { + case JsSuccess(value, _) => value + case JsError(errors) => + logger.error(errors.mkString("", " | ", "")) + Seq.empty + } + aggs + } + .await if (qString.nonEmpty) { client @@ -581,10 +603,10 @@ class ElasticRetriever @Inject() ( Seq.empty } - SearchFacetsResults(sresults, hits.result.totalHits) + SearchFacetsResults(sresults, hits.result.totalHits, searchFacetCategories) } } else { - Future.successful(SearchFacetsResults.empty) + Future.successful(SearchFacetsResults(Seq.empty, 0, searchFacetCategories)) } } diff --git a/app/models/GQLSchema.scala b/app/models/GQLSchema.scala index c90a8ba1..bbeba4dd 100644 --- a/app/models/GQLSchema.scala +++ b/app/models/GQLSchema.scala @@ -93,10 +93,11 @@ object GQLSchema { "facets", searchFacetsResultsGQLImp, description = Some("Search facets"), - arguments = queryString :: entityNames :: pageArg :: Nil, + arguments = optQueryString :: entityNames :: pageArg :: Nil, resolve = ctx => { + val queryString = ctx.arg(optQueryString).getOrElse("") val entities = ctx.arg(entityNames).getOrElse(Seq.empty) - ctx.ctx.searchFacets(ctx.arg(queryString), ctx.arg(pageArg), entities) + ctx.ctx.searchFacets(queryString, ctx.arg(pageArg), entities) } ), Field( diff --git a/app/models/entities/SearchFacetsResults.scala b/app/models/entities/SearchFacetsResults.scala index 23048b62..c0dd4706 100644 --- a/app/models/entities/SearchFacetsResults.scala +++ b/app/models/entities/SearchFacetsResults.scala @@ -4,13 +4,7 @@ import play.api.libs.json._ import play.api.libs.json.Reads._ import play.api.libs.functional.syntax._ -case class SearchFacetsResultAggCategory(name: String, total: Long) - -case class SearchFacetsResultAggEntity( - name: String, - total: Long, - categories: Seq[SearchFacetsResultAggCategory] -) +case class SearchFacetsCategory(name: String, total: Long) case class Facet( label: String, @@ -19,8 +13,6 @@ case class Facet( datasourceId: Option[String] ) -case class SearchFacetsResultAggs(total: Long, entities: Seq[SearchFacetsResultAggEntity]) - case class SearchFacetsResult( id: String, label: String, @@ -33,18 +25,25 @@ case class SearchFacetsResult( case class SearchFacetsResults( hits: Seq[SearchFacetsResult], - total: Long + total: Long, + categories: Seq[SearchFacetsCategory] ) object SearchFacetsResults { - val empty: SearchFacetsResults = SearchFacetsResults(Seq.empty, 0) + implicit val searchFacetsCategoryImpW: OWrites[SearchFacetsCategory] = + Json.writes[models.entities.SearchFacetsCategory] - implicit val FacetF: OFormat[Facet] = Json.format[Facet] + implicit val facetF: OFormat[Facet] = Json.format[Facet] - implicit val SearchFacetsResultImpW: OWrites[SearchFacetsResult] = + implicit val searchFacetsResultImpW: OWrites[SearchFacetsResult] = Json.writes[models.entities.SearchFacetsResult] - implicit val SearchFacetsResultImpR: Reads[models.entities.SearchFacetsResult] = + implicit val searchFacetsCategoryImpR: Reads[SearchFacetsCategory] = ( + (__ \ "key").read[String] and + (__ \ "doc_count").read[Long] + )(SearchFacetsCategory.apply _) + + implicit val searchFacetsResultImpR: Reads[models.entities.SearchFacetsResult] = ((__ \ "_id").read[String] and (__ \ "_source" \ "label").read[String] and (__ \ "_source" \ "category").read[String] and @@ -59,6 +58,6 @@ object SearchFacetsResults { case None => Seq.empty[String] })(SearchFacetsResult.apply _) - implicit val mSearchFacetsResultsImpW: OFormat[SearchFacetsResults] = + implicit val searchFacetsResultsImpW: OFormat[SearchFacetsResults] = Json.format[models.entities.SearchFacetsResults] } diff --git a/app/models/gql/Objects.scala b/app/models/gql/Objects.scala index 31527dcb..bf867602 100644 --- a/app/models/gql/Objects.scala +++ b/app/models/gql/Objects.scala @@ -1235,6 +1235,9 @@ object Objects extends Logging { ) ) + implicit val searchFacetsCategoryImp: ObjectType[Backend, SearchFacetsCategory] = + deriveObjectType[Backend, SearchFacetsCategory]() + val searchResultsGQLImp: ObjectType[Backend, SearchResults] = ObjectType( "SearchResults", "Search results", @@ -1275,6 +1278,12 @@ object Objects extends Logging { LongType, description = Some("Total number or results given a entity filter"), resolve = _.value.total + ), + Field( + "categories", + ListType(searchFacetsCategoryImp), + description = Some("Categories"), + resolve = _.value.categories ) ) )