1

I have a large list of recipes and inside of each recipe is a list of ingredients. I want to write a query that will return a list of recipes that have both "egg" and "butter". Here is a quick screenshot of the first few recipes in my CKRecord Type. In my example code below, it returns no results though also has no errors.

Ideally, it would be great to include partial searches, for instance "egg" would still return a recipe that has "eggs" as an ingredient. It would be even better if the result would prioritize recipes that had ingredients which contains both "egg" and "butter", and then on the lesser priority of the recipe list would have recipes which had either "egg" or "butter", making the .and,subpredicates query of a higher priority than the .or,subpredicates query.

The other StackOverflow posts concerning NSCompoundPredicate that I have found do not help here when working with a list of lists that are being queried (as in my example where I have a list [ingredients] inside a list of recipes).

@objc func refresh(_ completion: @escaping (Error?) -> Void) {
    let subdeptPredicate1 = NSPredicate(format: "ANY ingredients == %@", "egg")
    let subdeptPredicate2 = NSPredicate(format: "ANY ingredients == %@", "butter")
    let predicate = NSCompoundPredicate(type: .and,subpredicates: [subdeptPredicate1, subdeptPredicate2])
        
    let sort = NSSortDescriptor(key: "recipe_id", ascending: false)
    let query = CKQuery(recordType: "Recipe", predicate: predicate)
    query.sortDescriptors = [sort]
    AttachToMainThread(forQuery: query, completion)
  }

I am able to successfully return recipes that have either "butter" or "egg" in the ingredients with this line of code, but I'm looking to prioritize recipes which include both "egg" and "butter".

    let searchText: [String] = ["butter","egg"]
    let predicate = NSPredicate (format: "ANY ingredients IN %@",argumentArray: [searchText])

I appreciate your help!

UPDATE

I found that if I just have one of these subpredicates that every recipe that returns, only has "eggs" as the first ingredient.

let subdeptPredicate1 = NSPredicate(format: "ANY ingredients == %@", "egg")
let predicate = NSCompoundPredicate(type: .and,subpredicates: [subdeptPredicate1])

I'm sure that's why I'm returning zero results when I use both "eggs" and "butter" because they will never both be in the first ingredient.

Willy Mac
  • 21
  • 4
  • 1
    Note that `==` compares the strings _exactly_. Do you want to use `CONTAINS` instead? – Sweeper Jul 28 '21 at 06:58
  • Thank you @Sweeper for mentioning that! When I tried "CONTAINS" like this: `let subdeptPredicate1 = NSPredicate(format: "ANY ingredients CONTAINS %@", "egg")` I get an error: "libc++abi.dylib: terminating with uncaught exception of type CKException *** Terminating app due to uncaught exception 'CKException', reason: 'Invalid predicate: ANY ingredients CONTAINS "egg" AND ANY ingredients CONTAINS "butter" (Error Domain=CKErrorDomain Code=12 "Invalid modifier in : Unsupported predicate modifier: ANY" UserInfo={ck_isComparisonError=true, NSLocalizedDescription=I – Willy Mac Jul 28 '21 at 07:09
  • See [subquery](https://developer.apple.com/documentation/foundation/nsexpression/1411651-expressionforsubquery?language=objc) – Willeke Jul 28 '21 at 07:09
  • Hey @Willeke, can you help me implement this idea? When I try the following line `let predicate = NSPredicate(format:"(SUBQUERY(ingredients, $x, ANY ingredients == 'egg' && ANY ingredients == 'butter'))")` I get the error: libc++abi.dylib: terminating with uncaught exception of type CKException *** Terminating app due to uncaught exception 'CKException', reason: 'Invalid predicate: SUBQUERY(residents, $x, ANY ingredients == "egg" AND ANY ingredients == "butter").@count != 0 (Error Domain=CKErrorDomain Code=12 "Invalid left expression in – Willy Mac Jul 28 '21 at 07:24
  • I'm sorry, I didn't read the question properly and I'm overcomplicating. I think this might be a CloudKit problem. – Willeke Jul 28 '21 at 07:51

1 Answers1

0

It was an error on my end. I found that the way I was parsing the csv file cause the problem. Notice the first ingredient is the only without a space in the beginning of the word.

This does precisely what I'm wanting:

    let searchTextA: [String] = ["egg"," egg"]
    let subPred1 = NSPredicate (format: "ANY ingredients IN %@",argumentArray: [searchTextA])
    
    let searchTextB: [String] = ["butter"," butter"]
    let subPred2 = NSPredicate (format: "ANY ingredients IN %@",argumentArray: [searchTextB])
    
    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [subPred1, subPred2])
Willy Mac
  • 21
  • 4