我正在开发一个聊天应用程序。每个频道都有很多消息。我正在构建“频道列表”视图,在该视图中,我希望显示按每个频道发送的最新消息排序的所有频道。
每次发送或接收消息时,我都希望保持channel.latestMessageUpdatedAt
的更新,以便以后对通道进行排序。
我想分开关注,而不必记得每次更新消息时都要更新通道。
我的策略是将侦听器内的通道更新到消息领域,但我得到以下错误
Error: Wrong transactional state (no active transaction, wrong type of transaction, or transaction already in progress)
const ChannelSchema = {
name: "channel",
primaryKey: "id",
properties: {
id: "string",
latestMessageUpdatedAt: "date",
},
};
const MessageSchema = {
name: "message",
primaryKey: "id",
properties: {
id: "string",
channelId: "string",
text: "string",
updatedAt: "date",
},
};
const realm = await Realm.open({
path: "default.realm",
schema: [ChannelSchema, MessageSchema],
schemaVersion: 0,
});
const messagesRealm = realm.objects("message");
messagesRealm.addListener((messages, changes) => {
for (const index of changes.insertions) {
const channel = realm.objectForPrimaryKey(
"channel",
messages[index].channelId
);
if (!channel) continue;
const message = messages[index];
channel.latestMessageUpdatedAt = new Date();
}
});
我已经查了文档了,似乎没有理由不能这样做。
也许有一个更好的方法来拥有这个计算字段。
注意,我考虑在通道上嵌入对象/消息列表,但是消息的数量可能高达10k,我不希望所有这些都同时返回到内存中。
我也试过
realm.write(() => {
channel.latestMessageUpdatedAt = new Date();
});
但我得到的错误是,一个事务已经在进行中。
发布于 2022-02-03 12:32:45
OP请求一个Swift解决方案,因此我有两个解决方案:哪一个是使用的,取决于数据集和关系的编码首选项。在这个问题中没有自动的逆关系,只是为了以防万一而已。
1 -没有LinkingObjects:手动逆关系
让我们用从通道到消息的1多个关系来建立模型,然后从消息返回到它的父通道建立一个逆关系。
class ChannelClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var channelName = ""
@Persisted var messageList: List<MessageClass>
}
class MessageClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var msg = ""
@Persisted var msgDate = ""
@Persisted var myChannel: ChannelClass!
}
然后,在我们填充完领域之后,我们有一些类似于此的对象--记住不同的通道将在不同的时间添加消息。
channel 0
message 1
message 3
message 4
channel 1
message 5
message 9
message 10
channel 2
message 2
message 6
message 7
message 8
这样做的原因是:假设用户将消息发送到0通道,即消息1。一天后,另一个用户将消息发送到通道2,即消息2。然后,在另一天,用户将消息发送到0通道,即消息3等等。
请记住,当领域对象未排序时,List对象始终保持其顺序。因此,每个通道列表中的最后一个元素是该通道中最新的消息。
从那里开始,根据它们最近的消息排序的通道是一个班轮。
let messages = realm.objects(MessageClass.self).sorted(byKeyPath: "msgDate").distinct(by: ["myChannel._id"])
如果您现在迭代消息以打印通道,下面是输出。注意:这只是为了显示已经从领域中检索到的数据,并且在应用程序中不需要。
Channel 0 //the first message
Channel 2 //the second message
Channel 1 //the third message? Not quite
然后你对自己说:"self,等一下--第三条消息在0频道!那么为什么输出通道1是最后一条消息呢?“
其理由是OP需要只列出一次信道--因此,由于信道0已经列出,剩下的信道是通道1。
2 -与LinkingObjects:自动逆关系
以一个场景为例,使用LinkingObjects自动创建从messages对象返回到通道的反向链接,例如反向遍历对象图
class MessageClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var msg = ""
@Persisted var msgDate = ""
@Persisted(originProperty: "messageList") var linkedChannels: LinkingObjects<ChannelClass>
}
思维过程是相似的,但我们必须依靠斯威夫特一点点提供一种。这是一条班轮
let channels = realm.objects(ChannelClass.self).sorted { $0.messageList.last!.msgDate < $1.messageList.last!.msgDate }
这里要做的是查询通道,并使用每个通道列表中最后一个消息对象的msgDate属性对通道进行排序。输出是相同的
Channel 0 //the first message
Channel 2 //the second message
Channel 1 //see above
这里唯一的缺点是,这种解决方案将产生更大的内存影响,但增加了通过LinkingObjects实现自动反向关系的方便性。
3另一个选项
向channel类添加一个小函数和一个属性的另一个选项,既向通道messagesList
添加消息,也填充通道的“lastMsgDate”属性。那么对频道进行排序就很简单了。所以看起来会是这样
class ChannelClass: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var channelName = ""
@Persisted var messageList: List<MessageClass>
@Persisted var lastMsgDate: String
func addMessage(msg: MessageClass) {
self.messageList.append(msg)
self.lastMsgDate = msg.msgDate
}
}
当消息被添加到通道时,最后一个消息日期将被更新。然后通过lastMsgDate
对通道进行排序
someChannel.addMessage(msg: someMessage)
注意:为了简单起见,我使用String作为消息日期。如果要这样做,请确保它是yyyymmddhhmmss格式,或者只使用实际的日期属性。
https://stackoverflow.com/questions/70945686
复制相似问题