使用Realm SDK从MongoDB Atlas获取数据返回空响应问题排查求助
问题:Google One Tap登录+JWT认证下,Realm SDK从MongoDB Atlas获取日记数据返回空响应
我的Android应用采用Google One Tap登录与JWT认证,通过Realm SDK从MongoDB Atlas拉取日记数据时始终得到空响应。已经尝试过删除重建数据库、重启Realm同步等操作,问题仍未解决。
Diary模型
package com.krish.diaryapp.model import io.realm.kotlin.ext.realmListOf import io.realm.kotlin.types.ObjectId import io.realm.kotlin.types.RealmInstant import io.realm.kotlin.types.RealmList import io.realm.kotlin.types.RealmObject import io.realm.kotlin.types.annotations.PrimaryKey open class Diary : RealmObject { @PrimaryKey var _id: ObjectId = ObjectId.create() var ownerId: String = "" var mood: String = Mood.Neutral.name var title: String = "" var description: String = "" var images: RealmList<String> = realmListOf() var date: RealmInstant = RealmInstant.from(System.currentTimeMillis(), 0) }
RequestState密封类
package com.krish.diaryapp.util sealed class RequestState<out T> { object Idle : RequestState<Nothing>() object Loading : RequestState<Nothing>() data class Success<T>(val data: T) : RequestState<T>() data class Error(val error: Throwable) : RequestState<Nothing>() }
MongoRepository接口
package com.krish.diaryapp.data.repository import com.krish.diaryapp.model.Diary import com.krish.diaryapp.util.RequestState import kotlinx.coroutines.flow.Flow import java.time.LocalDate typealias Diaries = RequestState<Map<LocalDate, List<Diary>>> interface MongoRepository { fun configureTheRealm() fun getAllDiaries(): Flow<Diaries> }
MongoDB实现类
package com.krish.diaryapp.data.repository import android.util.Log import com.krish.diaryapp.model.Diary import com.krish.diaryapp.util.Constants.APP_ID import com.krish.diaryapp.util.RequestState import com.krish.diaryapp.util.toInstant import io.realm.kotlin.Realm import io.realm.kotlin.ext.query import io.realm.kotlin.log.LogLevel import io.realm.kotlin.mongodb.App import io.realm.kotlin.mongodb.sync.SyncConfiguration import io.realm.kotlin.query.Sort import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import java.time.ZoneId object MongoDB : MongoRepository { private val app = App.create(APP_ID) private val user = app.currentUser private lateinit var realm: Realm init { configureTheRealm() } override fun configureTheRealm() { if (user != null) { val config = SyncConfiguration.Builder(user, setOf(Diary::class)) .initialSubscriptions { sub -> add( query = sub.query<Diary>(query = "ownerId == $0", user.identity), name = "User's Diaries" ) } .log(LogLevel.ALL) .build() realm = Realm.open(config) } } override fun getAllDiaries(): Flow<Diaries> { return if (user != null) { try { realm.query<Diary>(query = "ownerId == $0", user.identity) .sort(property = "date", sortOrder = Sort.DESCENDING) .asFlow() .map { result -> RequestState.Success( data = result.list.groupBy { it.date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate() } ) } } catch (e: Exception) { flow { emit(RequestState.Error(e)) } } } else { flow { emit(RequestState.Error(UserNotAuthenticatedException())) } } } } private class UserNotAuthenticatedException : Exception("User is not Logged in.")
HomeViewModel
package com.krish.diaryapp.presentation.screens.home import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.krish.diaryapp.data.repository.Diaries import com.krish.diaryapp.data.repository.MongoDB import com.krish.diaryapp.util.RequestState import kotlinx.coroutines.launch class HomeViewModel : ViewModel() { var diaries: MutableState<Diaries> = mutableStateOf(RequestState.Idle) init { observeAllDiaries() } private fun observeAllDiaries() { viewModelScope.launch { MongoDB.getAllDiaries().collect { result -> diaries.value = result } } } }
MongoDB Atlas Schema
{ "title": "Diary", "bsonType": "object", "required": [ "_id", "ownerId", "mood", "title", "description", "date" ], "properties": { "_id": { "bsonType": "objectId" }, "date": { "bsonType": "date" }, "images": { "bsonType": "array", "items": { "bsonType": "string" } }, "description": { "bsonType": "string" }, "mood": { "bsonType": "string" }, "ownerId": { "bsonType": "string" }, "title": { "bsonType": "string" } } }
已完成的排查操作
- 配置MongoDB Atlas数据库访问规则
- 查看查询日志确认请求状态
- 确认集合中存在目标用户的日记数据
- 尝试删除重建数据库、重启Realm同步服务
排查方向与解决方案
1. 验证User Identity与ownerId匹配
检查Atlas集合中ownerId字段的值,是否和user.identity完全一致(包括大小写、空格、格式)。可以在代码中添加日志输出对比:
Log.d("MongoDB_DEBUG", "当前用户Identity: ${user?.identity}")
2. 修复Realm订阅同步逻辑
当前订阅配置未等待远程数据同步完成,添加waitForInitialRemoteData()确保启动时拉取完所有数据:
val config = SyncConfiguration.Builder(currentUser, setOf(Diary::class)) .initialSubscriptions { sub -> add( query = sub.query<Diary>(query = "ownerId == $0", currentUser.identity), name = "User's Diaries" ) } .waitForInitialRemoteData() // 新增:等待远程数据同步完成 .log(LogLevel.ALL) .build()
3. 修正Realm实例初始化时机
单例初始化时currentUser可能未赋值(Google登录是异步操作),改为实时获取用户并在登录成功后主动初始化:
override fun configureTheRealm() { val currentUser = app.currentUser if (currentUser != null && currentUser.isLoggedIn) { // 原有配置逻辑 } } // 在Google One Tap登录成功的回调中调用: MongoDB.configureTheRealm()
4. 检查数据库访问规则权限
确保Atlas规则允许用户查询自己的数据,规则示例:
{ "read": {"ownerId": "%%user.id"}, "write": {"ownerId": "%%user.id"} }
注意%%user.id要和你存储的ownerId来源匹配(如果用的是Google用户的identity,需调整规则变量)。
5. 排查日期分组逻辑是否导致空结果
在查询结果中添加日志,确认原始数据数量:
.map { result -> Log.d("MongoDB_DEBUG", "拉取到的日记数量: ${result.list.size}") RequestState.Success( data = result.list.groupBy { it.date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate() } ) }
如果原始数量大于0但分组后为空,检查toInstant()扩展函数的实现是否正确。
6. 验证用户认证状态
确保app.currentUser是已登录的有效用户,在查询前增加状态判断:
override fun getAllDiaries(): Flow<Diaries> { val currentUser = app.currentUser return if (currentUser != null && currentUser.isLoggedIn) { // 原有查询逻辑 } else { flow { emit(RequestState.Error(UserNotAuthenticatedException())) } } }
内容的提问来源于stack exchange,提问作者Krish Vachhani
相关产品推荐
相关产品推荐

