Fetchrequest with NSPredicate not returning right values - ios

I'm having a strange problem with Core Data.
The problem is that I'm looking for objects with a property less than X and it isn't returning all the values that matches.
There is no cache and I'm not using //[fetchRequest setFetchBatchSize:50];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.level <= [cd] %#", [self.filters objectForKey:#"level"]];
I add it to a MutableArray of predicates and later I execute
NSPredicate *myPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: subPredicates];
This one it's in a function that returns myPredicate called preparePredicates. For the moment there aren't more predicates, only one.
It's NSLog returns: level <=[cd] "30".
I have tried it also with intValue of the string and %i
The main function:
NSError* error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myEntity"
inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate* predicate = [self preparePredicates];
[fetchRequest setPredicate:predicate];
//[fetchRequest setFetchBatchSize:50];
NSArray *fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
NSLog (#"Error: %#", error);
NSLog(#"los fetchedobjects: %i",[fetchedObjects count]);
return fetchedObjects;
It doesn't return any error.
If I look at the results there isn't one having a level of 6, but there are others that matches (all are less than the specified). I know there is one with level 6.
If I open the .sqlite in SQLite Database Browser, I can see it there.
If I do with this program a
SELECT * FROM zmyentity WHERE zlevel <30;
it doesn't appear, same as in Core Data do. But if I do a
SELECT * FROM zmyentity WHERE zlevel == 6;
it appears.
I really don't know what is happening. Maybe I'm making a rookie mistake, so please point me in the right direction.
Thank you very much in advance.

Probably you have stored the level as a String attribute, because
"30" < "6"
when comparing strings. Choosing a numeric attribute type (Integer 16/32/64) should solve the problem. The corresponding level property in the myEntity class has then the NSNumber type.
You should also omit the [cd] modifier in the predicate, because that enforces a string comparison.
You predicate could then look like this:
[NSPredicate predicateWithFormat:#"self.level <= %d", [[self.filters objectForKey:#"level"] intValue]]

Related

NSPredicate to check whether a string of numbers separated by comma contains a number

I have a string say, "0,1,2,3,4,5,6,7,8,9,10,11" (Assumed to be months index string) in my Core Data. I want to use a predicate to fetch whether that field contains a number say, string contains '0'.
We cannot use 'CONTAINS' since '0' is also present in '10'. I need to fetch the object using NSPredicate to avoid loops from Core Data.
Update:
I just want to test whether '0,1,2,3,4,5,6,7,8,9,10,11' contains '0' or not using NSPredicate.
Solved
The "MATCHES" operator of NSPredicate can be used to compare against a regular expression:
NSString *searchTerm = #"0";
NSString *regex = [NSString stringWithFormat:#"(.*,)?%#(,.*)?", searchTerm];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"strIndex MATCHES %#", regex];
This worked fine for me. Thank you #skagedal for the suggestion.
I got this answer from Form NSPredicate from string that contains id's.
#bunty's answer is directly correct, but there is a meta answer.
You should probably have a Months entity that contains the 12 months of the year in the data store. Then, have a one-to-many (ordered or not depending on needs) relationship from the thing that contains that string to the Months entity.
Then, you simply follow the relationship (i.e. myThing.months) to fetch the months that the entity needs.
For large databases, fetches using string matching predicates are going to be quite slow and it really is a bit of anti-pattern.
As bunty writes, you can use regular expressions. If I understand the question, you want a predicate that looks for a specific string of digits – but only if not directly preceded or followed by other digits. This can be achieved with what in regular expressions is called look-behind and look-ahead assertions. More specifically, negative look-behind/look-ahead assertions.
Take a look at the syntax for regular expressions used by NSPredicate. To search for the number 1, without also finding 11 and 10, use the regular expression:
(?<!\d)1(?!\d)
So you can build your Core Data predicate with something like:
NSString *month = #"11"; // the number you're looking for
NSString *regex = [NSString stringWithFormat:#"(?<!\d)%#(?!\d)", month];
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
This will not be a very effective search, but if that's what your Core Data model looks like and you can't easily change it, it just might work fine. If you can, I'd go with bbum's suggestion and reorganize your model.
You can check numbers separated by comma by using regular expression as below:
NSString *str = #"0,1,2,3,4,5,6,7,8,9,10,11";
NSString *regex = #"(\\d+)(,\\s*\\d+)*";
NSPredicate *myTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
if ([myTest evaluateWithObject: str])
NSLog(#"matched");
else
NSLog(#"not matched");
So use this predicate while fetching data from core data with field name as below:
NSError* error = nil;
NSString *regex = #"(\\d+)(,\\s*\\d+)*";
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TableName" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"MonthFieldName MATCHES %#", regex];
[fetchRequest setPredicate:predicate];
NSArray* arrRecords = [context executeFetchRequest:fetchRequest error:&error];

Integer Comparison through Predicate

I am using core data in which I am having an entity called "Scan" who is having one attribute say electronic_id which is of NSNumber type. Now suppose in data base i am having 3 entries of this Scan attribute.
One entry of this entity having 35 as electronic_id.
Second entry of this entity having 354 as electronic_id.
Third entry of this entity having 375 as electronic_id.
I am performing searching on this attribute. so I when i start to search entity by passing 3 i want all these 3 objects.when i pass 35 then i want first two objects.
I want partial matches objects on NSNumber through predicate.
I am using this function.
-(void)filteredContentWithSearchText:(NSString*)searchText
{
if([searchText length])
{
int value=0;
value=[searchText intValue];
if ([strSelectedType isEqualToString:#"Electronic ID"])
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"electronic_id == %d",value];
NSManagedObjectContext *context =[self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Scan" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSError *error=nil;
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
if (error==nil)
{
}
else if (error!=nil)
{
NSArray *results =[APP_DELEGATE CallForGetScan:YES Predicateis:predicate];
[arrSessionSearchData removeAllObjects];
[arrSessionSearchData addObjectsFromArray:results];
}
}
}
}
Please help how to compare integer in NSPredicate.
Thanks in Advance.
Try cast integer to string and compare it
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"electronic_id.stringValue CONTAINS[c] %#",searchText];
#sage444 your answer is what I have looking for hours. Works like a charm.
I just provide Swift version for future readers:
let predicate = NSPredicate(format: "%K.stringValue CONTAINS[c] %#",
#keyPath(YOUR_MODEL.number),
substring)

Core Data: deleteObject crashes application, NSPredicate the cause?

I'm using a UICollectionView along with Core Data.
I can't seem to figure out why I can't remove an object from Core Data. It crashes at [self.managedObjectContext deleteObject:animation]; with an index 0 beyond bounds for empty array error. The following method is a delegate method that gets invoked from another viewcontroller. It gets a timestamp NSInteger and should create an NSPredicate with that timestamp.
The strange thing is that when I take the animationTimestamp NSInteger and hardcode it like:
[NSPredicate predicateWithFormat:#"timestamp == %i", #"1370169109"] it works and the object gets deleted without the error. However, when I want to pass the parameter as the argument it crashes. I've tried making it a string, NSNumber etc. Nothing works when I take the parameter, it only does when I hardcode it. I've logged animationTimestamp and it shows the correct value, so I'm stuck.
Thanks!
- (void)deleteAnimationWithTimestamp:(NSInteger)animationTimestamp {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Animations" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"timestamp == %i", animationTimestamp];
[fetchRequest setPredicate:predicate];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *animations = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *animation in animations) {
[self.managedObjectContext deleteObject:animation];
}
// Save core data
if (![self.managedObjectContext save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[self.animationsCollectionView reloadData];
}
EDIT:
More info that might matter. The timestamp attribute in the model is a 64 bit integer (should it be by the way?). When I create the object I use:
NSNumber *timestampNumber = [NSNumber numberWithInt:timestamp];
[newAnimation setValue:timestampNumber forKey:#"timestamp"];
Get the NSInteger value from NSNumber
Like
NSInteger timestampNumber = [[NSNumber numberWithInt:timestamp] integerValue];
// then use this integer
[newAnimation setValue:timestampNumber forKey:#"timestamp"];
// or in
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"timestamp == %d", timestampNumber];

NSPredicate, get results with a subset of one-to-many relationship

I'mm working around with Core Data and NSFetchedResultsController.
My Data Model looks like this:
Product with one-to-many relationship called dataLines.
The dataLine entity has a property name theWeek.
I want to fetch all Product where dataLines.theWeek == someValue. This is easily done with a subquery. But this returns all dataLines. Is it possible to create a NSPredicate that returns the Product and a subset if dataLines only with the dataLines == someValue?
What you want to achieve could be reached in two ways:
using a SUBQUERY
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18]];
or the ANY modifier
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", [NSNumber numberWithInt:18]];
You can do also the following if you need to check against multiple values:
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %# or $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18], [NSNumber numberWithInt:19]];
The same can be applied to ANY modifier. ANY ... OR ANY ....
Maybe if you share some code we could help you.
P.S. I suppose you don't use scalar values and theWeek is a number.
Hope it helps.
You should fetch the dataLine property instead.
Assuming your Product and dataLine entity connected by relationship someRelation then you can try this code;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityWithName:#"dataLine" inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"dataLines.week == %#",theWeek]];
NSMutableArray *tmpProduct [[NSMutableArray init] alloc];
NSMutableArray *tmpArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (dataLine *theDataLine in tmpArray);
NSLog(#"%#",theDataLine.someRelation.name);
tmpProduct = theDataLine.someRelation.name;
then you can just call tmpProduct to call or display your product in table view
Create a fetch request for the 'Product' entity:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: [NSEntityDescription entityForName:#"Product" ...]]
then create a predicate using the properties/attributes of Product with 'ANY':
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", <whatever week>]];
then execute the fetch to get an array of Product with at least one <whatever week>.
Generally see 'Fetching Managed Objects', NSPredicate and related documentation.

Core Data: NSPredicate containing to-many relationship yields no results when using SQLITE store type

I have been using Core Data with In-Memory store for my project until recently. Now I am trying to switch to SQLite store and I'm facing the following issue:
When Trying to fetch objects from the store using a predicate and sortDescriptor, my fetchedResultsController returns 0 objects. The exact same code works fine when the store type is in-memory.
This is how I am making the fetch:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY %K == %#", #"categories", category];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES selector:#selector(caseInsensitiveCompare:)];
self.fetchedResultsController = [self createFetchedResultsControllerWithEntityName : #"Provider" sectionName : #"group" sortDescriptor : sortDescriptor predicate : predicate cache : nil];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
and this is where I create the fetch request:
- (NSFetchedResultsController *)createFetchedResultsControllerWithEntityName : (NSString*) entityName sectionName : (NSString*) sectionName sortDescriptor : (NSSortDescriptor*) sortDescriptor predicate : (NSPredicate*) predicate cache : (NSString*) cacheName{
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//set predicate
if (predicate)
[fetchRequest setPredicate:predicate];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:sectionName
cacheName:cacheName];
aFetchedResultsController.delegate = self;
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return [aFetchedResultsController autorelease];
}
Again, this very same code fetches many objects when using in-memory store, but an empty fetchedObjects array when using SQLite.
I've read Apple's documentation with regard to the special considerations to make when using predicates and sortDescritors with SQLite store. I am using a single (many-)to-many relationship (categories) in the predicate, so this should be fine?
There is no error returned from the fetch. Simply no objects (fetchedResultsController.fetchedObjects is empty).
Any guidance would be greatly appreciated.
Thanks
I've figured out this issue eventually.
My query returned no results there were indeed no results. Tracing this back to where I was creating the objects and populating the realtionships with values I noticed that I was doing it wrong. I tried to do:
[provider.categories addObject : newCategory];
Which did not really add newCategory to the categories relationship.
After having realized that Core Data generates the accessors for to-many relationships automatically, I used the following to add newCategory:
[provider addCategoriesObject : newCategory];
This properly added newCategory to the relationship.
In addition to that I declared addCategoriesObject in Provider.h to suppress the compiler warning. This resolved the issue for me.
The only remaining caveat is that the compiler does not find the implementation of addCategoriesObject: and warns about "incomplete implementation". I haven't figured out a way to suppress this warning too.
I know SQLite stores have problems with "ANY" fetches in some cases (I don't remember the exact ones). Maybe try modifying your search predicate to see if you get different results, e.g.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"categories LIKE %#", category];

Resources