I need some help with this method:
// deletes all points in database except points with given identifiers
- (void)deleteAllPointsExcept:(NSArray *)safeIdentifiers save:(BOOL)save {
// create request to fetch all 'doomed' points
NSManagedObjectContext *context = [[TSAppDelegate delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MapPoint"];
NSMutableArray *subpredicates = [NSMutableArray array];
for (NSNumber *identifier in safeIdentifiers) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"pid!=%#", identifier];
[subpredicates addObject:pred];
}
fetchRequest.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
// fetch 'em & destroy 'em
NSError *error;
NSArray *points = [context executeFetchRequest:fetchRequest error:&error];
for (TSMapPoint *point in points) {
[context deleteObject:point];
}
// save if requested
if (save) {
[[[TSAppDelegate delegate] managedObjectContext] save:NULL];
}
}
It's supposed to purge unneeded MapPoints from my Core Data NSManagedObjectContext. It works fine but one day I've received this message:
CoreData: error: (1) I/O error for database at ... SQLite error code:1, 'Expression tree is too large (maximum depth 1000)'
I've googled that this happens when the predicate is too long like WHERE id=1 OR id=2 OR id=3 ... but I don't know how to work around this. Does anybody have an idea?
Thanks in advance,
Pete.
Your predicate should be simplified to something like the following
[NSPredicate predicateWithFormat:#"NOT (pid IN %#)", safeIdentifiers];
When you have too many identifiers, you cannot use them directly in an SQL command (regardless of whether you're using parameters or not).
You have to write all the IDs into a temporary table, and then use a single predicate that checks that table:
DELETE FROM PointsTable WHERE pid NOT IN MyTempTable
Related
in my app i have two entities: Members and Lists. they both have a one-to-many relationships (member can have more than one list, list can have more than one member). now i want to fetch the lists belonging to a specific member. here is my code:
WSAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Lists" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"has_members contains[cd] %#", [self.currentMember valueForKey:#"name"]]];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error.
NSLog(#"NO LISTS AVAILABLE IN REFRESH");
}
self.currentMember is a managed object of the user himself.
Note: member has name, (NSSet*) member_of_list
list has list_name, has-members
Problem: when i run the code it's breaking at the fetchedObjects array. i suspect that there is something wrong with the NSPredicate but i don't know where and how to fix it. can any one point out the problem?
First, the relationship you describe between Member (Calling an entity in a plural form is confusing) and List is many-to-many.
Second, instead of using CoreData's inherent object graph capabilities, you went and "rolled your own" relationship between the entities (you should use your interface builder to model a CoreData relationship between the two entities).
See HERE how to do that.
after you model your data, your predicate should look something like:
//Not tested
NSPredicate* p = [NSPredicate predicateWithFormat:#"ANY members = %#",self.currentMember];
DO NOT pass a formatted string to create the predicate, use NSPredicate formatting to substitute parameters or you will not be able to accomplish your goal (in most cases).
I currently have a table view that displays all the contents of the database Entity: ExData.
ExData has an attribute tag of string type.
What my problem is that i would like to display the contents of the ExData in the table view but only the entries that have a tag set of 2 for example.
The tag is to be sent from the previous view controller but this can be sorted out later as firstly i would just like to hard code only one tag value entries being displayed
ExDatasArray is a mutable Array.
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"ExData"];
self.ExdatasArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
Above is how the data from ExData is being fetched...
To fetch specific data, you add a predicate to the fetch request:
NSString *theTag = ...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"tag == %#", theTag];
[fetchRequest setPredicate:predicate];
Remark: If you are displaying the result set in a table view, you might also consider to
use a NSFetchedResultsController.
If I understand your question correctly, you are trying to limit the results of the fetchRequest. Use an NSPredicate to specify the query and if desired an NSSortDescriptor to sort it. Below I assume that your tag attribute is named tag, and that searchTagValue has been set appropriately.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"ExData"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"tag == %#", searchTagValue];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"tag" ascending:YES]];
NSError *error;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (!result) {
// handle error
}
else {
self.ExdatasArray = [results mutableCopy];
}
I am trying to understand how to run simultaneous searches in core-data.
Here is my example, but it doesn't work, because one of the GCDs seems to never activate
If I leave the custon MOC in there I get an error "unable to find model for entity 'Recipe''
-(void)performSearch:(NSString*)name{
//TODO: Is there a better way
//In case the previous search hasn't finished
if (globaDispatchRequestInprogress) {
//Send on GCD
dispatch_queue_t searchQueque = dispatch_queue_create("search queque 2", NULL);
dispatch_async(searchQueque, ^{
NSLog(#"\n\nDispatch 2 In Progress*******\n\n");
//Init local variables
NSFetchRequest *fetchRequest =[[NSFetchRequest alloc]init];
NSError *error;
//Create own MOC for multiThread
NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc]init];
[tempContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
NSPredicate *recipeName = [NSPredicate predicateWithFormat:#"ANY recipe.name ==[c] %#",name];
//Set predicate to fetch request
[fetchRequest setPredicate:recipeName];
//Set query. We are searching recipes
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:tempContext];
//sets up fetch request details
[fetchRequest setEntity:entity];
//Attempt to speed up program
[fetchRequest setReturnsObjectsAsFaults:NO];
//Perform fetch assign to return array
NSArray*records = [tempContext executeFetchRequest:fetchRequest error:&error];
//Add to temporary results
//TODO: Add to NSDictionary
[self addResultsToTemporaryResults:records];
NSLog(#"Total results = %i",[_temporaryResultsArray count]);
NSLog(#"\n\nDispatch 2 END**************\n\n");
});
}
//Send on GCD
dispatch_queue_t searchQueque = dispatch_queue_create("search queque", NULL);
dispatch_async(searchQueque, ^{
NSLog(#"\n\nDispatch In Progress*******\n\n");
//Set flag
globaDispatchRequestInprogress=YES;
//Init local variables
NSFetchRequest *fetchRequest =[[NSFetchRequest alloc]init];
NSError *error;
//Create own MOC for multiThread
NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc]init];
[tempContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
NSPredicate *recipeName = [NSPredicate predicateWithFormat:#"ANY recipe.name ==[c] %#",name];
//Set predicate to fetch request
[fetchRequest setPredicate:recipeName];
//Set query. We are searching recipes
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:tempContext];
//sets up fetch request details
[fetchRequest setEntity:entity];
//Attempt to speed up program
[fetchRequest setReturnsObjectsAsFaults:NO];
//Perform fetch assign to return array
NSArray*records = [tempContext executeFetchRequest:fetchRequest error:&error];
//Add to temporary results
//TODO: Add to NSDictionary
[self addResultsToTemporaryResults:records];
NSLog(#"Total results = %i",[_temporaryResultsArray count]);
globaDispatchRequestInprogress=NO;
NSLog(#"\n\nDispatch END**************\n\n");
});
}
I see several things that make me suspicious, but no obvious smoking gun.
If you're seeing "unable to find model", that suggests that your persistent store coordinator is not being configured the way you think it is. It would be interesting to NSLog self.persistentStoreCoordinator.managedObjectModel, and also self.persistentStoreCoordinator.managedObjectModel.entitiesByName.
The preferred GCD approach to Core Data is to use performBlock: or performBlockAndWait:, with the proper concurrency type for your managed object context. See http://developer.apple.com/library/mac/#releasenotes/DataManagement/RN-CoreData/index.html
You're keeping the results of your fetch around, in your addResultsToTemporaryResults: call. We don't see the source for it, but is it thread-safe? Those records you found have no existence outside of the tempContext you fetched them in, and may only be accessed from the thread that found them. You probably want to be using NSManagedObjectIDs there (and perhaps you already are).
Your second call to dispatch_queue_create() will always be executed. Did you mean to do an if-else instead of a simple if?
When you do -executeFetchRequest:error:, check the result. If it's a nil result, take a look at the NSError you passed in.
my program has a sqlite database with two related tables. One called "Rank" and other one called "Requirement"
I want to fetch all rows from the "Requirement" table that has a relationship with the specific row in a "Rank" table. Following is my code, it grabs the whole table, but I get the specified rows only according to the above mentioned rule.
-(NSArray *) getAllRequirementsForTheRank:(Rank *) rank
{
NSError *error;
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init]autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Requirement" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate *searchType = [NSPredicate predicateWithFormat:#"Rank = %#", rank];
[fetchRequest setPredicate:searchType];
NSArray *scoutRequirementArray = [self.context executeFetchRequest:fetchRequest error:&error];
for (Requirement *r in scoutRequirementArray)
{
NSLog(#"Requirementttt : %# :", r.requirementName);
}
return scoutRequirementArray;
}
If you have the relationship modelled in core data, just get the linked objects from the relationship property. You don't need another fetch request. rank.requirements will give you an NSSet of everything you need. (I'm assuming names for your object and properties here).
I m trying to update some records in Core Data. I m adopting following steps to get it done
Fetch function with predicate retrieves the records from the Core Data
Store the result set in a Object Array
Loops through the array and update each record
Call save context
I m running into two problems
After Initial run i get < fault > in the log
I m not sure whether the save context will actually save the object
My code:
- (void)fetchExpenses {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Expense" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"publishTimestamp == nil"];
[request setPredicate:predicate];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setExpenseArray: mutableFetchResults];
[mutableFetchResults release];
[request release];
}
- (void) save: {
[self fetchExpenses];
int i = 1;
int max = [expenseArray count];
for(i=1; i<=max; i++) {
// Get the expense selected.
Expense *expense = [expenseArray objectAtIndex: i];
// Do your updates here
[expense setTimestamp:2]
}
}
The fault you are seeing in the log doesn't indicate an error but means that the managed object is not fully loaded into memory but is instead represented by a fault object. This is normal behavior. When you try to access or change an object attribute the full object will be "faulted" or read-in to memory. It's a confusing old-fashion database terminology that dates back to 1960s.
Your code does not save any objects. Changes to managed objects in memory will not be persisted until you call a save on the managed object context.
You also do not want to use a mutable copy like this:
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
… because it waste memory and can lead to duplicate managed objects. There was some code in Apple docs that got this started but its erroneous. Instead, just use:
NSArray *fetchResults=[managedObjectContext executeFetchRequest:request error:&error];
… which will return an autoreleased array of the managed objects matching the fetch.