在 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 无法连接到另一个结果。我还有其他问题,比如,如果我可以将它们组合起来,我将如何进行排序。

其他想法:

我怎样才能做到这一点?

我的最终目标是在表格视图中显示这些消息和图像结果,消息与其附加图像分开。

我认为,继承在这里不是正确的解决方案,这会通过使您的对象模式复杂化而引入更多缺点,而不是您的用例的价值。

让我们回到你写的是你的最终目标:我猜你想在一个 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
            }
        }
    }
}