When using a NSPredicate, I'm trying to search all objects (strings) contained within an array. The code sample below works but the predicate only collects the object in the first index only?? The NSPredicate is used for a CKQueryOperation.
Each Record has a value for a key named Category.
let array: [String] = ["Education", "Sport", "TV and Film"]
// select all records
//let predicate = NSPredicate(format: "Category = %#", category )
let predicate = NSPredicate (format: "Category == %#", argumentArray: array)
let query = CKQuery(recordType: "quizRecord", predicate: predicate)
// get just one value only
let operation = CKQueryOperation(query: query)
//code works but only queries Records with the Category for "Education"
Try replacing:
"Category == %#"
With:
"Category IN %#"
I was having the same issue as Peter Wiley. The solution I found was a mash up of Danny Bravo's solution and comment on that solution.
let arrayPredicate = NSPredicate(format: "Name IN %#", argumentArray: [names])
To get multiple results I needed to use both the keyword "IN" in the format and wrap my argumentArray with [].
Related
I have a SwiftUI app with SwiftUI life cycle and am persisting data in Core Data.
I am using Xcode 14.0.1 and iOS 16 to create a NavigationSplitView architecture.
This all works fine. I have added .searchable to the main list and am able to search
the entity string fields in the entity but I want to include string fields in
the relationship entities and have not been able to do so.
Let's say I have an entity Trip, with name, description and comment attributes - all
Strings. I create a searchResults var and use the result in the list. This works for
the fields discussed.
var searchResults: [Trip] {
if searchText.isEmpty {
return Array(tripsFetched)
} else {
return Array(tripsFetched).filter {
$0.wrappedTripName.lowercased().contains(searchText.lowercased())
||
$0.wrappedTripDescription.lowercased().contains(searchText.lowercased())
||
$0.wrappedComment.lowercased().contains(searchText.lowercased())
}//filter
}//if else
}//var search results
Now let's say I have a one to many relationship between Trip and an entity Site and say
Site has string attributes for siteName and siteDescription. I have not been able to
add an iteration over the NSSet of Site objects to look for the searchText. I've made
many attempts including the following but nothing has worked.
||
$0.sites?.allObjects(where: $0.wrappedSiteName.contains(searchText.lowercased()))
Any guidance would be appreciated.
For searching we use the nsPredicate property on the fetch request.
You'll need an or predicate using the contains keyword for the text and equals with either the object or it's id for the relation, but this page shows all the different ways it can be configured:
https://developer.apple.com/documentation/foundation/nspredicate
There is a SwiftUI example on this page:
https://developer.apple.com/documentation/swiftui/fetchrequest/configuration
.onChange(of: query) { value in
quakes.nsPredicate = query.isEmpty
? nil
: NSPredicate(format: "place CONTAINS %#", value)
}
For others - malhal is correct. You can create a compound predicate to search the
one-to-many relationships. The important part is the syntax referenced in his links
that uses the ANY keyword. This is an example:
func cdSearchQuery(searchText: String) {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = Trip.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "tripName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicateTrip = NSPredicate(format: "tripName CONTAINS[cd] %# || tripDescription CONTAINS[cd] %# || tripComment CONTAINS[cd] %# || tripCompanions CONTAINS[cd] %#", searchText, searchText, searchText, searchText)
//for the to-many attributes, use the ANY keyword
let predicateSite = NSPredicate(format: "ANY sites.siteName CONTAINS[cd] %# || sites.siteDescription CONTAINS[cd] %# || sites.siteLocation CONTAINS[cd] %#", searchText, searchText, searchText )
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicateTrip, predicateSite])
fetchRequest.predicate = predicate
do {
let results = try context.fetch(fetchRequest)
//for testing - remove and send results to a Published array
if let tripArray = Array(results) as? [Trip] {
for t in tripArray {
print("included is \(String(describing: t.tripName))")
}
}
} catch {
print("Error retrieving compound predicate")
}
}//cd search
I simply have two entities: Product and Item. Product may have many items: Set<Item>. Item has two properties: isActive and identifier.
Now I need to fetch all Products which have at least one Item with the following conditions met the same time:
identifier IN %# //["1", "2"]
isActive = true
let format = "ANY (items.identifier IN %# AND items.isActive = true)"
let predicate = NSPredicate(format: format, ["1", "2"])
But I got exception: Unable to parse the format ...
Why?
You can try this -
let format = "SUBQUERY(items, $item, $item.identifier IN %# AND $item.isActive = true).#count > 0"
let predicate = NSPredicate(format: format, ["1", "2"])
Source : NSPredicate Cheatsheet
I have a 'tags' array stored in CoreData (type 'Transformable') as an Array.
I am making a request to CoreData to retrieve tags based on a searchText (string) input.
How can I use NSPredicate to filter the tags array based on the searchText?
let request = NSFetchRequest<NSFetchRequestResult>(entityName: Constants.Trip)
if(searchText != nil && searchText != "") {
request.predicate = NSPredicate(format: "ANY %# in tags", searchText!)
}
request.returnsObjectsAsFaults = false
You cannot do that. Check this answer core data array of string filter
You might consider to have an entity for tags, or join the array of tags and have it as a whole string in your current entity, thus having the tags property as type String. For the last approach, you could search with the predicate below.
NSPredicate(format: "tags CONTAINS[cd] %#", searchText)
The [cd] expression is to specify case and diacritic insensitivity respectively.
You can get know more about the Core Data operations here.
I got some basic operators from the documentation and put them below:
BEGINSWITH
The left-hand expression begins with the right-hand expression.
CONTAINS
The left-hand expression contains the right-hand expression.
ENDSWITH
The left-hand expression ends with the right-hand expression.
LIKE
The left hand expression equals the right-hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters.
You need to write predicate and set as a part of request like this ...
func getEntitiesWithName(_ entityName:String, sortKey:String?, predicate: NSPredicate?, ascending:Bool, context:NSManagedObjectContext) -> [NSManagedObject] {
var results:[NSManagedObject] = [NSManagedObject]()
context.performAndWait { () -> Void in
let request = NSFetchRequest<NSFetchRequestResult>()
request.entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
if let fetchPredicate = predicate {
request.predicate = fetchPredicate
}
if let key = sortKey {
let sortDescriptor = NSSortDescriptor(key: key, ascending: ascending, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
request.sortDescriptors = [sortDescriptor];
}
results = (try! context.fetch(request)) as! [NSManagedObject]
}
return results
}
And you can set predicate like this
let predicate = NSPredicate(format: "isActive == %#", NSNumber(value: true as Bool))
I am a new hand to Core Data,I don't know how to write some query sentence for NSPredicate and meet some problem.Please help to find my problem and give me some suggestion to achieve my query aim.For example,now I Have a table ,it has some data .(As the picture show)My database
The data in the table with the month attribute "2015/03/01 00:00:00".Now,I want to query a date which month is began with 2015/03,I code "fetchRequest.predicate = NSPredicate(format: "month BEGINSWITH[cd] %#/0%#", year,month)" or "fetchRequest.predicate = NSPredicate(format: "month LIKE[c] %#/0%#", year,month)",am I right?(year=2015,month=03)
If not,I really hope someone can help me.And another question is the 'cd/c' in [] means what?Is it set structures?The code as follow:
func remove(model:MonthlySummery) -> Int {
let cxt = self.managedObjectContext!
let entity = NSEntityDescription.entityForName("MonthlySummery", inManagedObjectContext: cxt)
let component = NSCalendar.currentCalendar().components([.Year,.Month], fromDate: model.month!)
let year = component.year
let month = component.month
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entity
NSLog("month == %i/0%i", year,month)
fetchRequest.predicate = NSPredicate(format: "month BEGINSWITH[cd] %#/0%#", year,month)
do{
let listData = try cxt.executeFetchRequest(fetchRequest) as NSArray!
if listData.count > 0 {
let dailyRecord = listData.lastObject as! NSManagedObject
cxt.deleteObject(dailyRecord)
if cxt.hasChanges {
try cxt.save()
}
}
} catch {
NSLog("Remove failure!")
let nserror = error as NSError
NSLog("Error: \(nserror), \(nserror.userInfo)")
}
return 0
}
It is a bad idea to parse dates by relying on formatted date strings.
Refactor your code to use proper dates. The NSManagedObject subclass should have a property of type NSDate. In the actual SQLite database, this will be represented by a long number, but you should not be interested in that.
You then calculate start and end date of particular month and use a predicate like this:
NSPredicate(format: "month >= %# && month < %#", start, end)
I was wondering if it was possible to search for a CKRecord by not only its record type but as well as its field in the database. For example heres my code I have now to fetch records:
database.performQuery(query, inZoneWithID: nil, completionHandler: { (records:[AnyObject]!, error:NSError!) in
// Check if there's an error
if error != nil {
NSLog(error.localizedDescription)
}
else {
}
})
This only searches through the record type and for my program I need to be able to go deeper and specify a field as well to search through. Any help will be greatly appreciated!
The CKQuery documentation shows you how to build more complex queries. Here is a simple example:
let firstName = "John"
let age = 20
let predicate = NSPredicate(format: "firstName = %# AND age >= %#", firstName, NSNumber(integer: age))
let query = CKQuery(recordType: "Employee", predicate: predicate)