Search core data for closest value - ios

Currently we have a database of multiple values for r,g,b, split into own attributes. ex:
r = type float
g = type float
b = type float
There will be multiple values for each r, g, and b attribute.
After getting a float from an outside source, we want to search the database to return the attribute with the closest number to that float. (ex. for r, if we get a value of 199, the r value with the closest value to 199 will be returned).
I know there are predicates that can be used like: "text CONTAINS[c] %#" but I didn't see any predicates for 'closest value'.

You can use NSSortDescriptor(key: "name_key", ascending: true) with your request.
let request1 = NSFetchRequest<NSFetchRequestResult>() // use correct constructor
let sortDescriptor = NSSortDescriptor(key: "name_key", ascending: true)
let sortDescriptors = [sortDescriptor]
request1.sortDescriptors = sortDescriptors
let predicate1 = NSPredicate(format: "text < %#", value)
request1.predicate = predicate1
// use this request1 to fetch from core data
let request2 = NSFetchRequest<NSFetchRequestResult>() // use correct constructor
request2.sortDescriptors = sortDescriptors
let predicate2 = NSPredicate(format: "text > %#", value)
request2.predicate = predicate2
// use this requst2 to fetch from core data
Then you can use predicate twice:
First time with format text < %# and use last item of the array.
and
Second time with format text > %# and use the first item of the array.
compare the difference of your first and last items from both iterations with your given value and see whose difference is smaller. That will be closest value.
If you don't know how to fetch data from core data you can follow Fetching Objects
Getting started with core data

Related

NSPredicate to get the max value of a property under a given value

I have an entity Update which has two attributes: date and amount. Suppose I have these objects in Core Data:
| Date | Amount |
|------------+--------|
| 2020-01-01 | 100 |
| 2020-01-05 | 200 |
| 2020-01-10 | 300 |
| 2020-01-15 | 400 |
My purpose is to get the object with the latest date before a given date. For example, given a date of 2020-01-12, the result should be the object whose date is 2020-01-10. I wonder if it's possible to do this with a single NSPredicate?
I tried the following but it doesn't work because max() is not aware of the other constraints (see a dicussion here)
request.predicate = NSPredicate(format: "date < %# AND date == max(date)", given_date as CVarArg)
I also considered SUBQUERY becuase that's the approach to do it in SQL. But unfortunately it seems that SUBQUERY in Core Data is supposed to be used with two tables (it requires an explicit collection argument).
I have read NSExpression document, but as far as I can tell, it's impossible to define an NSExpression to do it either (the above NSPredicate format string I tried is actually a NSExpression in string format so they use the same max()).
Does that mean I have to fetch multiple entries with NSPredicate(format: "date < %#", given_date as CVarArg) first and then run a second predicate to get the latest one? But isn't this inefficient because it fetch multiple entries although I only need one?
Am I missing something? Thanks for any suggestions.
Note: I considered setting fetchLimit to 1. But this doesn't work in my cases because there may be multiple objects with the same date and I want to get all of them if their dates meet the requirement.
It is possible to combine the two fetches into one. Rather than "running" the first fetch, pass it (as a NSFetchRequestExpression) to the main fetch request:
func fetchUpdates(_ date: Date) {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
request.predicate = NSPredicate(format: "date <= %#", date as CVarArg)
let expressionDescription = NSExpressionDescription()
expressionDescription.expression = NSExpression(format: "#max.date")
expressionDescription.name = "maxdate"
expressionDescription.expressionResultType = .dateAttributeType
request.propertiesToFetch = [expressionDescription]
request.resultType = NSFetchRequestResultType.dictionaryResultType
// Identical up to here, then:
let contextExpression = NSExpression(forConstantValue: self.managedObjectContext)
let fetchExpression = NSExpression(forConstantValue: request)
let fre = NSFetchRequestExpression.expression(forFetch: fetchExpression, context: contextExpression, countOnly: false)
let mainFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
mainFetch.predicate = NSPredicate(format: "date == %#", fre)
let results = try! self.managedObjectContext!.fetch(mainFetch)
....
Be aware that the Date attribute type includes time, so Updates occurring on the same DAY might have a different date.
I work out something like this:
func fetchUpdates(_ date: Date) {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
request.predicate = NSPredicate(format: "date <= %#", date as CVarArg)
let expressionDescription = NSExpressionDescription()
expressionDescription.expression = NSExpression(format: "#max.date")
expressionDescription.name = "maxdate"
expressionDescription.expressionResultType = .dateAttributeType
request.propertiesToFetch = [expressionDescription]
request.resultType = NSFetchRequestResultType.dictionaryResultType
// Run the request, this will return the the date
...
// Then construct a second fetch request to get the object(s) with the date
...
}
The function issues two fetch requests. If I understand it correctly, the first request still gathers multiple entries on SQLite layer and performs aggregation operation on Core Data layer. I'm not sure if this is better than the usual approach which issues only one fetch request but passes all entries to app layer and let app to search them (e.g., using NSPredicate) for the entry with max date.

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

NSFetchRequest fetch object with next closest string attribute

Say I have 5 Alphabet objects with an attribute letter of the given letter (of type String) persisted to Core Data.
Alphabet.letter = "A"
Alphabet.letter = "M"
Alphabet.letter = "G"
Alphabet.letter = "T"
Alphabet.letter = "D"
I can get them sorted with the following:
let alphabetFetch = NSFetchRequest(entityName: "Alphabet")
let alphabetSort = NSSortDescriptor(key: "letter", ascending: true)
Alphabet.letter = "A"
Alphabet.letter = "D"
Alphabet.letter = "G"
Alphabet.letter = "M"
Alphabet.letter = "T"
Now I want to retrieve only the Alphabet object with the closest following letter value after "G" (in this example I want to retrieve the Alphabet object with the letter attribute value "M"). How would I construct a fetch request to most efficiently fetch this object?
In addition to the sort descriptor, add a predicate and a fetch limit
to the request:
let request = NSFetchRequest(entityName: "Alphabet")
request.sortDescriptors = [ NSSortDescriptor(key: "letter", ascending: true) ]
request.predicate = NSPredicate(format: "letter > %#", "G")
request.fetchLimit = 1
The fetch request returns an array with a single element which
is the first matching object (according to the sort descriptor), or an empty array if no such element
exists.
Create an NSFetchRequest with a predicate to filter items > "G" and sort ascending.
let letter = "G"
let letterRequest = NSFetchRequest(entityName:"Alphabet")
letterRequest.predicate = NSPredicate(format: "letter > %#", letter)
letterRequest.sortDescriptors = [NSSortDescriptor(key: "letter", ascending: true)]
letterRequest.fetchLimit = 1

Using NSPredicate to filter Core Data entities without property equal to any of array property

I have a set of Price in Core Data
price1 = Price.newInManagedObjectContext(self.managedObjectContext!)
price1.price = 1
price1.size = 1
price1.market = market1
price2 = Price.newInManagedObjectContext(self.managedObjectContext!)
price2.price = 2
price2.size = 1
price2.market = market1
price3 = Price.newInManagedObjectContext(self.managedObjectContext!)
price3.price = 3
price3.size = 1
price3.market = market1
self.save()
Then I have an update set
let p1 = PriceUpdate(price: 1, size: 2)
let p2 = PriceUpdate(price: 4, size: 2)
let updates = [p1, p2]
And I want to fetch all Price objects in persistent store that don't have a price equal to any of the updates price. In the example that would be the price2 and price3 as price 2 and 3 are not in updates (1 and 4)
I've tried something like this but I'm not sure.
let request = NSFetchRequest()
request.entity = NSEntityDescription.entityForName("Price", inManagedObjectContext: self.managedObjectContext!)
request.predicate = NSPredicate(format: "%K == %#.%K", "price", updates, "price")
i think your can make one more attribute and insert 0 in your table attribute... and when you update your table than insert 1 in new attribute.. so when you fetched the data from table simple make a query with new attribute having 1 in table ...
You don't provide details of PriceUpdate, but I assume it's a struct or similar. Start by constructing an array holding just the prices:
let updatePrices = updates.map() { $0.price }
Then build a predicate which filters out the Price objects if their price is in the above array:
request.predicate = NSPredicate(format: "NOT %K IN %#", "price", updatePrices)

Setting NSPredicate in Swift CoreData

Am Rewriting my existing objective c code(ios) to swift and now am facing some issues with Coredata NSPredicate in swift as there is some many to one relationships and vice-versa.. am using the following code,
let fetchRequest = NSFetchRequest(entityName: "TUMessage")
let sortSequence = NSSortDescriptor(key: "sequence", ascending: true)
let sortTime = NSSortDescriptor(key: "epoch_time", ascending: true)
var filterPredicate:NSPredicate = NSPredicate(format:"thread == [c] %#",contact.threads.anyObject())
 fetchRequest.predicate = filterPredicate
var sortDescriptor:NSArray = NSArray(objects: sortTime,sortSequence)
fetchRequest.sortDescriptors = sortDescriptor
fetchRequest.fetchBatchSize = 50
return fetchRequest
where "TUMessage" is the table we need to fetch data from, "sequence" for sorting the fetched result, "thread == [c] %#" is a relationship of message table (many to one since each contact have multiple thread) ,
and contact.threads.anyObject() is the predicate which am trying to add.Unfortunately am getting the following error
type anyobject? does not confirm to protocol, CVarArgType
Any help would be appreciated..
Thank you
You need to unwrap it and cast to the specific class
contact.threads.anyObject()! as MYClass

Resources