Skip to content

Commit

Permalink
Add endpoint to get workset volumes by workset ID using volume-level …
Browse files Browse the repository at this point in the history
…aggregated collection.
  • Loading branch information
sandeep-ps committed May 16, 2024
1 parent 79cd201 commit e654808
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 1 deletion.
14 changes: 14 additions & 0 deletions app/controllers/EfController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ class EfController @Inject()(efRepository: EfRepository,
}
}

def getWorksetVolumesAggregated(@ApiParam(value = "the workset ID", required = true) wid: WorksetId,
@ApiParam(value = "comma-separated list of fields to return") fields: Option[String]): Action[AnyContent] = {
//TODO: Add query parameter to specify whether to include POS information if that data becomes available in the future
Action.async { implicit req =>
render.async {

Check warning on line 109 in app/controllers/EfController.scala

View check run for this annotation

Codecov / codecov/patch

app/controllers/EfController.scala#L108-L109

Added lines #L108 - L109 were not covered by tests
case Accepts.Json() =>
efRepository
.getWorkset(wid)
.flatMap(workset => efRepository.getVolumesAggregated(workset.htids, withPos = false, fields.map(tokenize(_)).getOrElse(List.empty)))
.map(WrappedResponse(_))
}
}
}

def getWorksetVolumesMetadata(@ApiParam(value = "the workset ID", required = true) wid: WorksetId,
@ApiParam(value = "comma-separated list of fields to return") fields: Option[String]): Action[AnyContent] =
Action.async { implicit req =>
Expand Down
3 changes: 3 additions & 0 deletions app/exceptions/VolumeNotFoundException.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ package exceptions

case class VolumeNotFoundException(id: String, cause: Throwable = null)
extends ResourceNotFoundException(s"Volume $id not found", cause)

case class NotImplementedException(msg: String, cause: Throwable = null)
extends RuntimeException(msg, cause)
1 change: 1 addition & 0 deletions app/repo/EfRepository.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ trait EfRepository {
def hasVolume(id: VolumeId): Future[Boolean]
def getVolume(id: VolumeId, withPos: Boolean = true, fields: List[String] = List.empty): Future[JsObject]
def getVolumes(ids: IdSet, withPos: Boolean = true, fields: List[String] = List.empty): Future[List[JsObject]]
def getVolumesAggregated(ids: IdSet, withPos: Boolean = false, fields: List[String] = List.empty): Future[List[JsObject]]
def getVolumePages(id: VolumeId, pageSeqs: Option[PageSet] = None, withPos: Boolean = true, fields: List[String] = List.empty): Future[JsObject]

def getVolumeMetadata(id: VolumeId, fields: List[String] = List.empty): Future[JsObject]
Expand Down
78 changes: 77 additions & 1 deletion app/repo/EfRepositoryMongoImpl.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package repo

import akka.stream.Materializer
import exceptions.{VolumeNotFoundException, WorksetNotFoundException}
import exceptions.{NotImplementedException, VolumeNotFoundException, WorksetNotFoundException}
import play.api.Logging
import play.api.libs.json._
import reactivemongo.api.bson._
Expand Down Expand Up @@ -29,6 +29,8 @@ class EfRepositoryMongoImpl @Inject()(val reactiveMongoApi: ReactiveMongoApi)
reactiveMongoApi.database.map(_.collection[BSONCollection]("ef"))
protected def featuresCol: Future[BSONCollection] =
reactiveMongoApi.database.map(_.collection[BSONCollection]("features"))
protected def featuresAggNoPosCol: Future[BSONCollection] =
reactiveMongoApi.database.map(_.collection[BSONCollection]("featuresAggNoPos"))

Check warning on line 33 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L33

Added line #L33 was not covered by tests
protected def metadataCol: Future[BSONCollection] =
reactiveMongoApi.database.map(_.collection[BSONCollection]("metadata"))
protected def pagesCol: Future[BSONCollection] =
Expand All @@ -51,6 +53,9 @@ class EfRepositoryMongoImpl @Inject()(val reactiveMongoApi: ReactiveMongoApi)
override def getVolumes(ids: IdSet, withPos: Boolean = true, fields: List[String] = List.empty): Future[List[JsObject]] =
if (withPos) getVolumesWithPos(ids, fields) else getVolumesNoPos(ids, fields)

override def getVolumesAggregated(ids: IdSet, withPos: Boolean = false, fields: List[String] = List.empty): Future[List[JsObject]] =
if (withPos) getVolumesAggregatedWithPos(ids, fields) else getVolumesAggregatedNoPos(ids, fields)

Check warning on line 57 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L57

Added line #L57 was not covered by tests

protected def getVolumeWithPos(id: VolumeId, fields: List[String] = List.empty): Future[JsObject] =
getVolumesWithPos(Set(id), fields).map {
case vol :: Nil => vol
Expand Down Expand Up @@ -484,6 +489,77 @@ class EfRepositoryMongoImpl @Inject()(val reactiveMongoApi: ReactiveMongoApi)
} yield volumes
}

// Reference MongoDB Query for getVolumesAggregatedNoPos
// db.getCollection("metadata").aggregate([
// {
// $match: {
// htid: { $in: ['gri.ark:/13960/t1fj9k903'] }
// }
// },
// {
// $lookup: {
// from: 'featuresAggNoPos',
// let: { htid: '$htid' },
// pipeline: [
// {
// $match: {
// $expr: {
// $eq: ['$htid', '$$htid']
// }
// }
// },
// { $project: { _id: 0 } },
// { $replaceRoot: { newRoot: '$features' } }
// ],
// as: 'features'
// }
// },
// {
// $unwind: '$features'
// },
// {
// $project: {
// _id: 0
// }
// }
// ])
private def getVolumesAggregatedNoPos(ids: IdSet, fields: List[String] = List.empty): Future[List[JsObject]] = {

val projFields = BSONDocument(fields.map(f => f -> BSONInteger(1)))

Check warning on line 528 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L528

Added line #L528 was not covered by tests

for {
col <- efCol; features <- featuresAggNoPosCol; metadata <- metadataCol;
volumes <- metadata
.aggregateWith[JsObject]() { framework =>

Check warning on line 533 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L531-L533

Added lines #L531 - L533 were not covered by tests
import framework._

val query = if (ids.isEmpty) document() else document("htid" -> document("$in" -> ids))

Check warning on line 536 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L536

Added line #L536 was not covered by tests

List(
Match(query),
LookupPipeline(
from = features.name,
let = document("htid" -> "$htid"),
pipeline = List(
Match(document("$expr" -> document("$eq" -> List("$htid", """$$htid""")))),
Project(document("_id" -> 0)),
ReplaceRootField("features")

Check warning on line 546 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L538-L546

Added lines #L538 - L546 were not covered by tests
),
as = "features"

Check warning on line 548 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L548

Added line #L548 was not covered by tests
),
UnwindField("features"),
Project(document("_id" -> 0) ++ projFields)

Check warning on line 551 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L550-L551

Added lines #L550 - L551 were not covered by tests
)
}
.collect[List]()

Check warning on line 554 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L554

Added line #L554 was not covered by tests
} yield volumes
}

//TODO: Implement this method after the aggregated collection with POS information is available
private def getVolumesAggregatedWithPos(ids: IdSet, fields: List[String] = List.empty): Future[List[JsObject]] = {
throw NotImplementedException("Volume level aggregation with POS information not implemented yet.")

Check warning on line 560 in app/repo/EfRepositoryMongoImpl.scala

View check run for this annotation

Codecov / codecov/patch

app/repo/EfRepositoryMongoImpl.scala#L560

Added line #L560 was not covered by tests
}

override def getVolumeMetadata(id: VolumeId, fields: List[String] = List.empty): Future[JsObject] = {
val ids = Set(id)
getVolumesMetadata(ids, fields).map {
Expand Down
1 change: 1 addition & 0 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ POST /worksets controllers.EfController.createWorkset()
GET /worksets/:id controllers.EfController.getWorkset(id)
DELETE /worksets/:id controllers.EfController.deleteWorkset(id)
GET /worksets/:id/volumes controllers.EfController.getWorksetVolumes(id, pos: Boolean ?= true, fields: Option[String] ?= None)
GET /worksets/:id/volumes/aggregated controllers.EfController.getWorksetVolumesAggregated(id, fields: Option[String] ?= None)
GET /worksets/:id/metadata controllers.EfController.getWorksetVolumesMetadata(id, fields: Option[String] ?= None)

0 comments on commit e654808

Please sign in to comment.