swiftcore-datasubquerynspredicatensexpression

Nested subqueries built with NSExpression crashes but not its predicate


I have some code that can recursively build nested subqueries with NSComparisonPredicate and NSExpression. Here it is when it's flat:

    // Nested subquery
    let entityPath = NSExpression(forKeyPath: "$s.mainUserResult.operations")
    let subQueryExpression = NSExpression(forSubquery: entityPath, usingIteratorVariable: "t", predicate: NSPredicate(format: "$t.operationType == 3"))
    let countSubQuery = NSExpression(format: "%@.@count", argumentArray: [subQueryExpression])
    let p1 = NSComparisonPredicate(leftExpression: countSubQuery,
                                 rightExpression: NSExpression(forConstantValue: 0),
                                 modifier: .direct,
                                 type: .greaterThan,
                                 options: NSComparisonPredicate.Options(rawValue: 0))

    // Main subquery
    let entityPath2 = NSExpression(forKeyPath: "sessions")
    let subQueryExpression2 = NSExpression(forSubquery: entityPath2, usingIteratorVariable: "s", predicate: p1)
    let countSubQuery2 = NSExpression(format: "%@.@count", argumentArray: [subQueryExpression2])
    
    let p2 = NSComparisonPredicate(leftExpression: countSubQuery2,
                                 rightExpression: NSExpression(forConstantValue: 0),
                                 modifier: .direct,
                                 type: .greaterThan,
                                 options: NSComparisonPredicate.Options(rawValue: 0))

The above code crashes when I fetch some entities with the following error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't have a non-relationship collection element in a subquerySUBQUERY($s.mainUserResult.operations, $t, $t.operationType == 3)'

What's really weird is that when I build a predicate by hand by copying the resulted predicateFormat of the NSComparisonPredicate, it actually works!

Fetching with the predicate p2 results in an error, whereas fetching with the resulted format works:

   let p2 = NSPredicate(format: "SUBQUERY(sessions, $s, SUBQUERY($s.mainUserResult.operations, $t, $t.operationType == 3).@count > 0).@count > 0")

How to make the NSComparisonPredicate work?


Solution

  • NSExpression(forKeyPath: "$s.mainUserResult.operations")

    does

    self.value(forKeyPath: "$s.mainUserResult.operations")

    This should be

    s.value(forKeyPath: "mainUserResult.operations")

    expression:

    NSExpression(format: "$s.mainUserResult.operations", argumentArray: [])