I am learning core data and particularly working on aggregation.
Current what I want to do : count the number of records from the table which is in to-many relationship with inverse relationship on some criteria.
Currently I am doing this :
NSExpression *ex = [NSExpression expressionForFunction:#"count:"
arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:#"ddname"]]];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ddtype == 'Home'"];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:#"countDDEvents"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger16AttributeType];
NSArray *properties = [NSArray arrayWithObject:ed];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPredicate:pred];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DDEvent" inManagedObjectContext:[self.currentAccount managedObjectContext]];
[request setEntity:entity];
NSArray *results = [[self.currentAccount managedObjectContext] executeFetchRequest:request error:nil];
NSDictionary *dict = [results objectAtIndex:0];
NSLog(#"Average birthdate for female heroes: %#", [dict objectForKey:#"countDDEvents"]);
Its from jeff lemarche.
EDIT : and I have found my solution as
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ddtype == 'Home'"];
[request setPredicate:pred];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DDEvent" inManagedObjectContext:[self.currentAccount managedObjectContext]];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [[self.currentAccount managedObjectContext] countForFetchRequest:request error:&error];
It is working nicely .But I want to do more request of such type at a time . So i think this can't be a preferred way of getting the count .
EDIT :
So I think the approach would be the appropriate one ????
So can anyone tell me more efficient an preferred way of doing this .
Thanks .
I had to count about 10 000 entities and it slowed down my interface responsiveness a lot while doing it with countForFetchRequest..
Here is a way of doing it wth NSExpression:
- (NSUInteger) unfilteredFCsCount {
// Just the fetchRequest
NSFetchRequest *fetchRequest = [self unfilteredFCsFetchRequest];
[fetchRequest setResultType: NSDictionaryResultType];
// You can use any attribute of the entity. its important, because you are not counting
// the properties, but actually the entities
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"sortIndex_"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"fcCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObject:expressionDescription]];
NSUInteger fcCount = 0;
NSError *error = nil;
NSArray *results = nil;
results = [self.managedObjectContext executeFetchRequest: fetchRequest error: &error];
KSLog(KSLogLevelDebug, #"unfilteredFCsCount results: %#", results);
if([results count] > 0) {
NSNumber *count = [[results objectAtIndex: 0] objectForKey: #"fcCount"];
fcCount = [count intValue];
}
return fcCount;
}
Jeff LaMarche is just using this as a simple example. In practice, this need is so common that Key-Value Coding has a built in macro to handle it and other common collection operations.
See: The Key-Value Programming Guide: Set and Array Operators
In this case you would use the #count operator in your predicate.
Of course, hand tuning your own expression gives you fine control over your predicates but the operators handle 80% of such task.
Related
I find out the User has a sm_owner field. But how do I get the user using sm_owner. My code is like this:
[self.client getLoggedInUserOnSuccess:^(NSDictionary *result) {
NSString *currentLogginedUser = [result valueForKey:#"sm_owner"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedOjbectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sm_owner like user/%#", currentLogginedUser];
[request setPredicate:predicate];
self.user = [[self.managedOjbectContext executeFetchRequest:request error:nil] objectAtIndex:0];
} onFailure:^(NSError *error) {
}];
And the crash message is:reason: 'Unimplemented SQL generation for predicate (sm_owner LIKE user / "user/test")'
I'm the platform Evangelist for StackMob.
The result from the getLoggedInUserOnSuccess method normally contains primary key "username". So the following code should work.
[self.client getLoggedInUserOnSuccess:^(NSDictionary *result) {
NSString *currentLogginedUser = [result objectForKey:#"username"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedOjbectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username == %#", currentLogginedUser];
[request setPredicate:predicate];
self.user = [[self.managedOjbectContext executeFetchRequest:request error:nil] objectAtIndex:0];
} onFailure:^(NSError *error) {
}];
Alternatively, if you rather grab the sm_owner from the NSDictionary result. I would do the following.
NSString *sm_owner = [result valueForKey:#"sm_owner"];
NSArray *myArray = [sm_owner componentsSeparatedByString:#"/"];
NSString *currentLogginedUser = [myArray objectAtIndex:1];
Then perform your fetch.
Im trying to save a list of Device classes (a custom class) using Core Data and retrieve it. But After I save it, my query, which is VERY simple, doesnt return any records.
My call to save the records always returns YES and no errors:
BOOL resultOfSave = [managedObjectContext save:&err];
My predicate for searching by the property userLogin is like so:
- (NSArray *) devicesForUser:(NSString *)username
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Device" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userLogin = %#", username];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"macAddress" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (fetchResults == nil) {
// Handle the error.
NSLog(#"ERROR: nil fetch result array");
}
[request release];
NSLog(#"devicesForUser [%#] count %d", username, [fetchResults count]);
return fetchResults;
}
but I always get zero results, if I get rid of the predecate, I get all the objects, and if I loop through them I can see that the userLogin property for at least one of the object IS set to the username that I pass in...
Has anyone ever had an issue like this???
Thanks for any help you can give
Mark
In your predicate, change:
#"userLogin = %#"
...to:
#"userLogin == %#"
I want to retrieve a single value (String) from "Entry." I believe I am missing something. Can someone point it out?
XYZAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", entryToSearchFor];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entry" inManagedObjectContext:managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[request setEntity: entity];
[request setPredicate: predicate];
NSArray *results = [managedObjectContext executeFetchRequest:request error:nil];
if (results == nil) {
NSLog(#"No results found");
}else {
NSLog(#"entryToSearchFor %#", entryToSearchFor);
NSLog(#"results %#", [results objectAtIndex:0]);
}
Btw, the NSLog results outputs the following:
results <NSManagedObject: 0x3d2d360> (entity: Entry; id: 0x3d13650 <x-coredata://6EA12ADA-8C0B-477F-801C-B44FE6E6C91C/Entry/p3> ; data: <fault>)
NSFetchRequest will return an array of NSManagedObjects that matches the request. That is consistent with what you are seeing. Assuming you want to fetch the name, you can do NSLog(#"the name is %#", [[results objectAtIndex:0] name]); to print out the name.
NSFetchRequest *req = [NSFetchRequest init];
NSEntityDescription *descr = [NSEntityDescription entityForName:#"City" inManagedObjectContext:context];
[req setEntity:descr];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"streetName" ascending:YES];
[req setSortDescriptors:[NSArray arrayWithObject:sort]];
[sort release];
//fetch
NSError *error;
NSArray *result = [context executeFetchRequest:req error:&error];
//extract names
NSMutableArray *streets = [[NSMutableArray alloc] init];
for () {
??? = [array objectAtIndex:i];
[streets addObject:name];
}
I expected Core Data to be little more intuitive. I am new in it and I could use some help.
I fetched all objects(rows) from the entity (table) City. Now I have an array of objects. From the array I need to extract the attribute “streetName” to an array which will feed the picker. I figured I need to do it in the loop but I could not figure out the way to do it.
Please help.
I have a background with SQL but Core Data is still a big mystery to me. Is there any publication which would take a SQL statement and show comparable Core Data syntax?
Thanks.
It's very simple because of key-value coding:
NSArray *streets = [result valueForKey:#"streetName"];
i think core data changed quite a bit. here is how i do my fetch now
NSError *error;
NSFetchRequest *fr =[NSFetchRequest fetchRequestWithEntityName:#"Category"];
[fr setPredicate:[NSPredicate predicateWithFormat:#"name == \"myCategpry\""]];
NSArray *rs = [self.managedObjectContext executeFetchRequest:fr error:&error];
for (NSManagedObject *index in rs) {
NSLog(#"%#",[index primitiveValueForKey:#"name"]);
}
In my iPhone project, I want to write a function that checks wether there's an object in my Core Data ManagedObjectContext with a given value for a certain property, say some_property.
If there's already an object with some_property == 12, I want the function to return the object, otherwise, I want to create the object, or at least return nil.
How would I do that?
The following snippet shows how to retrieve the objects matching a specific predicate. If there are no such objects, the snippet shows how to create a new object, save it and return it.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourEntityName" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// retrive the objects with a given value for a certain property
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"property == %#", value];
[request setPredicate:predicate];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"yourSortKey" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
// 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:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
[sortDescriptor release];
[sortDescriptors release];
if ((result != nil) && ([result count]) && (error == nil)){
return [NSMutableArray arrayWithArray:result];
}
else{
YourEntityName *object = (YourEntityName *) [NSEntityDescription insertNewObjectForEntityForName:#"YourEntityName" inManagedObjectContext:self.managedObjectContext];
// setup your object attributes, for instance set its name
object.name = #"name"
// save object
NSError *error;
if (![[self managedObjectContext] save:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return object;
}
It's better if you don't do multiple fetching if you want to check for certain properties on the local data. Just do one fetch request using a pre-populated array and then iterate or filter the results.
This is a code snippet from Core Data Programming Guide "Implementing Find-or-Create Efficiently":
// get the names to parse in sorted order
NSArray *employeeIDs = [[listOfIDsAsString componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector: #selector(compare:)];
// create the fetch request to get all Employees matching the IDs
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:
[NSEntityDescription entityForName:#"Employee" inManagedObjectContext:aMOC]];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat: #"(employeeID IN %#)", employeeIDs]];
// make sure the results are sorted as well
[fetchRequest setSortDescriptors: [NSArray arrayWithObject:
[[[NSSortDescriptor alloc] initWithKey: #"employeeID"
ascending:YES] autorelease]]];
// Execute the fetch
NSError *error;
NSArray *employeesMatchingNames = [aMOC
executeFetchRequest:fetchRequest error:&error];