Syed Tariq
Syed Tariq

Reputation: 2918

Find an object in a Realm using BETWEEN

I am using Realm object as shown below and created a database of the objects. I also created a function to find an element based on current time. I am having difficulty in creating the predicate this function needs. I am using a format as defined in the Realm cheatsheet, but I'm getting an exception as follows:

Terminating app due to uncaught exception 'Invalid predicate', reason: 'Predicate with BETWEEN operator must compare a KeyPath with an aggregate with two values'

import Foundation
import RealmSwift

func composeUniqueRealmPrimaryKeyForWorkorder(_ start:String) -> String {
    return start
}

class WorkorderRecord: Object {

    dynamic var id = ""
    dynamic var workorder = ""
    dynamic var start = "" // defines start time string as in 2017-06-09 9:00
    dynamic var end = "" // defines end of interval as in 2017-06-10 16:30


    override static func primaryKey() -> String? {
        return "id"
    }

    override class func indexedProperties() -> [String] {
        return ["workorder","start","end"]
    }

    convenience init(_ workorder:String, start:String, end:String) {
        self.init()
        self.id = composeUniqueRealmPrimaryKeyForWorkorder (workorder)
        self.workorder = workorder
        self.start = start
        self.end = end
    }
}

func getValueForInterval(time:String) -> String? {
    let realm = try! Realm()
    let predicate = NSPredicate(format: "%@ BETWEEN {start , end }", time)
    let r = realm.objects(WorkorderRecord.self).filter(predicate)
    if r.count == 0 {
        print("NO RECORD FOUND")
        return ""
    } else {
        let workorder = r[0].workorder
        let start = r[0].start
        let end = r[0].end
        print("RECORD Retrieved: workorder= \(workorder) start= \(start) end= \(end)")
        return workorder
    }
}

Upvotes: 2

Views: 1684

Answers (1)

bdash
bdash

Reputation: 18308

If you reason about what semantics the BETWEEN operator provides, you'll soon realize that foo BETWEEN {bar, baz} is equivalent to foo >= bar AND foo <= baz[0]. While Realm currently supports only constant values in the {bar, baz} aggregate expression, there's no such restriction on normal relational comparison operators.

One sticking point you'll run into is that while Realm supports comparison operators such as >= and <= on numeric and Date types, they are not currently supported on string types. Since you're storing your start and end times as strings, you're not able to take advantage of this transformation without further changes.

If you switch to storing your times using the Date type, you'll be able to reformulate your query as:

let predicate = NSPredicate(format: "%@ >= start AND %@ <= end", time, time)

This would also have the advantage of reducing the space required to store the dates.

[0]: Note that this is only true if the key path foo does not traverse any to-many relationships. If it does traverse to-many relationships, a SUBQUERY is needed to match BETWEENs semantics.

Upvotes: 4

Related Questions