From 8a0a4e57fba2abef27014a4fde5cec3c2a372eb4 Mon Sep 17 00:00:00 2001 From: Alessio Gallitano <25105748+galales@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:52:21 +0100 Subject: [PATCH] PIN-4743: Improve getAgreementEServices endpoint query --- .../api/impl/AgreementApiServiceImpl.scala | 2 +- .../readmodel/ReadModelAgreementQueries.scala | 98 ++++++++----------- .../common/readmodel/ReadModelId.scala | 13 +++ 3 files changed, 53 insertions(+), 60 deletions(-) create mode 100644 src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelId.scala diff --git a/src/main/scala/it/pagopa/interop/agreementprocess/api/impl/AgreementApiServiceImpl.scala b/src/main/scala/it/pagopa/interop/agreementprocess/api/impl/AgreementApiServiceImpl.scala index 474162d6..af784061 100644 --- a/src/main/scala/it/pagopa/interop/agreementprocess/api/impl/AgreementApiServiceImpl.scala +++ b/src/main/scala/it/pagopa/interop/agreementprocess/api/impl/AgreementApiServiceImpl.scala @@ -1293,7 +1293,7 @@ final case class AgreementApiServiceImpl( toEntityMarshallerProblem: ToEntityMarshaller[Problem] ): Route = authorize(ADMIN_ROLE, API_ROLE, SECURITY_ROLE, SUPPORT_ROLE) { val operationLabel = - s"Retrieving EServices with consumers $consumersIds, producers $producersIds" + s"Retrieving EServices with consumers $consumersIds, producers $producersIds, states $states, offset $offset, limit $limit" logger.info(operationLabel) val result: Future[CompactEServices] = for { diff --git a/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelAgreementQueries.scala b/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelAgreementQueries.scala index 13588300..69eec9c5 100644 --- a/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelAgreementQueries.scala +++ b/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelAgreementQueries.scala @@ -1,22 +1,22 @@ package it.pagopa.interop.agreementprocess.common.readmodel -import it.pagopa.interop.agreementprocess.common.Adapters._ import it.pagopa.interop.agreementmanagement.model.agreement.{PersistentAgreement, PersistentAgreementState} +import it.pagopa.interop.agreementmanagement.model.persistence.JsonFormats._ +import it.pagopa.interop.agreementprocess.api.impl._ +import it.pagopa.interop.agreementprocess.common.Adapters._ +import it.pagopa.interop.agreementprocess.model.{AgreementState, CompactEService, CompactOrganization} +import it.pagopa.interop.catalogmanagement.{model => CatalogManagement} import it.pagopa.interop.commons.cqrs.service.ReadModelService -import it.pagopa.interop.agreementprocess.model.{AgreementState, CompactEService} -import scala.concurrent.{ExecutionContext, Future} import org.mongodb.scala.Document import org.mongodb.scala.bson.conversions.Bson import org.mongodb.scala.model.Accumulators.first -import org.mongodb.scala.model.Aggregates.{`match`, count, lookup, project, sort, unwind, addFields, group} -import org.mongodb.scala.model.{Filters, Field, UnwindOptions} +import org.mongodb.scala.model.Aggregates._ import org.mongodb.scala.model.Projections.{computed, fields, include} import org.mongodb.scala.model.Sorts.ascending -import it.pagopa.interop.agreementmanagement.model.persistence.JsonFormats._ -import it.pagopa.interop.catalogmanagement.{model => CatalogManagement} -import it.pagopa.interop.agreementprocess.model.CompactOrganization -import it.pagopa.interop.agreementprocess.api.impl._ +import org.mongodb.scala.model.{Field, Filters, UnwindOptions} + import java.util.UUID +import scala.concurrent.{ExecutionContext, Future} object ReadModelAgreementQueries extends ReadModelQuery { @@ -298,23 +298,6 @@ object ReadModelAgreementQueries extends ReadModelQuery { } yield PaginatedResult(results = agreements, totalCount = count.headOption.map(_.totalCount).getOrElse(0)) } - private def listEServiceAgreementsFilters( - name: Option[String], - consumersIds: List[String], - producersIds: List[String], - states: List[AgreementState] - ): Bson = { - val nameFilter = name.map(n => Filters.regex("eservices.data.name", escape(n), "i")) - val consumersIdsFilter = mapToVarArgs(consumersIds.map(Filters.eq("data.consumerId", _)))(Filters.or) - val producersIdsFilter = mapToVarArgs(producersIds.map(Filters.eq("data.producerId", _)))(Filters.or) - val statesFilter = listStatesFilter(states) - - mapToVarArgs(nameFilter.toList ++ consumersIdsFilter.toList ++ producersIdsFilter.toList ++ statesFilter.toList)( - Filters.and - ) - .getOrElse(Filters.empty()) - } - def listEServicesAgreements( eServiceName: Option[String], consumersIds: List[String], @@ -323,45 +306,42 @@ object ReadModelAgreementQueries extends ReadModelQuery { offset: Int, limit: Int )(implicit ec: ExecutionContext, readModel: ReadModelService): Future[PaginatedResult[CompactEService]] = { - val query: Bson = listEServiceAgreementsFilters(eServiceName, consumersIds, producersIds, states) - val filterPipeline: Seq[Bson] = Seq( - lookup("eservices", "data.eserviceId", "data.id", "eservices"), - unwind("$eservices", UnwindOptions().preserveNullAndEmptyArrays(false)), - `match`(query), - group( - Document("""{ "_id": "$data.eserviceId" } """), - first("eserviceId", "$data.eserviceId"), - first("eserviceName", "$eservices.data.name") - ) + val agreementQuery: Bson = listAgreementsFilters( + eServicesIds = Nil, + consumersIds = consumersIds, + producersIds = producersIds, + descriptorsIds = Nil, + states = states, + showOnlyUpgradeable = false ) + def eserviceQuery(eserviceIds: Seq[String]): Bson = + `match`( + mapToVarArgs( + eServiceName.map(n => Filters.regex("data.name", escape(n), "i")).toList :+ + Filters.in("data.id", eserviceIds: _*) + )(Filters.and).getOrElse(Filters.empty()) + ) + for { - // Using aggregate to perform case insensitive sorting - // N.B.: Required because DocumentDB does not support collation - agreements <- readModel.aggregate[CompactEService]( - "agreements", - filterPipeline ++ - Seq( - project( - fields( - computed("data", Document("""{ "id": "$eserviceId", "name": "$eserviceName" }""")), - computed("lowerName", Document("""{ "$toLower" : "$eserviceName" }""")) - ) - ), - sort(ascending("lowerName")) + agreementEservicesIds <- readModel + .distinct[String]("agreements", "data.eserviceId", agreementQuery) + eservicesFilters = eserviceQuery(agreementEservicesIds) + eservices <- readModel.aggregate[CompactEService]( + "eservices", + Seq( + eservicesFilters, + project( + fields( + computed("data", Document("""{ "id": "$data.id", "name": "$data.name" }""")), + computed("lowerName", Document("""{ "$toLower" : "$data.name" }""")) + ) ), + sort(ascending("lowerName")) + ), offset = offset, limit = limit ) - // Note: This could be obtained using $facet function (avoiding to execute the query twice), - // but it is not supported by DocumentDB - count <- readModel.aggregate[TotalCountResult]( - "agreements", - filterPipeline ++ - Seq(count("totalCount"), project(computed("data", Document("""{ "totalCount" : "$totalCount" }""")))), - offset = 0, - limit = Int.MaxValue - ) - } yield PaginatedResult(results = agreements, totalCount = count.headOption.map(_.totalCount).getOrElse(0)) + } yield PaginatedResult(results = eservices, totalCount = agreementEservicesIds.size) } } diff --git a/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelId.scala b/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelId.scala new file mode 100644 index 00000000..b6e645d8 --- /dev/null +++ b/src/main/scala/it/pagopa/interop/agreementprocess/common/readmodel/ReadModelId.scala @@ -0,0 +1,13 @@ +package it.pagopa.interop.agreementprocess.common.readmodel + +import it.pagopa.interop.commons.queue.message.Message.uuidFormat +import spray.json.DefaultJsonProtocol._ +import spray.json.RootJsonFormat + +import java.util.UUID + +final case class ReadModelId(id: UUID) + +object ReadModelId { + implicit val rmiFormat: RootJsonFormat[ReadModelId] = jsonFormat1(ReadModelId.apply) +}