diff --git a/app/components/elasticsearch/ElasticsearchComponent.scala b/app/components/elasticsearch/ElasticsearchComponent.scala index 58addb6..2c7cacb 100644 --- a/app/components/elasticsearch/ElasticsearchComponent.scala +++ b/app/components/elasticsearch/ElasticsearchComponent.scala @@ -14,16 +14,22 @@ import com.sksamuel.elastic4s.http.JavaClient import com.sksamuel.elastic4s.requests.searches._ import com.sksamuel.elastic4s.requests.searches.queries.funcscorer.FieldValueFactorFunctionModifier import configuration.ElasticsearchConfiguration + import javax.inject.{Inject, Singleton} -import models.entities.DNA.{Gene, Variant} -import models.entities.Entities.{SearchResultSet, Study} +import models.entities.DNA.Variant +import models.entities.Entities.SearchResultSet import models.entities.Violations.{InputParameterCheckError, SearchStringViolation} -import models.implicits.{ElasticSearchEntity, EsHitReader} -import play.api.{Configuration, Logger, Logging} +import models.implicits.{ + ElasticSearchEntity, + EsHitReader, + GeneSearchResult, + StudySearchResult, + VariantSearchResult +} +import play.api.{Configuration, Logger} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -import scala.util.{Failure, Success} object GeneticsApiQueries { @@ -142,11 +148,11 @@ class ElasticsearchComponent @Inject() (configuration: Configuration) extends El // build output SearchResultSet( gene.totalHits, - gene.hits.asInstanceOf[Seq[Gene]], + gene.hits.asInstanceOf[Seq[GeneSearchResult]], variant.totalHits, - variant.hits.asInstanceOf[Seq[Variant]], + variant.hits.asInstanceOf[Seq[VariantSearchResult]], study.totalHits, - study.hits.asInstanceOf[Seq[Study]] + study.hits.asInstanceOf[Seq[StudySearchResult]] ) } } diff --git a/app/models/entities/DNA.scala b/app/models/entities/DNA.scala index 178debb..ee0e9b8 100644 --- a/app/models/entities/DNA.scala +++ b/app/models/entities/DNA.scala @@ -129,8 +129,7 @@ object DNA { gnomadAnnotation: GnomadAnnotation, chromosomeB37: Option[String], positionB37: Option[Long] - ) extends ElasticSearchEntity - with SkelVariant { + ) extends SkelVariant { lazy val idB37: Option[String] = (chromosomeB37, positionB37) match { case (Some(c), Some(p)) => @@ -258,7 +257,7 @@ object DNA { end: Option[Long], fwd: Option[Boolean], exons: Seq[Long] - ) extends ElasticSearchEntity + ) object Gene extends ((String, diff --git a/app/models/entities/Entities.scala b/app/models/entities/Entities.scala index 3d1446c..d300652 100644 --- a/app/models/entities/Entities.scala +++ b/app/models/entities/Entities.scala @@ -6,8 +6,8 @@ import models.Functions.{ defaultIntervalTypes, defaultQtlTypes } -import models.entities.DNA.{Gene, SimpleVariant, Variant} -import models.implicits.ElasticSearchEntity +import models.entities.DNA.{SimpleVariant, Variant} +import models.implicits.{GeneSearchResult, StudySearchResult, VariantSearchResult} import scala.collection.SeqView @@ -220,7 +220,7 @@ object Entities { nCases: Option[Long], traitCategory: Option[String], numAssocLoci: Option[Long] - ) extends ElasticSearchEntity + ) case class GeneTagVariant(geneId: String, tagVariantId: String, overallScore: Double) @@ -302,16 +302,15 @@ object Entities { } } - case class VariantSearchResult(variant: Variant) - case class SearchResultSet( totalGenes: Long = 0, - genes: Seq[Gene] = Seq(), + genes: Seq[GeneSearchResult] = Seq(), totalVariants: Long = 0, - variants: Seq[Variant] = Seq(), + variants: Seq[VariantSearchResult] = Seq(), totalStudies: Long = 0, - studies: Seq[Study] = Seq() + studies: Seq[StudySearchResult] = Seq() ) { + // fixme: this appears to be unused: remove? def combine(that: SearchResultSet): SearchResultSet = SearchResultSet( totalGenes + that.totalGenes, diff --git a/app/models/gql/GQLSchema.scala b/app/models/gql/GQLSchema.scala index daf16cc..a6d74f9 100644 --- a/app/models/gql/GQLSchema.scala +++ b/app/models/gql/GQLSchema.scala @@ -276,14 +276,6 @@ object GQLSchema ) ) - val variantSearchResult: ObjectType[Backend, VariantSearchResult] = ObjectType( - "VariantSearchResult", - "Variant search result object", - fields[Backend, VariantSearchResult]( - Field("variant", variant, Some("A variant"), resolve = _.value.variant) - ) - ) - val searchResult: ObjectType[Backend, SearchResultSet] = ObjectType( "SearchResult", "Search data by a query string", @@ -303,13 +295,28 @@ object GQLSchema Some("Total number of studies found"), resolve = _.value.totalStudies ), - Field("genes", ListType(gene), Some("Gene search result list"), resolve = _.value.genes), - Field("variants", - ListType(variant), - Some("Variant search result list"), - resolve = _.value.variants + Field( + "genes", + ListType(gene), + Some("Gene search result list"), + resolve = searchResultSet => { + val geneIds = searchResultSet.value.genes.map(_.id) + genesFetcher.deferSeq(geneIds) + } ), - Field("studies", ListType(study), Some("Study search result list"), resolve = _.value.studies) + Field( + "variants", + ListType(variant), + Some("Variant search result list"), + resolve = + searchResultSet => variantsFetcher.deferSeq(searchResultSet.value.variants.map(_.id)) + ), + Field("studies", + ListType(study), + Some("Study search result list"), + resolve = + searchResultSet => studiesFetcher.deferSeq(searchResultSet.value.studies.map(_.id)) + ) ) ) diff --git a/app/models/implicits/Elastic4sHitReaders.scala b/app/models/implicits/Elastic4sHitReaders.scala index 6dccb7b..63c1ae6 100644 --- a/app/models/implicits/Elastic4sHitReaders.scala +++ b/app/models/implicits/Elastic4sHitReaders.scala @@ -1,18 +1,25 @@ package models.implicits import com.sksamuel.elastic4s.{Hit, HitReader} -import models.entities.DNA.{Gene, Variant} -import models.entities.Entities.Study -import play.api.{Logger, Logging} -import play.api.libs.json.{JsError, JsResult, JsSuccess, Json, Reads} +import play.api.Logging +import play.api.libs.json.{JsError, JsPath, JsSuccess, Json, Reads} -import scala.util import scala.util.{Failure, Success, Try} -trait ElasticSearchEntity +sealed trait ElasticSearchEntity +case class GeneSearchResult(id: String) extends ElasticSearchEntity +case class VariantSearchResult(id: String) extends ElasticSearchEntity +case class StudySearchResult(id: String) extends ElasticSearchEntity object EsHitReader extends HitReader[ElasticSearchEntity] with Logging { + implicit val gsrReader: Reads[GeneSearchResult] = + (JsPath \ "gene_id").read[String].map(GeneSearchResult) + implicit val vsrReader: Reads[VariantSearchResult] = + (JsPath \ "variant_id").read[String].map(VariantSearchResult) + implicit val ssrReader: Reads[StudySearchResult] = + (JsPath \ "study_id").read[String].map(StudySearchResult) + def convertHit[T](hit: Hit)(implicit r: Reads[T]): Try[T] = Json.parse(hit.sourceAsString).validate[T] match { case JsSuccess(value, _) => Success(value) @@ -21,13 +28,11 @@ object EsHitReader extends HitReader[ElasticSearchEntity] with Logging { Failure(new RuntimeException(errors.mkString(" "))) } - override def read(hit: Hit): Try[ElasticSearchEntity] = { - import models.implicits.EsImplicits._ - + override def read(hit: Hit): Try[ElasticSearchEntity] = hit.index match { - case "genes" => convertHit[Gene](hit) - case "studies" => convertHit[Study](hit) - case s: String if s.contains("variant") => convertHit[Variant](hit) + case "genes" => convertHit[GeneSearchResult](hit) + case "studies" => convertHit[StudySearchResult](hit) + case s: String if s.contains("variant") => convertHit[VariantSearchResult](hit) case _ => Failure( new RuntimeException( @@ -36,6 +41,4 @@ object EsHitReader extends HitReader[ElasticSearchEntity] with Logging { ) } - } - } diff --git a/test/components/BackendSpec.scala b/test/components/BackendSpec.scala index 78860db..83946e5 100644 --- a/test/components/BackendSpec.scala +++ b/test/components/BackendSpec.scala @@ -219,10 +219,10 @@ class BackendSpec "Search expression yields results" taggedAs IntegrationTestTag in { // given - val searchExpr = "BRCA1" + val searchExpr = "ENSG00000012048" whenReady(backend.search(searchExpr, None))(r => - all(List(r.genes must have length (1), r.genes.head.symbol.get must be(searchExpr))) + all(List(r.genes must have length (1), r.genes.head.id must be(searchExpr))) ) }