Core data crash when fetching over 1000 objects - ios

When I try to fetch more than 1000 NSManagedObjects from Core Data, my app crashes with this message:
error: (1) I/O error for database at .../Documents/Stores/Model.sqlite.
SQLite error code:1, 'Expression tree is too large (maximum depth 1000)'
CoreData: error: (1) I/O error for database at .../Documents/Stores/Model.sqlite.
SQLite error code:1, 'Expression tree is too large (maximum depth 1000)'
The code I use to fetch the objects selected by the user is this:
NSManagedObjectContext *context = cdh.context;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Spot" inManagedObjectContext:context];
NSError *error = nil;
[request setEntity:entity];
request.includesPropertyValues = NO;
NSMutableArray *subPredicatesArray = [NSMutableArray array];
for (NSString *string in uuidStrings)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#", #"sID", string];
[subPredicatesArray addObject:predicate];
}
NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicatesArray];
[request setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:request error:&error];
Is there a better way to fetch 1000+ objects that won't cause my app to crash?

Assuming the uuidStrings contain exact matches for the sID attribute, you should be able to replace the code with this (untested):
// remove subPredicatesArray, the loop and compoundPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sID IN %#", uuidStrings];
[request setPredicate:predicate];

Related

Unable to fetch specific column that from core data

I have created a an entity called Statutory with four columns namely name,complianceName,statMappingName,country
i want a get all complianceName names that matches a specific statMappingName. Following is my code
NSString *nameToGet = self.statNameArray[indexPath.row] ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"complianceName = %#", nameToGet];
[fetch setPredicate:predicate];
NSLog(#"n %#",predicate);
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetch error:&error];
if(results) {
NSLog(#"Entities with that name: %#", results);
} else {
NSLog(#"Error: %#", error);
}
But it is not providing the compliance name specific to that statMappingName. How can I be able to get all complianceName that has a specific statMappingName?
Try this:-
NSString *firstName = statMappingName;
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"firstName == %#", firstName]];
If you want to fetch specific column from core data
You can use the below method:-
NSEntityDescription *entity = [NSEntityDescription entityForName:entityname inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setReturnsObjectsAsFaults:NO];
[request setEntity:entity];
[request setFetchLimit:1];
[request setPredicate:[NSPredicate predicateWithFormat:#"(complianceName LIKE %#)",nametoGet]];
NSError *error = nil;
NSMutableArray *tempDataArr = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
Note:-
*entityname: Your entity name
*self.managedObjectContext : Your managedobjectcontext
To Get All compliance Name use the below method
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:entityName];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSMutableArray *tempDataArr =[NSMutableArray new];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(ANY complianceName LIKE %#)",nametoGet]];
tempDataArr = [[self.managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
try these code for contains statMappingName
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"complianceName contains[c] %#", statMappingName];

Filter title from core data

I have an entity that has title title and amount. I would like to display the total value I have for each title in my entity.
You can pretty much dispense with fetch requests by leveraging KVC (Key-Value-Coding).
First fetch (or filter) with this predicate:
[NSPredicate predicateWithFormat:#"category = %#", #"Food"];
Then just sum up the result in one line:
NSNumber *sum = [result valueForKeyPath:#"#sum.amount"];
There is no need to fetch only certain attributes using NSDictionaryResultType - just fetch the normal NSManagedObjects (Core Data will optimize for you).
You use NSPredicate to filter your objects by category and then you can fetch only the values of a certain property e.g.:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Entity
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Incoming" inManagedObjectContext:context];
[request setEntity:ent];
// Predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"category MATCHES[cd] %#", #"FOOD"];
[request setPredicate:pred];
// Property fetch
[request setResultType:NSDictionaryResultType];
[request setReturnsDistinctResults:NO];
[request setPropertiesToFetch:#[#"Amount"]];
// Execute the fetch.
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
int total = 0;
if (objects) {
for(NSDictionary *dict in objects) {
NSString *key = dict.allKeys.lastObject;
total += [dict[key] intValue];
}
} else {
NSLog(#"Fetch error: %#", error.localizedDescription);
}
NSLog(#"Total: %i", total);
The following will get the categories and the sum in one fetch operation:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Incoming"];
fetchRequest.resultType = NSDictionaryResultType;
NSExpression *sumExp = [NSExpression expressionWithFormat:#"sum:(amount)"];
NSExpressionDescription *sumED = [[NSExpressionDescription alloc] init];
sumED.name = #"sum";
sumED.expression = sumExp;
sumED.expressionResultType = NSDoubleAttributeType;
fetchRequest.propertiesToFetch = [#"title", sumED];
fetchRequest.propertiesToGroupBy = [#"title"];
NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
See the NSExpression documentation here.

Subquery Predicate Core Data

I have two Entity models "Content" and "LessonsContent" with no relationship between them. Both entities have common attribute "contentID" of NSNumber type. I am fetching all contents objects from "Content" entity for given predicate and storing these objects in contents array. I was successful in doing so.
Now, I want to fetch all lessonsContent objects from "LessonsContent" entity whose contentIds are present in contents array.
I am using following code:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"(contentType == %#)", #"Games"];
NSError * error;
NSFetchRequest * request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Content class])];
[request setPredicate:predicate];
[request setReturnsObjectsAsFaults:NO];
NSArray *contents = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
if(error){ NSLog(#"Error in fatching data");}
//predicate = [NSPredicate predicateWithFormat:#"Content.contentID IN %#", contents];
predicate = [NSPredicate predicateWithFormat:#"(SUBQUERY(contents, $x, $x.Content.contentID IN %#).#count != 0)",contents];
request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([LessonsContent class])];
[request setPredicate:predicate];
NSArray * mappingArray = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
NSLog(#"mappingArray %#", mappingArray);
But app is crashing. Please suggest appropriate solution.
Thanks in advance.
You have to get an array of contentID's first. Then use IN.
NSArray *contents = [[DELEGATE managedObjectContext] executeFetchRequest:request error:&error];
NSArray * allContentIDs = [contents valueForKey:#"contentID"];
then use the second predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"contentID IN %#", allContentIDs];
[request setPredicate:predicate];
Hope this helps.

NSPredicate over different entities

I want to make NSPredicate over different entities. Here is my core database.
To make it simple, this is the query I should do.
Select c.c_name From Company c, Person p Where c.c_id = p.cu_company_id
Now I was wondering how my predicate should look like if I want to achieve the result like the query above.
Assuming that c_id and cu_company_id are integers, you can try
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"cu_company_id == data.company.c_id"];
[fr setPredicate:predicate];
NSArray *persons = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have persons array, you can then loop through it and get the person's name. To get it the other way around
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Company"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"c_id == data.person.cu_company_id"];
[fr setPredicate:predicate];
NSArray *companies = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have companies array, you can then loop through it and get the company's name.

ios core data - copy entity records to another entity

I have 2 entity, Units and BUnits, the Units entity have a data that will be replaced many time, and the BUnits will be the backup of the Units entity before clearing it's data.
So, I've created a NSManagedObjectContext instance, then I've retrive an instance for each entity by using
NSManagedObjectContext *context = [mainDelegate managedObjectContext];
NSEntityDescription *unitsEntity = [NSEntityDescription entityForName:#"Units" inManagedObjectContext:context];
NSEntityDescription *bUnitsEntity = [NSEntityDescription entityForName:#"BUnits" inManagedObjectContext:context];
but i've didn't managed to copy the Units entity records to the BUnits entity, other than make a loop for each records, but i believe that there is a better solution.
What do you think about this, is there a better solution?
UPDATE:
The solution i've used in case anyone could use it is in my answer, I think there is a better way to do this, i will keep checking for it and i will update the question if i found anything.
Here is what i've used, using looping for each record:
- (void) copyEntities:(NSString *)sourceEntity And:(NSString *)destinationEntity {
NSManagedObjectContext *context = [mainDelegate managedObjectContext];
NSEntityDescription *Units = [NSEntityDescription entityForName:sourceEntity inManagedObjectContext:context];
NSEntityDescription *BUnits = [NSEntityDescription entityForName:destinationEntity inManagedObjectContext:context];
NSFetchRequest *dataRequest = [[NSFetchRequest alloc] init];
[dataRequest setEntity:Units];
NSError *error = nil;
NSArray *dataArray = [context executeFetchRequest:dataRequest error:&error];
for (UnitsClass *unit in dataArray) {
UnitsClass *savedUnits;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:BUnits];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(code = %#)", unit.code];
NSPredicate *pred1 = [NSPredicate predicateWithFormat:#"(code2 = %#)", unit.code2];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:pred, pred1, nil]];
[request setPredicate:compoundPredicate];
NSError *error = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
//this if to indicate if we inserting to the BUnits or updating it.
if ([objects count] > 0) {
//Found the record, update The info
savedUnits = [objects objectAtIndex:0];
} else {
savedUnits = [NSEntityDescription insertNewObjectForEntityForName:destinationEntity inManagedObjectContext:context];
}
savedUnits.code = unit.code;
/*Add your updated info*/
NSError *errors;
if (![context save:&errors]) {
NSLog(#"Whoops, couldn't save: %#", [errors localizedDescription]);
}
}
NSLog(#"BUnits count = %d",[context countForFetchRequest:[NSFetchRequest fetchRequestWithEntityName:destinationEntity] error:&error]);
}
Based on the given information, there is no better way than looping each unit object and creating the backup object.

Resources