如何从 Firestore 获取与另一个 LiveData 的内容相匹配的项目列表的 LiveData?

问题描述 投票:0回答:1

我的 Android 应用程序使用 Firebase Firestore。我有用户的集合,也有成就的集合。每个成就都有一个所有者列表,它们是用户集合的文档 ID。在我的 ViewModel 中,我有一个

LiveData<Achievement>
对象,并且我希望有一个
LiveData<List<User>>
对象,其中包含成就的owners 属性中列出的确切用户。

目前有效:

class AchievementDetailsViewModel(private val achievementId: String) : ViewModel() {
    private val _achievement: MediatorLiveData<Achievement> = MediatorLiveData()
    public val achievement: LiveData<Achievement> get() = _achievement

    init {
        fetchAchievementData()
    }

    private fun fetchAchievementData() {
        viewModelScope.launch {
            val achievementLiveData = AchievementService.getAchievement(achievementId)
            _achievement.addSource(achievementLiveData) {
                _achievement.value = it
            }
        }
    }

}

object AchievementService {
    fun getAchievement(id: String): LiveData<Achievement> {
        return Firebase.firestore.collection(Collections.ACHIEVMENTS).document(id)
            .snapshots()
            .map { s -> s.toObject(Achievement::class.java)!! }
            .asLiveData()
    }
}

这样我就可以访问成就,并且

LiveData
按预期更新。

要将用户列表添加到 ViewModel,我尝试添加以下代码:

class AchievementDetailsViewModel(private val achievementId: String) : ViewModel() {
    private val _achievement: MediatorLiveData<Achievement> = MediatorLiveData()
    public val achievement: LiveData<Achievement> get() = _achievement
    private val _owners: MediatorLiveData<List<User>> = MediatorLiveData(emptyList())
    public val owners: LiveData<List<User>> get() = _owners


    init {
        fetchAchievementData()
        fetchOwnerData()
    }

    private fun fetchOwnerData() {
        viewModelScope.launch {
            _achievement.observeForever { achievement ->
                val ownersLiveData = UserService.getUsers(achievement.ownerIds)
                _owners.addSource(ownersLiveData) { _owners.value = it }
            }
        }
    }
}

object UserService {
    fun getUsers(userIds: List<String>): LiveData<List<User>> {
        return Firebase.firestore.collection(Collections.USERS)
            .whereIn(FieldPath.documentId(), userIds)
            .snapshots()
            .map { s -> s.toObjects(User::class.java) }
            .asLiveData()
    }
}

这不起作用,因为第一次调用

getUsers
时,
userIds
参数是一个空列表。这不是
whereIn
调用的合法参数,该调用期望列表仅包含有效的 documentId,至少一个。我尝试按以下方式更改方法:

fun getUsers(userIds: List<String>): LiveData<List<User>> {
    if (userIds.isEmpty()) {
        return MutableLiveData(ListOf())
    }
    return Firebase.firestore.collection(Collections.USERS)
        .whereIn(FieldPath.documentId(), userIds)
        .snapshots()
        .map { s -> s.toObjects(User::class.java) }
        .asLiveData()
}

这里的问题是,第一次调用这个函数时,它返回一个空列表的

LiveData
,而这个
LiveData
对象将始终代表那个不变的空列表。我确实尝试了其他一些事情,但这些尝试也归结为在某个地方有一个条件来处理空列表的情况,而我总是得到一个空列表的
LiveData
。遗憾的是,等待
_achievement.value.ownerIds.isNotEmpty()
不是一个选择,因为该成就的所有者列表可能是一个空列表。我在这里缺少什么?难道没有一种简单的方法来处理
whereIn
调用中空列表的情况吗?

android kotlin google-cloud-firestore android-livedata android-viewmodel
1个回答
0
投票

userIds 参数是一个空列表。这不是 whereIn 调用的合法参数,它期望列表仅包含有效的 documentId,至少一个

这种说法并不完全正确。您不必仅提供有效的文档 ID。您可以提供任何您想要的字符串。我不知道如果您传递一个空列表会发生什么,但听起来您是在说这是一个错误。

如果您还没有要过滤的用户列表,并且无法传递空列表,则只需传递包含空字符串的单个元素的列表。它永远不会匹配任何文档,但至少查询应该是有效的。

fun getUsers(userIds: List<String>): LiveData<List<User>> {
    if (userIds.isEmpty()) {
        return MutableLiveData(ListOf(""))  // list of a single empty string
    }
    return Firebase.firestore.collection(Collections.USERS)
        .whereIn(FieldPath.documentId(), userIds)
        .snapshots()
        .map { s -> s.toObjects(User::class.java) }
        .asLiveData()
}
© www.soinside.com 2019 - 2024. All rights reserved.