我遇到了情况,Firebase 推送密钥 ID 不按时间顺序排列。
在他的回答的评论中,@FrankvanPuffelen 说
如果您遇到无法容忍此类问题的场景, 您还需要在子节点中写入服务器端时间戳, 并根据该值对结果进行排序
我当前的代码:
fun sendMessage(fromId: String, toId: String) {
val messageId = Firebase.database.reference.child("messages").push().key.toString()
val messageMap = mutableMapOf<String, Any>()
messageMap["timeStamp"] = System.currentTimeMillis()
messageMap["message"] = "..."
messageMap["fromId"] = fromId
messageMap["toId"] = toId
messageMap["messageId"] = messageId
val messagesRef = "/messages/$messageId"
val fromIdRef = "/user-messages/$fromId/$toId/$messageId"
val toIdRef = "/user-messages/$toId/$fromId/$messageId"
val atomicMap = mutableMapOf<String, Any>()
atomicMap[messagesRef] = messageMap
atomicMap[fromIdRef] = 1
atomicMap[toIdRef] = 1
val rootDbRef = Firebase.database.reference
rootDbRef.updateChildren(atomicMap)
}
结果是:
-messages
-NseAh8_4jwiZ9CR9XXw
message: "..."
messageId: "-NseAh8_4jwiZ9CR9XXw"
timeStamp: 1696624119048
// ...
-user-messages
-user_A
user_B
-NseAh8_4jwiZ9CR9XXw: 1
-user_B
user_A
-NseAh8_4jwiZ9CR9XXw: 1
每当任何一个用户向另一个用户发送新消息时,我都会有一个侦听器来侦听 user-messages/the_current_uid/other_uid/last_message_Id,因此所有新消息当前都会向两个用户显示:
var userMessagesObserver = Firebase.database.reference.child("user-messages")
fun listenForNewMessages() {
query = userMessagesObserver
.child(fromId) // fromId is the current user
.child(toId) // toId is the other user they are messaging with
.orderByKey()
.limitToLast(1)
listenerRefHandle = query?.addChildEventListener(object : ChildEventListener{
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
val lastMessageId = snapshot.key.toString()
fetchMessage(lastMessageId)
}
})
}
fun fetchMessage(messageId: String) {
// fetch message from messagesRef using the messageId then display it
}
根据弗兰克的建议,一旦我研究使用
ServerValue.TIMESTAMP
,它就不能用作key
,只能用作值,所以我不知道如何(1)将其添加到user-messages/the_current_uid/other_uid/last_message_Id 节点,因为无论发生什么,子节点的密钥始终是 messageId,例如。 -NseAh8_4jwiZ9CR9XXw:1,正如我的问题中所述,这可能会出现严重错误。 (2) 我如何收听新添加的ServerValue.TIMESTAMP
,以便我知道何时显示新消息?
正如 @FrankvanPuffelen 建议的那样,最好在子节点中使用服务器端时间戳,并根据该时间戳对值进行排序。
正如我在你的代码中看到的:
messageMap["timeStamp"] = System.currentTimeMillis()
timeStamp
属性保存当前毫秒的值,但该值是在运行该代码的计算机上生成的,而不是在服务器上生成的。
根据 Frank 的建议,一旦我研究使用 ServerValue.TIMESTAMP,就不能用作键,只能用作值。是的,您无法使用
ServerValue.TIMESTAMP
作为节点的名称。为什么?这是因为
ServerValue.TIMESTAMP
是一个对象,更好地说是 Map,并且实时数据库中的键始终是字符串。 虽然这可能是可能的,但并没有多大帮助。实时数据库中的时间戳是用很长的数字来表示的,所以如果你设置一个数字作为键,它就会变成一个字符串,如果你想对字符串进行排序,你不会得到想要的结果,因为当你对字符串进行排序时,字符串按字典顺序排序。
正如之前所说,最好的选择是将时间戳添加为字段的值。在 Kotlin 中,它会很简单:
在 Kotlin 中将数据类的对象添加到 Firestore 时如何添加 Firebase 时间戳?。有一个下行站点,不幸的是,您无法像在 Firestore 中那样指定方向。因此,您不能要求实时数据库按降序返回元素。所以这里的技巧是添加负值。这意味着当您的订单中有负数元素升序时,您会得到所需的结果。