Core Data Relationship Designing - Load only desired entity - xcode

I need help in designing the database for my application using Core Data.
Namely:
I have 2 Entites called Verse and Translation.
Verse has one-to-many relationship with translation table.
Translation table contains translations in different languages such as EN,DE,AR.
I know if i load the Verse table then corresponding translations will also be lazy loaded.
But i want to load only desired translation of each Verse. E.g only EN.
Because if i load 1000 Verse and with 5 different Translation (DE,EN,TR,AR,FR) then i have a big amount of data which i want to avoid >> 1000x5 = 5000.
I don‘t want load everytime the translations which i dont need. Only the selected translation.
thank you for any advice.
Regards,

Core data does something called faulting, it should only load data you actually ask for. It might load some metadata for all those objects, but it won't actually load the translation until you ask for it. I would suggest looking into faulting more if you want to know more than that because it seems fairly complex. At least the rest of what faulting is seems complex.

i found a solution which is exactly what i wanted. I tested and it returns only the desired Translations.
- (NSArray *) loadAllVersesByLanguage
{
NSManagedObjectContext *_managedObjectContext = [self managedObjectContext];
NSArray *fetchedObjects;
NSString *turkish = #"TR";//should be parameterized
NSString *arabic = #"AR";
NSPredicate *pred = [NSPredicate predicateWithFormat:#"languageCode==%# OR languageCode==%#",turkish,arabic];
NSFetchRequest *fetch = [[NSFetchRequest alloc] initWithEntityName:#"Verse"];
NSError * error = nil;
fetchedObjects = [_managedObjectContext executeFetchRequest:fetch error:&error];
if(fetchedObjects != nil && fetchedObjects.count > 0 ){
for(Verse *verse in fetchedObjects){
NSSet *verseSet = [verse.translations filteredSetUsingPredicate:pred];
[verse.translations setSet:verseSet];
}
return fetchedObjects;
}else{
return nil;
}
return nil;
}

Related

Fetched Results Controller objectAtIndexPath without using TableView

I have a Core Data financial app that needs to accumulate sales quantities contained in the Trans Entity for each product and then update the totals into the appropriate attribute of the Product Entity.
I am able to achieve this by nesting a for (transArray) inside and tableView (product).
However I need to sort and format the tableView based on the results first.
General Question: Can fetched results be used without tableViews?
- (void)calculateAmounts {
NSIndexPath *indexPath=0;
for (Product *product in self.fetchedResultsController.fetchedObjects){ // All product records
selectedProduct = [self.fetchedResultsController objectAtIndexPath:indexPath];
// >>>>>NSLog shows correct number of object, however selectedProduct # Index Path Are NULL
for (id product1 in transProductArray) { // An array of all of the trans for product
if ((NSNull *)product1 == [NSNull null]) {
}
else if ([product1 isEqualToString:selectedProduct]) {
float qty = [#"1" floatValue];
NSNumber *numQty=[NSNumber numberWithFloat:qty]; // Update quantity sold in product by 1
NSNumber *quantity = [NSNumber numberWithFloat:([selectedProduct.quantitySold floatValue] + [numQty floatValue])];
selectedProduct.quantitySold = quantity;
[self.product.managedObjectContext save:nil];
}
} // Next Trans
} // Next Product
}
This is a good question. Not (clearly) knowing the implementation, I will bet it is possible to use fetched results outside a tableview. However, the Overview of NSFetchedResultsController documentation has this as the first line:
You use a fetched results controller to efficiently manage the results
returned from a Core Data fetch request to provide data for a
UITableView object.
While table views can be used in several ways, fetched results
controllers are primarily intended to assist you with a master list
view.
This infers that the two objects are linked for efficiency and ease of use. Plus, there are monitors under-the-hood to watch for changes, etc.
What you may be looking for is a 'plain-old' NSFetchRequest. A tutorial.
Hi I am not to sure whether you still require an answer to this however I had the same issue myself. I overcame as described in my own question on the subject.
Display multiple core data entities objects in 1 Non-Table View Controller
I inserted a small tableview into my VC and set it to alpha and used the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to enable displaying details in labels.
This was the only way I could overcome it. Hope this helps.
Thanks for the answer. Like a lot of things the answer was too easy! It's about the fetch, not the tableview. Just do a fetch into an array, then loop though the array to accumulate your values: Also useful for finding a specific object or output to a CSV.
NSError *error;
NSNumber *total=0;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"TransDetail" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (TransDetail *trans in fetchedObjects) {
total = total + trans.amount;
}

Understanding Core Data Queries(searching data for a unique attribute)

Since I am coming from those programmers who have used sqlite extensively, perhaps I am just having a hard time grasping how Core Data manages to-many relationships.
For my game I have a simple database schema on paper.
Entity: Level - this will be the table that has all information about each game level
Attributes: levelNumber(String), simply the level number
: levelTime(String), the amount of time you have to finish the level(this time will vary with the different levels)
: levelContent(String), a list of items for that level(and that level only) separated by commas
: levelMapping(String), how the content is layed out(specific for a unique level)
So basically in core data i want to set up the database so i can say in my fetchRequest:
Give me the levelTime, levelContent and levelMapping for Level 1.(or
whatever level i want)
How would i set up my relationships so that i can make this type of fetchRequest?
Also I already have all the data ready and know what it is in advance. Is there any way to populate the entity and its attributes within XCode?
As you've described it, it's a single Core Data entity, called Level that has four string attributes. Since there's just the one entity, there are no relationships. You'd create the one entity and add properties so that it looks just like you've described it above:
Getting just one Level is basic Core Data fetching:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Level"];
NSString *levelNumber = #"1";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"levelNumber = %#", levelNumber];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:&error];
NSManagedObject *level = nil;
if ([results count] > 0) {
level = [results objectAtIndex:0];
}
// Use level...
If it was me I'd use one of the numeric types for levelNumber, but maybe you have some reason to use a string there. I'd also probably break levelContent into a separate entity, because (a) comma delimited strings are ugly, no matter how you slice 'em, and (b) you might well want the items to have more attributes, and a separate entity would hold those.

Core Data Transformable attributes NOT working with NSPredicate

I often use Transformable for Core Data attributes, so I can change them later.
However, it seems like, if I want to use NSPredicate to find a NSManagedObject, using "uniqueKey == %#", or "uniqueKey MATCHES[cd] %#", it's not working as it should.
It always misses matching objects, until I change the attributes of the uniqueKey of the matching object to have specific class like NSString, or NSNumber.
Can someone explain the limitation of using NSPredicate with Transformable attributes?
Note: I'm not sure when/if this has changed since 5/2011 (from Scott Ahten's accepted answer), but you can absolutely search with NSPredicate on transformable attributes. Scott correctly explained why your assumptions were broken, but if Can someone explain the limitation of using NSPredicate with Transformable attributes? was your question, he implied that it is not possible, and that is incorrect.
Since the is the first google hit for "Core Data transformable value search nspredicate" (what I searched for trying to find inspiration), I wanted to add my working answer.
How to use NSPredicate with transformable properties
Short, heady answer: you need to be smart about your data transformers. You need to transfrom the value to NSData that contains what I'll call "primitive identifying information", i.e. the smallest, most identifying set of bytes that can be used to reconstruct your object. Long answer, ...
Foremost, consider:
Did you actual mean to use a transformable attribute? If any supported data type -- even binary data -- will suffice, use it.
Do you understand what transformable attributes actually are? How they pack and unpack data to and from the store? Review Non-Standard Persistent Attributes in Apple's documentation.
After reading the above, ask: does custom code that hides a supported type "backing attribute" work for you? Possibly use that technique.
Now, past those considerations, transformable attributes are rather slick. Frankly, writing an NSValueTransformer "FooToData" for Foo instances to NSData seemed cleaner than writing a lot of adhoc custom code. I haven't found a case where Core Data doesn't know it needs to transform the data using the registered NSValueTransformer.
To proceed simply address these concerns:
Did you tell Core Data what transformer to use? Open the Core Data model in table view, click the entity, click the attribute, load the Data Model Inspector pane. Under "Attribute Type: Transformable", set "Name" to your transformer.
Use a default transformer (again, see the previous Apple docs) or write your own transformer -- transformedValue: must return NSData.
NSKeyedUnarchiveFromDataTransformerName is the default transformer and may not suffice, or may draw in somewhat-transient instance data that can make two similar objects be different when they are equal.
The transformed value should contain only -- what I'll call -- "primitive identifying information". The store is going to be comparing bytes, so every byte counts.
You may also register your transformer globally. I have to do this since I actually reuse them elsewhere in the app -- e.g. NSString *name = #"FooTrans"; [NSValueTransformer setValueTransformer:[NSClassFromString(name) new] forName:name];
You probably don't want to use transforms heavily queried data operations - e.g. a large import where the primary key information uses transformers - yikes!
And then in the end, I simply use this to test for equality for high-level object attributes on models with NSPredicates -- e.g. "%K == %#" -- and it works fine. I haven't tried some of the various matching terms, but I wouldn't be surprised if they worked sometimes, and others not.
Here's an example of an NSURL to NSData transformer. Why not just store the string? Yeah, that's fine -- that's a good example of custom code masking the stored attribute. This example illustrates that an extra byte is added to the stringified URL to record if it was a file URL or not -- allowing us to know what constructors to use when the object is unpacked.
// URLToDataTransformer.h - interface
extern NSString *const kURLToDataTransformerName;
#interface URLToDataTransformer : NSValueTransformer
#end
...
// URLToDataTransformer.m - implementation
#import "URLToDataTransformer.h"
NSString *const kURLToDataTransformerName = #"URLToDataTransformer";
#implementation URLToDataTransformer
+ (Class)transformedValueClass { return [NSData class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value
{
if (![value isKindOfClass:[NSURL class]])
{
// Log error ...
return nil;
}
NSMutableData *data;
char fileType = 0;
if ([value isFileURL])
{
fileType = 1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value path] dataUsingEncoding:NSUTF8StringEncoding]];
}
else
{
fileType = -1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value absoluteString] dataUsingEncoding:NSUTF8StringEncoding]];
}
return data;
}
- (id)reverseTransformedValue:(id)value
{
if (![value isKindOfClass:[NSData class]])
{
// Log error ...
return nil;
}
NSURL *url = nil;
NSData *data = (NSData *)value;
char fileType = 0;
NSRange range = NSMakeRange(1, [data length]-1);
[data getBytes:&fileType length:1];
if (1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL fileURLWithPath:str];
}
else if (-1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:str];
}
else
{
// Log error ...
return nil;
}
return url;
}
#end
Transformable attributes are usually persisted as archived binary data. As such, you are attempting to compare an instance of NSData with an instance of NSString or NSNumber.
Since these classes interpret the same data in different ways, they are not considered a match.
you can try this way
NSExpression *exprPath = [NSExpression expressionForKeyPath:#"transformable_field"];
NSExpression *exprKeyword = [NSExpression expressionForConstantValue:nsdataValue];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:exprPath rightExpression:exprKeyword modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];

Core Data - get/create NSManagedObject performance

I'm creating an iphone/ipad app that basically reads XML documents and creates tableviews from objects created based on the xml. The xml represents a 'level' in a filesystem. Its basically a browser.
Each time i parse the xml documents i update the filesystem which is mirrored in a core-data sqllite database. For each "File" encountered in the xml i attempt to get the NSManagedObject associated with it.
The problem is this function which i use to get/create either a new blank entity or get the existing one from database.
+(File*)getOrCreateFile:(NSString*)remotePath
context:(NSManagedObjectContext*)context
{
struct timeval start,end,res;
gettimeofday(&start,NULL);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"File" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"remotePath == %#",remotePath];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
File *f;
if ([items count] == 0)
f = (File*)[NSEntityDescription insertNewObjectForEntityForName:#"File" inManagedObjectContext:context];
else
f = (File*)[items objectAtIndex:0];
gettimeofday(&end, NULL);
[JFS timeval_subtract:&res x:&end y:&start];
count++;
elapsed += res.tv_usec;
return f;
}
For eksample, if i'm parsing a document with 200ish files the total time on a iPhone 3G is about 4 seconds. 3 of those seconds are spent in this function getting the objets from core data.
RemotePath is a unique string of variable length and indexed in the sqllite database.
What am i doing wrong here? or.. what could i do better/different to improve performance.
Executing fetches is somewhat expensive in Core Data, though the Core Data engineers have done some amazing work to keep this hit minimal. Thus, you may be able to improve things slightly by running a fetch to return multiple items at once. For example, batch the remotePaths and fetch with a predicate such as
[NSPredicate predicateWithFormat:#"remotePath IN %#", paths];
where paths is a collection of possible paths.
From the results, you can do the searches in-memory to determine if a particular path is present.
Fundamentally, however, doing fetches against strings (even if indexed) is an expensive operation. There may not be much you can do. Consider fetching against non-string attributes, perhaps by hasing the path and saving the hash in the entity as well. You'll get back a (potentially) larger result set which you could then search in memory for string equality.
Finally, do not make any changes without some performance data. Profile, profile, profile.

How do I get Core Data to use my own NSManagedObjectID URI scheme?

I am writing an app that connects to a database to fetch data. Since the fetching is expensive and the data is generally unchanging, I'm using CoreData to cache the results so that I can do fast, local queries.
From the database, for each type, there is a string property that is guaranteed to be unique. In fact, there is a URI scheme for the database which is a unique address for each item.
The URL scheme is very basic along the lines of:
ngaobject://<server_license_id>/<type>/<identifier>
I'd like to be able to use this in CoreData as well. I've made a method to fetch a single item from the CoreData store:
-(NSFetchRequest*)fetchRequestForType:(NSString*)typeName identifier:(NSString*)identifier
{
NSFetchRequest * fetchRequest = [self fetchRequestForType:typeName];
[fetchRequest setFetchLimit:1];
NSString * identifierProperty = [self identifierPropertyNameForObjectType:typeName];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"%K == %#", identifierProperty, identifier];
[fetchRequest setPredicate:predicate];
return fetchRequest;
}
-(NGAObject*)objectWithType:(NSString*)typeName
identifier:(NSString*)identifier
{
// First try to retrieve it from the cache
NSAssert1( (identifier != nil), #"Request to create nil-name object of type %#", typeName );
NSFetchRequest * fetchRequest = [self fetchRequestForType:typeName identifier:identifier];
if ( !fetchRequest )
return nil;
NSError * error = nil;
NSArray * fetchResults = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if ( !fetchResults )
{
NSLog(#"%#", error);
[NSApp presentError:error];
return nil;
}
if ( [fetchResults count] )
return [fetchResults objectAtIndex:0];
return nil;
}
When I retrieve an item from the server, I want to first get a reference to it in the cache and if it's there, update it. If it's not, create a new one.
Since I'm getting back thousands of objects from the server, performing a fetch for a single object for which I know a unique ID brings my machine to a crawl.
Instead, what I'm doing is pre-loading all the objects of a type, then creating a dictionary of identifiers->object, then processing the thousands of objects for that type by running it through the dictionary. This works fine, but is awkward.
Could I not write a method that takes the type/identifier combo and get a single object from CoreData without having to execute a lengthy fetch request?
It seems there is a solution if I can get CoreData to use my own URI specification. I could then call -(NSManagedObjectID*)managedObjectIDForURIRepresentation:(NSURL*)url on the persistent store coordinator.
So, the question is, how can I get CoreData to use my URI scheme? How can I make CoreData use my own unique identifiers?
You can't make Core Data use a custom URI scheme. The URI scheme is hardcoded into Core Data such that the URI can be decoded to locate particular data in a particular store in a particular apps on a particular piece of hardware. If the URI was customizable, that system would break down.
Fetching object singularly is what is killing you. Instead you need to batch fetch all objects whose customID matches those provided by the server. The easiest way to that is to use the IN predicate operator:
NSArray *customIDs=//... array of customIDs provided by server
NSPredicate *p;
p=[NSPredicate predicateWithFormat: #"customIdAtrribute IN %#", customIDs];
This will return all existing objects that you can ignore.
Alternatively, you could
Do a fetch on just the customID property by setting the fetch's propertiesToFetch to the customID attribute.
Set the fetch result type to dictionary.
Use the above predicate.
You will get an array of one key dictionaries returned with the customID as each value.
Convert the dictionary to an array of values e.g cachedIDs
Convert customIDs above to a mutable array.
Filter the customIDs array using the predicate, #"NOT (SELF IN %#)", cachedIDs"
The filtered customIDs array will now only contain the customID values NOT cached in Core Data.
You can create managed objects for only the new ids.
(This is how you use a filter predicate if you are unfamilar with it.)
NSMutableArray *f=[NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",nil];
NSArray *g=[NSArray arrayWithObjects:#"5",#"6",nil];
[f filterUsingPredicate:[NSPredicate predicateWithFormat:#"NOT (SELF IN %#)",g]];
NSLog(#"f=%#",f);
...which outputs:
f=(
1,
2,
3,
4
)
Are all the fields which you are using for unique-ID lookup marked as "Indexed" in the CoreData designer? If that has been done then the CoreData fetches shouldn't be lengthy ...

Resources