setFetchBatchSize on a NSFetchRequest on the iPhone - iphone

I don't understand the meaning of setFetchBatchSize.
In the following code, I set the fetch batch size to 20, but I still get 49 objects in my results array (which are ALL the objects from the query)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"company.id = %#", company.id];
NSFetchRequest *req = [CategoryEntity requestAllSortedBy:#"id" ascending:YES withPredicate:predicate];
[req setFetchBatchSize:20];
NSArray *results = [CategoryEntity executeFetchRequest:req];
So what's the meaning of setFetchBatchSize, since it doesn't seem to limit the request?

To limit the size use fetchLimit.
With batch size, the objects are faulted. They look like they are there but those are just stubs. When you access them - behind the scenes - the values are then populated.

Related

How to make NSExpression's expressionForFunction:withArguments: honour the fetch request's predicate

I'm using Core Data to store simple entities that consist of a value and a timestamp. I'm looking to make a fetch request which returns the latest value added, as well as a running average of all values. This all seemed straightforward enough until I attempted to use an NSPredicate to filter the results to within a certain time period.
I'm using NSExpression's expressionForFunction:withArguments: method to determine the average. By setting the launch flag -com.apple.CoreData.SQLDebug 1, I can clearly see that only the latest value is adhering to my date predicate. The average calculating is instead being performed as a subquery, but not taking my date predicate into account:
SELECT (SELECT avg(t1.ZVALUE) FROM ZEVENT t1 WHERE t1.Z_ENT = ?), t0.ZVALUE FROM ZEVENT t0 WHERE ( t0.ZTIMESTAMP >= ? AND t0.Z_ENT = ?) ORDER BY t0.ZTIMESTAMP DESC
What's the most efficient (and scalable) way of determining the average value while still honouring my NSFetchRequest's predicate?
For reference, here is my code in it's entirety:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Reading" inManagedObjectContext:moc];
[request setEntity:entity];
[request setSortDescriptors:#[sortDescriptor]];
[request setResultType:NSDictionaryResultType];
// Filter by date where necessary
NSPredicate *datePredicate = [NSPredicate predicateWithFormat:#"(timestamp >= %#)", toDate];
[request setPredicate:datePredicate];
NSExpression *valueExpression = [NSExpression expressionForKeyPath:valueKey];
NSExpressionDescription *valueDescription = [[NSExpressionDescription alloc] init];
[valueDescription setName:#"value"];
[valueDescription setExpression:valueExpression];
[valueDescription setExpressionResultType:NSDoubleAttributeType];
NSExpression *avgExpression = [NSExpression expressionForFunction:#"average:" arguments:[NSArray arrayWithObject:valueExpression]];
NSExpressionDescription *avgDescription = [[NSExpressionDescription alloc] init];
[avgDescription setName:#"averageReading"];
[avgDescription setExpression:avgExpression];
[avgDescription setExpressionResultType:NSDoubleAttributeType];
[request setPropertiesToFetch:#[avgDescription, valueDescription]];
I see two errors. There's no initialization shown for toDate. I also notice that you are passing setPropertiesToFetch: an array of NSExpressions, but the documentation calls for an array of NSPropertyDescriptions. I would expect that discrepancy to cause a null result, and populated NSError, when you execute the fetch request.
What result do you see from executeFetchRequest:error:? Be sure to check the NSError result. Idiom is something like this:
NSError *error;
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
if (!results) {
NSLog(#"%# fetch error for request %#: %# %#", fetchRequest,
error.localizedDescription, error.localizedFailureReason);
}
I would take a different approach. One fetch request has a limit of 1, sort timestamp descending, and returns the latest timestamp. Add a predicate if you like, for time restriction. Then use a second fetch request to compute the average of the timestamps. You could even encapsulate these calls into their own methods:
-(NSDate *)latestTimestamp:(NSManagedObjectContext *)moc;
-(NSNumber *)averageValueSinceTime:(NSDate *)intervalStart
context:(NSManagedObjectContext *)moc;

fetching objects from core data not in a set

I'm trying to fetch objects from core data that are not in a given set, but I haven't been able to get it to work.
For instance, suppose that we have a core data entity named User, which has a few attributes such as userName, familyName, givenName, and active. Given an array of strings representing a set of usernames, we can easily fetch all the users corresponding to that list of usernames:
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User"
inManagedObjectContext:moc];
[request setEntity:entity];
NSArray *userNames = [NSArray arrayWithObjects:#"user1", #"user2", #"user3", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userName IN %#", userNames];
[request setPredicate:predicate];
NSArray *users = [moc executeFetchRequest:request error:nil];
However, I want to fetch the complement of that set, i.e., I want all the users in core data that don't have the usernames specified in the userNames array. Does anyone have an idea how to approach this issue? I thought it would be simple enough to add a "NOT" in the predicate (i.e., "userName NOT IN %#"), but Xcode throws an exception saying the predicate format could not be parsed. I also tried using the predicate builder available for fetch requests with no luck. The documentation wasn't particularly helpful either. Suggestions? Comments? Thanks for all your help :)
In order to find the objects that aren't in your array, all you have to do is something like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT (userName IN %#)", userNames];
That should return a request of all the objects without the ones you specified
I am not strong at core data/objective-c but the predicate should be like the following statement;
[predicateFormat appendFormat:#"not (some_field_name in {'A','B','B','C'})"];
An example:
NSMutableString * mutableStr = [[NSMutableString alloc] init];
//prepare filter statement
for (SomeEntity * e in self.someArray) {
[mutableStr appendFormat:#"'%#',", e.key];
}
//excluded objects exist
if (![mutableStr isEqual:#""])
{
//remove last comma from mutable string
mutableStr = [[mutableStr substringToIndex:mutableStr.length-1] copy];
[predicateFormat appendFormat:#"not (key in {%#})", mutableStr];
}
//...
//use this predicate in NSFetchRequest
//fetchRequest.predicate = [NSPredicate predicateWithFormat:predicateFormat];
//...
Here's another useful example, showing how to take a list of strings, and filter out any which DON'T start with the letters A-Z:
NSArray* listOfCompanies = [NSArray arrayWithObjects:#"123 Hello", #"-30'c in Norway", #"ABC Ltd", #"British Rail", #"Daily Mail" #"Zylophones Inc.", nil];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"NOT (SELF MATCHES[c] '^[A-Za-z].*')"];
NSArray *filteredList = [listOfCompanies filteredArrayUsingPredicate:bPredicate];
for (NSString* oneCompany in filteredList)
NSLog(#"%#", oneCompany);
I use this kind of NSPredicate when I'm populating a UITableView with an A-Z index, and want an "everything else" section for items which don't start with a letter.

Using NSPredicate to get an array of Core Data objects?

Say I have a Core Data entity called Person. How would I get an NSArray of Persons whose properties match certain values? For instance someone of a particular age, height, or weight... or someone with a whose height,weight and age are specific values...
Can I use an NSPredicate like so:
NSPredicate *pred =
[NSPredicate predicateWithFormat:
#"(age == 25) OR (height_in_cms == 185) OR (age == 30 AND height_in_cms == 170 AND weight_in_kgs == 80)";
// All properties are NSNumber
I'm not an expert on the syntax for predicateWithFormat:, but you have the basic gist. You can find details on the format in Apple's Predicate Programming Guide. If you're asking what to do with the predicate once you have it, here is a snippet that shows you the steps:
// Create a fetch request.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Set the entity for the fetch request.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[entity release];
// Set the predicate for the fetch request.
[fetchRequest setPredicate:predicate];
// Perform the fetch.
NSError *error;
NSArray *array = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
If you want the results to be sorted, you can pass an array of sort descriptors to the fetch request using setSortDescriptors: prior to executing the fetch.
You can follow the given statement if you have these value in a variable.
[fetchResults filterUsingPredicate:[NSPredicate predicateWithFormat:#"age == %i OR hieght== %i AND weight==%i",age,height,weight]];
And also your approach is correct in case for specific values but your statement having syntax error so maintain proper syntax

Fetch Specific Number of Random Rows from CoreData

I'm using the code below to Fetch a queried set of all rows using CoreData matching the search criteria: itemType = 1.
But what I need to do is to Fetch a specific number of Random rows from the data instead.
For example, instead of retrieving all 100 rows of data in which the column name dataType = 1, I need to get 25 rows randomly in which dataType = 1.
I'm hoping there is relatively painless solution.
Any help is appreciated.
lq
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"MyAppName"
inManagedObjectContext:[self managedObjectContext]]];
NSError *error = nil;
NSPredicate *predicate;
NSArray *fetchResults;
predicate = [NSPredicate predicateWithFormat:#"(itemType = %i)", 1];
[request setPredicate:predicate];
fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (!fetchResults) {
// NSLog(#"no fetch results error %#", error);
}
self.mutableArrayName = [NSMutableArray arrayWithArray:fetchResults];
[request release];
You can not actually fetch random rows. A reasonable randomization strategy may be to fetch all of the objects matching your predicate, and then randomly select a specific number of objects.
Anyway you can use the following methods of NSFetchRequest:
- (void)setFetchLimit:(NSUInteger)limit
- (void)setFetchOffset:(NSUInteger)limit
Basically, setFetchLimit allows you to define how many rows you want to fetch (in your case you will set limit to 25), while setFetchOffset defines the offset at which rows will begin being returned (see the documentation of the fetchOffset property for details).
This is not a random process, but you may randomly generate the offset. However, it is worth noting here that, depending on the offset, you may then fetch a number of objects falling between zero and your fetch limit.
You could also use reference approach. When you sort by view counts.
I posted long time ago about it: http://www.alterplay.com/ios-dev-tips/2010/06/fetch-random-record-with-coredata.html
Sorry for formatting. it's broken after switching from Blogger to Wordpress.

Core Data : How to check for the presence of Many to Many relationship

I have a "Song" Entity and a "Tag" entity and they have a many to many relationship between them. A Song can have multiple Tags and a Tag can be applied to multiple Songs.
I want to check if a Song has a particular Tag associated with it. If the Song has the Tag associted with it, I want to show a checkmark in the table view.
For a similar logic, in Apple "TaggedLocations" sample code, the following check is made to check for the presence of the relationship.
if ([event.tags containsObject:tag]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
This may be inefficient if there are a lot of Tags in the database as this will fetch all of them in the memory. Please correct me if I am wrong here.
Is there a more efficient way to check if the Song is associated with a particular Tag instead of checking in Song.Tags?
It's actually pretty easy to do, if completely undocumented. You want to create a fetch request with a predicate that has a set operation. If we imagine that your Tag model has a property called tagValue, the predicate you care about is "ANY tags.tagValue == 'footag'"
NSString *tagSearch = #"footag";
// However you get your NSManagedObjectContext. If you use template code, it's from
// the UIApplicationDelegate
NSManagedObjectContext *context = [delegate managedObjectContext];
// Is there no shortcut for this? Maybe not, seems to be per context...
NSEntityDescription *songEntity = [NSEntityDescription entityForName:#"Song" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:songEntity];
// The request looks for this a group with the supplied name
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.tagValue == %#", tagSearch];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
[request release];
You are correct, using that code will retrieve the entire set and the object comparison may be quite complex, depending on how many properties and relationship are part of the object's entity.
Anyway, you can not avoid a set comparison for inclusion. Probably, the best you can do is to avoid fetching all of the properties/relationships by asking Core Data to retrieve NSManagedObjectID Objects only.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Tag" inManagedObjectContext:[self managedObjectContext]]];
[fetchRequest setResultType:NSManagedObjectIDResultType];
NSManagedObjectID objects are guaranteed to be unique, therefore you can safely use them to check for set inclusion. This should be much more efficient for a performance perspective.