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)
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")
For anyone coming across this in the future, you don't need to build the predicate string yourself, just use predicateFormat
directly in your subquery string. It is already properly escaped, so use string interpolation.
let ordersPredicate = NSPredicate()
// Add it to the query with string interpolation
let subQueryPredicate = NSPredicate(format: "SUBQUERY(orders, $x, \(ordersPredicate.predicateFormat)).@count > 0")
If you try to pass it as an argument, it will try to escape the string again, which will probably not work.
// Don't do this!
let subQueryPredicate = NSPredicate(format: "SUBQUERY(orders, $x, %@).@count > 0", ordersPredicate.predicateFormat)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With