在Realm中组合查询?

时间:2015-06-24 18:53:49

标签: ios database swift 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]
    }
}

我想形成一个查询,它返回消息和图像,按sentTime排序的消息和按messageContainingImage的发送时间排序的图像。他们会被整理在一起。

查询的推荐代码是:

let messages = Realm().objects(Message).sorted("sentTime", ascending: true)

这将返回Result<Message>个对象。 Result没有办法加入另一个结果。我的方式也存在其他问题,例如,如果我可以将它们组合在一起,那么我将如何进行排序。

其他想法:

  • 我还可以向Image添加一个名为sentTime的属性,然后一旦它们合并,我就可以在它们上面调用该属性。
  • 我可以将它们作为具有sentTime的类型的子类。问题是,做Realm()。对象(Message)只会返回消息,而不是Message的子类。

我怎么能这样做?

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

1 个答案:

答案 0 :(得分:1)

我认为,继承不是正确的解决方案,这会使您的对象架构复杂化,从而为您的用例带来更多的弊端。

让我们回到你所写的是你的最终目标:我想你想在一个表视图中将消息和图像一起显示为分开的行,其中图像跟随他们的消息。我理解正确吗?

您不需要对两者进行排序,对消息进行排序并以适当的方式访问它们,并确保所有内容都能正确排序。主要的挑战是如何枚举/随机访问这个二维数据结构作为一维序列。

根据您查询的数据量,您必须决定是否可以通过一次将所有内容保存在内存中来实现简单方法,或者在结果上引入视图对象,这样可以访问所有数据对象按顺序。

第一个解决方案可能如下所示:

let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
array = reduce(messages, [Object]()) { (var result, message) in
            result.append(message)
            result += map(message.images) { $0 }
            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: { $0.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
            }
        }
    }
}