lila/modules/study/src/main/StudyRepo.scala

265 lines
8.0 KiB
Scala
Raw Normal View History

2016-02-26 05:08:11 -07:00
package lila.study
import org.joda.time.DateTime
2019-12-13 07:30:20 -07:00
import reactivemongo.akkastream.{ cursorProducer, AkkaStreamCursor }
import reactivemongo.api._
2016-02-26 05:08:11 -07:00
import lila.db.AsyncColl
2016-04-12 02:44:50 -06:00
import lila.db.dsl._
2016-02-26 05:08:11 -07:00
import lila.user.User
final class StudyRepo(private[study] val coll: AsyncColl)(implicit ec: scala.concurrent.ExecutionContext) {
2016-02-26 05:08:11 -07:00
import BSONHandlers._
2019-07-13 12:24:14 -06:00
private object F {
2019-12-13 07:30:20 -07:00
val uids = "uids"
val likers = "likers"
val views = "views"
val rank = "rank"
val likes = "likes"
2020-02-24 13:07:08 -07:00
val topics = "topics"
2019-07-13 12:24:14 -06:00
val createdAt = "createdAt"
}
2016-05-26 14:01:43 -06:00
private[study] val projection = $doc(
2019-12-13 07:30:20 -07:00
F.uids -> false,
2019-07-13 12:24:14 -06:00
F.likers -> false,
2019-12-13 07:30:20 -07:00
F.views -> false,
F.rank -> false
)
2016-05-26 14:01:43 -06:00
private[study] val lightProjection = $doc(
2019-12-13 07:30:20 -07:00
"_id" -> false,
"visibility" -> true,
2019-12-13 07:30:20 -07:00
"members" -> true
)
def byId(id: Study.Id) = coll(_.find($id(id), projection.some).one[Study])
2016-02-26 05:08:11 -07:00
def byOrderedIds(ids: Seq[Study.Id]) = coll(_.byOrderedIds[Study, Study.Id](ids)(_.id))
2016-07-25 03:11:39 -06:00
def lightById(id: Study.Id): Fu[Option[Study.LightStudy]] =
coll(_.find($id(id), lightProjection.some).one[Study.LightStudy])
2019-12-03 21:40:01 -07:00
def sortedCursor(
2019-12-13 07:30:20 -07:00
selector: Bdoc,
sort: Bdoc,
readPreference: ReadPreference = ReadPreference.secondaryPreferred
): Fu[AkkaStreamCursor[Study]] =
coll.map(_.find(selector).sort(sort).cursor[Study](readPreference))
2016-07-25 03:11:39 -06:00
def exists(id: Study.Id) = coll(_.exists($id(id)))
2016-02-26 05:08:11 -07:00
2019-12-13 07:30:20 -07:00
private[study] def selectOwnerId(ownerId: User.ID) = $doc("ownerId" -> ownerId)
2020-05-05 22:11:15 -06:00
private[study] def selectMemberId(memberId: User.ID) = $doc(F.uids -> memberId)
2019-12-13 07:30:20 -07:00
private[study] val selectPublic = $doc(
"visibility" -> VisibilityHandler.writeTry(Study.Visibility.Public).get
)
private[study] val selectPrivateOrUnlisted = "visibility" $ne VisibilityHandler
.writeTry(Study.Visibility.Public)
.get
2019-07-13 12:24:14 -06:00
private[study] def selectLiker(userId: User.ID) = $doc(F.likers -> userId)
2017-03-29 18:18:32 -06:00
private[study] def selectContributorId(userId: User.ID) =
selectMemberId(userId) ++ // use the index
$doc("ownerId" $ne userId) ++
$doc(s"members.$userId.role" -> "w")
2020-02-24 13:07:08 -07:00
private[study] def selectTopic(topic: StudyTopic) = $doc(F.topics -> topic)
2016-05-11 03:20:19 -06:00
def countByOwner(ownerId: User.ID) = coll(_.countSel(selectOwnerId(ownerId)))
2016-05-11 03:20:19 -06:00
2019-12-13 07:30:20 -07:00
def insert(s: Study): Funit =
coll {
_.insert.one {
StudyBSONHandler.writeTry(s).get ++ $doc(
F.uids -> s.members.ids,
F.likers -> List(s.ownerId),
F.rank -> Study.Rank.compute(s.likes, s.createdAt)
)
}
2019-12-13 07:30:20 -07:00
}.void
def updateSomeFields(s: Study): Funit =
coll {
_.update
.one(
$id(s.id),
$set(
"position" -> s.position,
"name" -> s.name,
"settings" -> s.settings,
"visibility" -> s.visibility,
"description" -> ~s.description,
"updatedAt" -> DateTime.now
)
2019-12-13 07:30:20 -07:00
)
}.void
2020-02-21 12:27:22 -07:00
def updateTopics(s: Study): Funit =
coll {
_.update
.one(
$id(s.id),
$set("topics" -> s.topics, "updatedAt" -> DateTime.now)
)
}.void
2020-02-21 12:27:22 -07:00
def delete(s: Study): Funit = coll(_.delete.one($id(s.id))).void
2016-05-12 08:21:25 -06:00
def deleteByIds(ids: List[Study.Id]): Funit = coll(_.delete.one($inIds(ids))).void
def membersById(id: Study.Id): Fu[Option[StudyMembers]] =
coll(_.primitiveOne[StudyMembers]($id(id), "members"))
2016-04-20 04:19:34 -06:00
def setPosition(studyId: Study.Id, position: Position.Ref): Funit =
coll(
_.update
.one(
$id(studyId),
$set(
"position" -> position,
"updatedAt" -> DateTime.now
)
2019-12-13 07:30:20 -07:00
)
).void
2016-05-27 04:04:16 -06:00
def incViews(study: Study) = coll.map(_.incFieldUnchecked($id(study.id), F.views))
2016-04-20 04:19:34 -06:00
2016-05-27 04:04:16 -06:00
def updateNow(s: Study) =
coll.map(_.updateFieldUnchecked($id(s.id), "updatedAt", DateTime.now))
2016-05-20 10:13:31 -06:00
2016-04-20 22:42:49 -06:00
def addMember(study: Study, member: StudyMember): Funit =
coll {
_.update
.one(
$id(study.id),
$set(s"members.${member.id}" -> member) ++ $addToSet(F.uids -> member.id)
)
}.void
2016-04-20 22:42:49 -06:00
def removeMember(study: Study, userId: User.ID): Funit =
coll {
_.update
.one(
$id(study.id),
$unset(s"members.$userId") ++ $pull(F.uids -> userId)
)
}.void
2016-04-20 22:42:49 -06:00
2016-04-21 20:53:16 -06:00
def setRole(study: Study, userId: User.ID, role: StudyMember.Role): Funit =
coll {
_.update
.one(
$id(study.id),
$set(s"members.$userId.role" -> role)
)
}.void
2016-05-22 04:11:32 -06:00
2019-07-13 12:24:14 -06:00
def uids(studyId: Study.Id): Fu[Set[User.ID]] =
coll(_.primitiveOne[Set[User.ID]]($id(studyId), F.uids)) dmap (~_)
2016-05-26 14:01:43 -06:00
private val idNameProjection = $doc("name" -> true)
2017-07-21 04:51:56 -06:00
def publicIdNames(ids: List[Study.Id]): Fu[List[Study.IdName]] =
coll(_.find($inIds(ids) ++ selectPublic, idNameProjection.some).cursor[Study.IdName]().list())
2017-07-21 04:49:54 -06:00
def recentByOwner(userId: User.ID, nb: Int) =
coll {
_.find(selectOwnerId(userId), idNameProjection.some)
.sort($sort desc "updatedAt")
.cursor[Study.IdName](ReadPreference.secondaryPreferred)
.list(nb)
}
// heavy AF. Only use for GDPR.
private[study] def allIdsByOwner(userId: User.ID): Fu[List[Study.Id]] =
coll(_.distinctEasy[Study.Id, List]("_id", selectOwnerId(userId), ReadPreference.secondaryPreferred))
def recentByContributor(userId: User.ID, nb: Int) =
coll {
_.find(selectContributorId(userId), idNameProjection.some)
.sort($sort desc "updatedAt")
.cursor[Study.IdName](ReadPreference.secondaryPreferred)
.list(nb)
}
2017-06-10 11:48:04 -06:00
def isContributor(studyId: Study.Id, userId: User.ID) =
coll(_.exists($id(studyId) ++ $doc(s"members.$userId.role" -> "w")))
2017-06-10 11:48:04 -06:00
2018-08-12 11:21:55 -06:00
def isMember(studyId: Study.Id, userId: User.ID) =
coll(_.exists($id(studyId) ++ (s"members.$userId" $exists true)))
2018-08-12 11:21:55 -06:00
def like(studyId: Study.Id, userId: User.ID, v: Boolean): Fu[Study.Likes] =
2017-04-03 18:21:00 -06:00
countLikes(studyId).flatMap {
case None => fuccess(Study.Likes(0))
2017-04-03 18:21:00 -06:00
case Some((prevLikes, createdAt)) =>
val likes = Study.Likes(prevLikes.value + (if (v) 1 else -1))
coll {
_.update.one(
$id(studyId),
$set(
F.likes -> likes,
F.rank -> Study.Rank.compute(likes, createdAt)
) ++ {
if (v) $addToSet(F.likers -> userId) else $pull(F.likers -> userId)
}
) inject likes
}
2016-05-26 14:01:43 -06:00
}
def liked(study: Study, user: User): Fu[Boolean] =
coll(_.exists($id(study.id) ++ selectLiker(user.id)))
2016-05-26 14:01:43 -06:00
def filterLiked(user: User, studyIds: Seq[Study.Id]): Fu[Set[Study.Id]] =
2019-12-11 12:38:44 -07:00
studyIds.nonEmpty ??
coll(_.primitive[Study.Id]($inIds(studyIds) ++ selectLiker(user.id), "_id").dmap(_.toSet))
2016-05-27 06:40:05 -06:00
2019-12-13 07:30:20 -07:00
def resetAllRanks: Fu[Int] =
coll {
_.find(
2019-12-13 07:30:20 -07:00
$empty,
$doc(F.likes -> true, F.createdAt -> true).some
)
.cursor[Bdoc]()
.foldWhileM(0) { (count, doc) =>
~(for {
id <- doc.getAsOpt[Study.Id]("_id")
likes <- doc.getAsOpt[Study.Likes](F.likes)
createdAt <- doc.getAsOpt[DateTime](F.createdAt)
} yield coll {
_.update
.one(
$id(id),
$set(F.rank -> Study.Rank.compute(likes, createdAt))
)
}.void) inject Cursor.Cont(count + 1)
}
}
2020-03-02 13:23:03 -07:00
private[study] def isAdminMember(study: Study, userId: User.ID): Fu[Boolean] =
coll(_.exists($id(study.id) ++ $doc(s"members.$userId.admin" -> true)))
2020-03-02 13:23:03 -07:00
private def countLikes(studyId: Study.Id): Fu[Option[(Study.Likes, DateTime)]] =
coll {
_.aggregateWith[Bdoc]() { framework =>
2019-12-13 07:30:20 -07:00
import framework._
2020-07-19 08:27:36 -06:00
List(
Match($id(studyId)),
2019-12-13 07:30:20 -07:00
Project(
$doc(
"_id" -> false,
F.likes -> $doc("$size" -> s"$$${F.likers}"),
F.createdAt -> true
)
)
)
}.headOption
}.map { docOption =>
for {
doc <- docOption
likes <- doc.getAsOpt[Study.Likes](F.likes)
createdAt <- doc.getAsOpt[DateTime](F.createdAt)
} yield likes -> createdAt
}
2016-02-26 05:08:11 -07:00
}