【问题标题】:how to filter flow returned by one repository by another repository without blocking?如何在不阻塞的情况下过滤一个存储库返回的另一个存储库的流?
【发布时间】:2021-07-29 15:01:53
【问题描述】:

我有一个服务可以获取用户的所有朋友,它应该过滤掉被用户阻止或阻止用户的用户。

这是我目前的方法。有两个存储库。

友情信息:

interface FriendshipRepository : CoroutineCrudRepository<Friendship, Long> {
  fun findByUserId(userId: Long): Flow<Friendship>
}

还有一个用于块信息:

interface BlockRepository : CoroutineCrudRepository<Block, Long> {
  fun findByUserIdAndFriendIdIn(userId: Long, friendId: Set<Long>): Flow<Block>
  fun findByUserIdInAndFriendId(userId: Set<Long>, friendId: Long): Flow<Block>
}

问题

我有两个问题。

  1. 在查询blockRepository之前,我需要阻止收集朋友ID。
  2. 我不知道如何过滤原始流而不阻塞blockRepository的结果,因为我需要知道所有块。

此实现有效 - 由于 asFluxblock()!! 操作,我觉得它看起来很奇怪,但我想不出更好的解决方案:

override suspend fun findFriendIdsByUserId(userId: Long): Flow<Long> {
  val friends = friendshipRepository.findByUserId(userId)
    .filter { it.status == FRIEND }
    .map { it.friendId }
    .asFlux()
    .collectList()
    .block()!!
    .toMutableSet()

  val userBlocks = blockRepository.findByUserIdAndFriendIdIn(userId, friends)
  val userIsBlockedBy = blockRepository.findByUserIdInAndFriendId(friends, userId)

  val blocks =  userBlocks.asFlux().mergeWith(userIsBlockedBy.asFlux())
    .map { it.friendId }
    .collectList()
    .block()!!
    .toSet()
  friends.removeAll(blocks)
  return friends.asFlow()
}

有更好的方法吗?

【问题讨论】:

  • 为什么findByUserId会返回一个流?它实际上是返回可观察数据还是只是一个包装器?
  • 另一个控制器/服务使用相同的方法向前端提供好友列表(包括被屏蔽的好友)。它更像是一个包装器,但我认为让它返回 Set 会与使用反应弹簧堆栈相矛盾。
  • @Stuck 只是出于好奇,如果您已经有了流,为什么还要到处使用.asFlux() 而不是使用Flows?
  • @Joffrey:因为流没有合并运算符。但我很乐意让它们保持流动!
  • 嗯,merge(vararg Flow) 呢?

标签: kotlin spring-webflux kotlin-coroutines


【解决方案1】:

我认为至少您可以通过依赖Flow 的挂起运算符(如toSet() 而不是Fluxblock)来避免阻塞:

override suspend fun findFriendIdsByUserId(userId: Long): Flow<Long> {
  val friends = friendshipRepository.findByUserId(userId)
    .filter { it.status == FRIEND }
    .map { it.friendId }
    .toSet()

  val userBlocks = blockRepository.findByUserIdAndFriendIdIn(userId, friends)
  val userIsBlockedBy = blockRepository.findByUserIdInAndFriendId(friends, userId)

  val blocks = merge(userBlocks, userIsBlockedBy)
    .map { it.friendId }
    .toSet()

  return (friends - blocks).asFlow()
}

但是由于您需要知道blocks 的所有值才能开始过滤,并且您需要所有friends 才能知道blocks,我不确定您是否可以在不重新设计的情况下做得更好数据库。

【讨论】:

  • 我觉得答案很好,我可以摆脱实验性的merge by .asFlux().mergeWith(b.asFlux()).asFlow()
  • 老实说,如果你最终得到Set,你甚至根本不需要merge(),你可以直接将2个结果集加在一起
  • 是的,而且还是有一个错误,因为userBlocks 和 `userIsBlockedBy' 的过滤器不同,所以我立即将它们转换为集合,然后添加它们。
猜你喜欢
  • 2021-01-27
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
  • 2016-12-03
  • 1970-01-01
  • 2019-08-04
  • 2015-09-14
  • 1970-01-01
相关资源
最近更新 更多