Search code examples
swiftfull-text-searchtokenpredicatecloudkit

Matching ALL tokens for full-text search in Swift


My apologies if this has been asked and I've missed finding the answer, but I am trying to do a full text search of a CloudKit database where all of the search words match. My example below:

var searchString = "almond salad"
let predicate = NSPredicate(format: "self CONTAINS %@", searchString)
let query = CKQuery(recordType: "Recipe", predicate: predicate)

I'll omit all of the other code to set default cloud database, execute query, etc. It all compiles correctly, but what I want it to do is give me any records that contain "almond" and contain "salad" in the same record - ideally I would get Almond Salad recipes. With it the way it is, it will give records such as "almond cake" and "tomato salad" where only one of the words match (it will also give Almond Salad, but I want to get rid of the others). I guess I'm reading the CKQuery documentation incorrectly, because it seems to say that all tokens should match in one record, but it doesn't seem to be doing so. Help?

Edit: Here's my final solution based on Dave's answer.

var searchString = "almond salad"
var formatString = ""

let searchStringArray = searchString.componentsSeparatedByString(" ")

if searchString != "" {
formatString = "self CONTAINS '\(searchStringArray[0])'"

if searchStringArray.count > 1 {
for var index = 1; index < searchStringArray.count; ++index {
formatString = formatString + " AND self CONTAINS '\(searchStringArray[index])'"
}
}

let predicate = NSPredicate(format: formatString)

Edit 2: I was having issues with apostrophe's with the way I formatted it, so I switched (pun intended) my approach. Here's my actual final solution.

var searchString = "momma's apple pie"
let searchStringArray = searchString.componentsSeparatedByString(" ")

if searchString.isEmpty { let predicate = NSPredicate(value: true) }

else {
switch (searchStringArray.count) {

case 1:
predicate = NSPredicate(format: "self CONTAINS %@", searchStringArray[0])

case 2:
predicate = NSPredicate(format: "self CONTAINS %@ AND self CONTAINS %@", searchStringArray[0], searchStringArray[1])

.
.
.

default:
print("Limiting search to 5 terms")
*copied and pasted case 5 predicate*

Perhaps a bit brute force, but if anyone has a more elegant solution, I'm willing to hear it. Thanks again for the help!


Solution

  • It looks like the CKQuery documentation is currently incorrect. I will file a bug about this.

    I was able to get the result you wanted by doing the following:

    NSPredicate(format: "self CONTAINS 'almond' AND self CONTAINS 'salad'")
    

    That means you'll need a little extra code to separate the tokens in your string by spaces and then build this AND predicate to put them together (if there are two or more).