Search code examples
swiftcore-datanspredicatensexpression

NSPredicate Inside a SUBQUERY


I'm trying to write a SUBQUERY for an NSPredicate. My problem is I don't have the query on hand for the predicate portion of the SUBQUERY. Is there a way to nest an NSPredicate inside the SUBQUERY?

For example this is what I have tried:

let ordersPredicate = NSPredicate()//some predicate passed in
//how do I use the ordersPredicate inside the subquery??
let subQueryPredicate = NSPredicate(format: "SUBQUERY(orders, $x,'%@').@count > 0", ordersPredicate.predicateFormat)

Update: I had to do a dirty workaround for now by executing the first predicate and doing an ANY IN query. Its lame :( because I'm executing the ordersPredicate elsewhere as well later in the pipeline..

let fetchRequest = NSFetchRequest()//blah
fetchRequest.predicate = orderPredicate
let orders = //execute fetch
let subQueryPredicate = NSPredicate(format: "SUBQUERY(orders, $x, ANY $x in %@).@count > 0", orders)

Solution

  • Thanks to some pointers from @Willeke I was able to come up with a solution.

    public extension NSPredicate{
        public func stringForSubQuery(prefix:String) -> String{
            var predicateString = ""
            if let predicate = self as? NSCompoundPredicate{
                for (index, subPredicate) in predicate.subpredicates.enumerate(){
    
                    if let subPredicate = subPredicate as? NSComparisonPredicate{
                        predicateString = "\(predicateString) \(prefix)\(subPredicate.predicateFormat)"
                    }
                    else if let subPredicate = subPredicate as? NSCompoundPredicate{
                        predicateString = "\(predicateString) (\(subPredicate.stringForSubQuery(prefix)))"
                    }
                    //if its not the last predicate then append the operator string
                    if index < predicate.subpredicates.count - 1 {
                        predicateString = "\(predicateString) \(getPredicateOperatorString(predicate.compoundPredicateType))"
                    }
                }
            }
            return predicateString
        }
        private func getPredicateOperatorString(predicateType: NSCompoundPredicateType) -> String{
            switch(predicateType){
            case .NotPredicateType: return "!"
            case .AndPredicateType: return "&&"
            case .OrPredicateType: return "||"
            }
        }
    }
    

    And here is the usage

    let ordersPredicate = NSPredicate()//some predicate passed in
    let ordersPredicateFormat = orderPredicate.stringForSubQuery("$x.")
    let subQueryPredicate = NSPredicate(format: "SUBQUERY(orders, $x, \(ordersPredicateFormat)).@count > 0")