Create NSPredicate with ANY and two conditions for it - ios

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

Related

SwiftUI Search Core Data With Relationships

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

How to use NSPredicate to filter an array stored in CoreData?

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))

Core Data predicate to fetch based on two of relationship's properties

I have two simple Entities:
Part {
#NSManaged var states: Set<PartState>
}
PartState {
#NSManaged var date: Date
#NSManaged var state: Int
}
I want to fetch those entities from Part, that newest PartState has state == 1.
I've tried to create NSPredicate:
NSPredicate(format: "SUBQUERY(states, $s, $s.date == max($s.date) AND $s.state == 1).#count > 0")
It looks like max($.date) checks all existing PartState entities, not just those belonging to a given Part and always returns 1 or 0 results.
NSPredicate(format: "ANY (states.state == 1 AND states.date == states.#max.date)")
Fails with: Unable to parse the format string...
NSPredicate(format: "ANY states.state == 1 AND ANY states.date == states.#max.date")
Returns all entities that have ever had state == 1 (which is what I would expect).
The only other solution that comes to my mind, is to have newestState property in Part but I still looking for some NSPredicate-based way.
You can fetch latest entry based on date using sort Descriptor
and apply predicate to fetch which has state == 1
let fetchRequest = NSFetchRequest<Part>.init(entityName: "Part")
fetchRequest.sortDescriptors = [NSSortDescriptor.init(key: "states.date", ascending: false)]
let predicate = NSPredicate.init(format: "states.state = %#","1")

How to search particular contact from nsarray of nsarray

I have store the contact in alphabetical order so I have array of array for contact list which i have display on screen. Now i want to search the contact via name but predicate not work properly here. I have done below code.
filterArray.filterUsingPredicate(NSPredicate(format: "ANY SELF.name CONTAINS[cd] '\(tfSearchBar.text!)'", argumentArray: nil))
In filter array first i have all contact but when i search for "a" it gives all the section array which has "a" in the contact name. but here i have stuck. It is not necessary that the all contact of the section contain "a" in the contact name.
For example
(
A:(
{
name = "abc"
number = "123456"
}
{
name = "azx"
number = "123456"
}
)
)
For example for above example after search for "a". when i search for "ab" then same array return by predicate.Not only first object. Any way to find only first object with out nested predicate.
I'm not sure what you are trying to accomplish with the word "ANY" in your predicate but it seems redundant. This code works as you describe when put into a Playground:
import UIKit
#objc class DataElement : NSObject {
let name : String
let number : String
init(name : String, number : String) {
self.name = name
self.number = number
}
}
let dataArray : NSArray = [
DataElement(name: "abc", number: "123456"),
DataElement(name: "azx", number: "689101")
]
let searchTerm = "a"
let predicate = NSPredicate(format: "SELF.name CONTAINS[cd] '\(searchTerm)'", argumentArray: nil)
dataArray.filteredArrayUsingPredicate(predicate)

Using NSPredicate with array for cloudKit search

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 [].

Resources