NSPredicate to use with Transformable property - ios

I have array of strings which I save in Core Data as Transformable. From json I am gettings needed strings like this:
if let mealTimes = dictionary["mealTimes"] as? [String]{
self.mealTimes = mealTimes
}
Now I would like to filter fetch results by strings in mealTime property. I have tried this way:
let predicate = NSPredicate(format: "mealTimes LIKE[cd] '*%#*'", "breakfast")
var breakfastContents = StaticContent.fetchStaticContentsWithPredicate(predicate, inManagedObjectContext: self.coreDataStack.context)
if (breakfastContents.count > 0) {
breakfast = breakfastContents.first!
}
The problem is that result array is empty but I know I have breakfast string in some content. So how can I fix it? I was reading something about saving transformable as NSData so it would need some great trick. I was trying to use LIKE (with and without *) command and CONTAINS.
Extension for StaticContent:
class func fetchStaticContentsWithPredicate(predicate: NSPredicate, inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> [StaticContent] {
// Define fetch request/predicate
var fetchRequest = NSFetchRequest(entityName: "StaticContent")
// Assign fetch request properties
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
// Handle results
let fetchedResults = managedObjectContext.executeFetchRequest(fetchRequest, error: nil) as! [StaticContent]
return fetchedResults
}

You should refactor your data model to get rid of the "array of strings". This is not a very sound way to store this type of values.
Instead, consider a relationship with a new entity that captures those strings. Alternatively, device a scheme where you store a string and have a unique character that separates the records (e.g ; or something similar). Then any simple string predicate would work, like this:
NSPredicate(format: "mealtimes contains[cd] %#", "breakfast")

Related

Is it possible to perform NSBatchUpdateRequest with an array of NSManagedObjectID?

Currently, I perform multiple update operations via the following code.
func updateOrders(_ updates : [(objectID: NSManagedObjectID, order: Int64)]) {
if updates.isEmpty {
return
}
let coreDataStack = CoreDataStack.INSTANCE
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
for update in updates {
let objectID = update.objectID
let order = update.order
let nsPlainNote = try! backgroundContext.existingObject(with: objectID) as! NSPlainNote
nsPlainNote.order = order
}
RepositoryUtils.saveContextIfPossible(backgroundContext)
}
}
Since I would like to
Make the update operations run faster
Avoid delegate of NSFetchedResultController from being notified
I would like to utilise NSBatchUpdateRequest for performing such update operation.
However, I don't find a way, how I can apply array of NSManagedObjectID and array of Int64 value, to NSBatchUpdateRequest.
Given an array of NSManagedObjectID and Int64, is it possible to use NSBatchUpdateRequest to perform updated on CoreData?
You must use NSPredicate to set object id
func updateOrders(_ updates : [(objectID: NSManagedObjectID, order: Int64)]) {
updates.forEach {
let request = NSBatchUpdateRequest(entityName: "NSPlainNote")
request.propertiesToUpdate = ["order": $0.order]
request.predicate = NSPredicate(format: "objectID == %#", $0.objectID)
let result = try? context.execute(request)
}
}
NSBatchUpdateRequest is not suitable for your task since using it makes sense for large amount of records with a common attribute's value so that you can filter all by your criteria and update all fields with your values at once.
The fact is that the NSBatchDeleteRequest is an NSPersistentStoreRequest which operates at the SQL level in the persistent store itself and it doesn't update your in-memory objects after execution thats why it works so fast and Core Data translates your native requests to a SQL ones where you can not use dynamically code to get and insert needed data from dictionary etc. but you can update the current value of a filed e.g.:
let batchRequest = NSBatchUpdateRequest(entityName: "Note")
batchRequest.predicate = predicate
// Increase `order` value
batchRequest.propertiesToUpdate = ["order" : NSExpression(format: "order + 1")]
do {
try context.execute(batchRequest)
}
catch {
fatalError(error.localizedDescription)
}

coredata fetch with predicate for object relation using swift

I'm trying to perform a fetch for an entity in coreData. This entity has a one to one relation with another entity. I want to only get the items in the first entity that have a relation to a particular item in the second entity. I'm trying this predicate which I know is incorrect:
let fetchRequest: NSFetchRequest<ItemA> = NSFetchRequest(entityName: "ItemA")
fetchRequest.predicate = NSPredicate(format: "itemA.itemB.itemBId == %d", Int(itemB.itemBId))
Currently you are using itemA.itemB.itemBId , here you are using a new entity of itemA , where as you dont need to give its name, since this predicate will be applied on itemA entity, so either you can use only itemB.itemBId inside predicate or you can use SELF.itemB.itemBId (I am not sure about SELF or self, obviously you can look it up).
So I think you can get the items of type itemA whose relation itemB has an id of itemBId like this:
let fetchRequest: NSFetchRequest<ItemA> = NSFetchRequest(entityName: "ItemA")
fetchRequest.predicate = NSPredicate(format: "itemB.itemBId == %d", Int(itemB.itemBId))

Realm subquery with List count in Swift

I have
class Car : Object
{
let queries : List<QueryModel> = List<QueryModel>()
}
and
class QueryModel : Object
{
let location : String = ""
}
Now how do I write that NSPredicate to fetch just that cars which have queries with location equal to 'London'?
p.s. It's a known fact that things like #count do not work on Swift List class...
p.p.s. I need it as a predicate because this is the only way RBQFetchedResultsController knows how to do it's job.
How I thought it could've been written was something like NSPredicate(format: "SUBQUERY(queries, $query, $query.location == %#)", 'London'). But nothing like this ever passes Realm's predicate validation :)
You can write
NSPredicate(format: "ANY queries.location = %#", locationString)
If you want to add other filtering, you can create other NSPredicate and then use
NSCompoundPredicate(andPredicateWithSubpredicates: [pred1, pred2])
to combine the filters.
Now, if you want to filter cars, you have to prepare a
let request = FetchRequest<Car>(realm: realm, predicate: predicate)
then you can use RBQFetchedResultsController to execute the request.

Do this to core data: "Select * WHERE "attribute" == "Some string" "

I did manage to do this by making a function with a loop that checks the attribute to a string. But i'm looking for a better way to do this.
In sql I do this:
Select * WHERE "attribute" == "string"
Is there a method to do this in swift?
My function looks like this:
func tableData()
{
let objects = retrieveValues("JobTime") //Retrieve a NSMutableArray
if !objects.isEmpty
{
for var index = 0; index < objects.count; ++index
{
if objects[index].valueForKey("jobTitle") as? String == transferTitle
{
print("Job title matched: \(index)")
}
else
{
print("Nothing here!")
}
}
}
}
In order to perform fetch request in CoreData you have to initialise NSFetchRequest class. In order to specify in what kind of entities you are interested you create NSPredicate class. It gives you ability to specify pretty advanced queries. In most cases the simplest way to create NSPredicate is by using format string - details about the syntax can be found Apple's Predicate Format String Syntax document.
You can find example of how you can perform fetch request in CoreData (and Swift) below.
let managedObjectContext: NSManagedObjectContext
let predicate = NSPredicate(format: "jobTitle == %#", "Programmer")
let fetchRequest = NSFetchRequest.init(entityName: "People")
fetchRequest.predicate = predicate
//fetchRequest.sortDescriptors = [] //optionally you can specify the order in which entities should ordered after fetch finishes
let results = managedObjectContext.executeFetchRequest(fetchRequest)
You can pass the query to CoreData and only retrieve what you want. The NSManagedObjectContext class has a executeFetchRequest method that you call to retrieve data from the database. You pass NSFetchRequest object to it. That object contains a NSPredicate, which defines your query.

Using regex in NSPredicate to perform a NSFetchRequest

I try to perform an NSFetchRequest with this NSPredicate:
let searchString: NSString = "приш[её]л"
let predicate = NSPredicate(format: "text MATCHES[cd] %#", searchString)
let fetchRequest = NSFetchRequest(entityName: "Data")
fetchRequest.predicate = predicate
do {
let objects = try context!.executeFetchRequest(fetchRequest) as! [NSManagedObject]
return objects
} catch {
print("Error")
}
But no results, though actually 'text' contains "пришёл"
I've found answer in this qustion: Setting up a CoreData regular expression predicate for a comma-separated list
let searchString = "приш[её]л"
let format = "(text MATCHES %#)"
let pattern = NSString(format: ".*(\\b%#\\b).*", searchString)
let predicate = NSPredicate(format: format, pattern)
You need to be aware that using MATCHES[cd] to perform regex (regular expression) matching is very expensive if you have a lot of data in your database.
You should try to avoid regular expressions if you can. And in this particular case it seems like using normalized text would allow you to use a predicate of the form %K BEGINWITH[n] %# or %K ==[n] %# wich is very fast.
To use normalized strings, you’d have both an attribute text and a normalized version of it, e.g. textNormalized which you could update in the setter for text. You’d then use textNormalized in the [n] predicates.
You can normalize text with
extension String {
public var normalizedForSearch: String {
let transformed = applyingTransform(
StringTransform("Any-Latin; Latin-ASCII; Lower"),
reverse: false)
return transformed as String? ?? ""
}
}
Be sure to check the chapter on Text in our book: https://www.objc.io/books/core-data/ — it describes how to use normalized versions of your text to make sure searches like these are efficient.

Resources