在 Realm 中组合查询?
Combining queries in Realm?
我的模型中有这两个对象:
留言:
class Message: Object {
//Precise UNIX time the message was sent
dynamic var sentTime: NSTimeInterval = NSDate().timeIntervalSince1970
let images = List<Image>()
}
图片:
class Image: Object {
dynamic var mediaURL: String = ""
var messageContainingImage: Message {
return linkingObjects(Message.self, forProperty: "images")[0]
}
}
我想形成一个查询,其中包含 returns 条消息和图像,消息按 sentTime
排序,图像按 messageContainingImage
的发送时间排序。他们会被放在一起。
推荐的查询代码是这样的:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
这个returns一个Result<Message>
对象。 Result
无法连接到另一个结果。我还有其他问题,比如,如果我可以将它们组合起来,我将如何进行排序。
其他想法:
- 我还可以添加一个 属性 到名为 sentTime 的图像,然后一旦它们合并,我就可以在它们两个上调用 属性。
- 我可以使它们都从具有 sentTime 的类型中继承。问题是,做 Realm().objects(Message) 只会 returns 作为消息的东西,而不是 Message 的子类。
我怎样才能做到这一点?
我的最终目标是在表格视图中显示这些消息和图像结果,消息与其附加图像分开。
我认为,继承在这里不是正确的解决方案,这会通过使您的对象模式复杂化而引入更多缺点,而不是您的用例的价值。
让我们回到你写的是你的最终目标:我猜你想在一个 table 视图中将消息和图像一起显示为单独的行,其中图像跟随它们的消息。我的理解正确吗?
您不需要对两者都进行排序,对消息进行排序并以 suitable 方式访问它们及其图像将确保所有内容都正确排序。主要挑战更多的是如何枚举/随机访问这个二维数据结构作为一维序列。
根据您查询的数据量,您必须决定是否可以采用一种简单的方法,即一次将它们全部保存在内存中,或者在 Results 之上引入一个视图对象,它负责按顺序访问所有对象。
第一个解决方案可能如下所示:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
array = reduce(messages, [Object]()) { (var result, message) in
result.append(message)
result += map(message.images) { [=10=] }
return result
}
虽然后一种解决方案更复杂,但可能看起来像这样:
// Let you iterate a list of nodes with their related objects as:
// [a<list: [a1, a2]>, b<list: [b1, b2, b3]>]
// in pre-order like:
// [a, a1, a2, b, b1, b2, b3]
// where listAccessor returns the related objects of a node, e.g.
// listAccessor(a) = [a1, a2]
//
// Usage:
// class Message: Object {
// dynamic var sentTime = NSDate()
// let images = List<Image>()
// }
//
// class Image: Object {
// …
// }
//
// FlattenedResultsView(Realm().objects(Message).sorted("sentTime"), listAccessor: { [=11=].images })
//
class FlattenedResultsView<T: Object, E: Object> : CollectionType {
typealias Index = Int
typealias Element = Object
let array: Results<T>
let listAccessor: (T) -> (List<E>)
var indexTransformVectors: [(Int, Int?)]
var notificationToken: NotificationToken? = nil
init(_ array: Results<T>, listAccessor: T -> List<E>) {
self.array = array
self.listAccessor = listAccessor
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
self.notificationToken = Realm().addNotificationBlock { note, realm in
self.recomputeTransformVectors()
}
}
func recomputeTransformVectors() {
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
}
static func computeTransformVectors(array: Results<T>, _ listAccessor: T -> List<E>) -> [(Int, Int?)] {
let initial = (endIndex: 0, array: [(Int, Int?)]())
return reduce(array, initial) { (result, element) in
var array = result.array
let list = listAccessor(element)
let vector: (Int, Int?) = (result.endIndex, nil)
array.append(vector)
for i in 0..<list.count {
let vector = (result.endIndex, Optional(i))
array.append(vector)
}
return (endIndex: result.endIndex + 1, array: array)
}.array
}
var startIndex: Index {
return indexTransformVectors.startIndex
}
var endIndex: Index {
return indexTransformVectors.endIndex
}
var count: Int {
return indexTransformVectors.count
}
subscript (position: Index) -> Object {
let vector = indexTransformVectors[position]
switch vector {
case (let i, .None):
return array[i]
case (let i, .Some(let j)):
return listAccessor(array[i])[j]
}
}
func generate() -> GeneratorOf<Object> {
var arrayGenerator = self.array.generate()
var lastObject: T? = arrayGenerator.next()
var listGenerator: GeneratorOf<E>? = nil
return GeneratorOf<Object> {
if listGenerator != nil {
let current = listGenerator!.next()
if current != nil {
return current
} else {
// Clear the listGenerator to jump back on next() to the first branch
listGenerator = nil
}
}
if let currentObject = lastObject {
// Get the list of the currentObject and advance the lastObject already, next
// time we're here the listGenerator went out of next elements and we check
// first whether there is anything on first level and start over again.
listGenerator = self.listAccessor(currentObject).generate()
lastObject = arrayGenerator.next()
return currentObject
} else {
return nil
}
}
}
}
我的模型中有这两个对象:
留言:
class Message: Object {
//Precise UNIX time the message was sent
dynamic var sentTime: NSTimeInterval = NSDate().timeIntervalSince1970
let images = List<Image>()
}
图片:
class Image: Object {
dynamic var mediaURL: String = ""
var messageContainingImage: Message {
return linkingObjects(Message.self, forProperty: "images")[0]
}
}
我想形成一个查询,其中包含 returns 条消息和图像,消息按 sentTime
排序,图像按 messageContainingImage
的发送时间排序。他们会被放在一起。
推荐的查询代码是这样的:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
这个returns一个Result<Message>
对象。 Result
无法连接到另一个结果。我还有其他问题,比如,如果我可以将它们组合起来,我将如何进行排序。
其他想法:
- 我还可以添加一个 属性 到名为 sentTime 的图像,然后一旦它们合并,我就可以在它们两个上调用 属性。
- 我可以使它们都从具有 sentTime 的类型中继承。问题是,做 Realm().objects(Message) 只会 returns 作为消息的东西,而不是 Message 的子类。
我怎样才能做到这一点?
我的最终目标是在表格视图中显示这些消息和图像结果,消息与其附加图像分开。
我认为,继承在这里不是正确的解决方案,这会通过使您的对象模式复杂化而引入更多缺点,而不是您的用例的价值。
让我们回到你写的是你的最终目标:我猜你想在一个 table 视图中将消息和图像一起显示为单独的行,其中图像跟随它们的消息。我的理解正确吗?
您不需要对两者都进行排序,对消息进行排序并以 suitable 方式访问它们及其图像将确保所有内容都正确排序。主要挑战更多的是如何枚举/随机访问这个二维数据结构作为一维序列。
根据您查询的数据量,您必须决定是否可以采用一种简单的方法,即一次将它们全部保存在内存中,或者在 Results 之上引入一个视图对象,它负责按顺序访问所有对象。
第一个解决方案可能如下所示:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
array = reduce(messages, [Object]()) { (var result, message) in
result.append(message)
result += map(message.images) { [=10=] }
return result
}
虽然后一种解决方案更复杂,但可能看起来像这样:
// Let you iterate a list of nodes with their related objects as:
// [a<list: [a1, a2]>, b<list: [b1, b2, b3]>]
// in pre-order like:
// [a, a1, a2, b, b1, b2, b3]
// where listAccessor returns the related objects of a node, e.g.
// listAccessor(a) = [a1, a2]
//
// Usage:
// class Message: Object {
// dynamic var sentTime = NSDate()
// let images = List<Image>()
// }
//
// class Image: Object {
// …
// }
//
// FlattenedResultsView(Realm().objects(Message).sorted("sentTime"), listAccessor: { [=11=].images })
//
class FlattenedResultsView<T: Object, E: Object> : CollectionType {
typealias Index = Int
typealias Element = Object
let array: Results<T>
let listAccessor: (T) -> (List<E>)
var indexTransformVectors: [(Int, Int?)]
var notificationToken: NotificationToken? = nil
init(_ array: Results<T>, listAccessor: T -> List<E>) {
self.array = array
self.listAccessor = listAccessor
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
self.notificationToken = Realm().addNotificationBlock { note, realm in
self.recomputeTransformVectors()
}
}
func recomputeTransformVectors() {
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
}
static func computeTransformVectors(array: Results<T>, _ listAccessor: T -> List<E>) -> [(Int, Int?)] {
let initial = (endIndex: 0, array: [(Int, Int?)]())
return reduce(array, initial) { (result, element) in
var array = result.array
let list = listAccessor(element)
let vector: (Int, Int?) = (result.endIndex, nil)
array.append(vector)
for i in 0..<list.count {
let vector = (result.endIndex, Optional(i))
array.append(vector)
}
return (endIndex: result.endIndex + 1, array: array)
}.array
}
var startIndex: Index {
return indexTransformVectors.startIndex
}
var endIndex: Index {
return indexTransformVectors.endIndex
}
var count: Int {
return indexTransformVectors.count
}
subscript (position: Index) -> Object {
let vector = indexTransformVectors[position]
switch vector {
case (let i, .None):
return array[i]
case (let i, .Some(let j)):
return listAccessor(array[i])[j]
}
}
func generate() -> GeneratorOf<Object> {
var arrayGenerator = self.array.generate()
var lastObject: T? = arrayGenerator.next()
var listGenerator: GeneratorOf<E>? = nil
return GeneratorOf<Object> {
if listGenerator != nil {
let current = listGenerator!.next()
if current != nil {
return current
} else {
// Clear the listGenerator to jump back on next() to the first branch
listGenerator = nil
}
}
if let currentObject = lastObject {
// Get the list of the currentObject and advance the lastObject already, next
// time we're here the listGenerator went out of next elements and we check
// first whether there is anything on first level and start over again.
listGenerator = self.listAccessor(currentObject).generate()
lastObject = arrayGenerator.next()
return currentObject
} else {
return nil
}
}
}
}