ios Coredata relationship query - ios

I have a question regarding the core data relationship. I have to create a core data for following features of device catalog:
where device contains device name, device SKU number, device price, device brand and color. I have a filter in my application which queries all data based on the respected filter. like if the user clicks brand, it will show a list of brands and then filter apply on a dataset for particular brand only, same for color if the user chooses any color it will show devices with a particular color. also, the user can select multiple filters also like brand + color.
under this situation I created this data model :
here I created 3 different tables where device have many to many relationships with color, as the device can have multiple colors. and colors can have multiple devices.
for the brand, I use many to one relationship. as one device can have only one brand but on the other hand, a brand can contain multiple devices.
forgetting all devices I write this code :
-(void)loadAllDevices{
// Fetching
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Device"];
// Add Sort Descriptor
NSSortDescriptor *sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey:#"deviceName" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey:#"devicePrice" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor1, sortDescriptor2]];
// Execute Fetch Request
NSError *fetchError = nil;
NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
if (!fetchError) {
for (NSManagedObject *managedObject in result) {
NSLog(#"DeviceName = %#, DevicePrice = %#", [managedObject valueForKey:#"deviceName"], [managedObject valueForKey:#"devicePrice"]);
Brand * deviceBrand = [managedObject valueForKey:#"deviceBrand"];
NSLog(#"Device Brand = %#",deviceBrand.brandName);
NSSet *deviceColorSet = [managedObject valueForKey:#"deviceColor"];
for (Color *color in deviceColorSet) {
NSLog(#"Device Color = %#", color.colorName);
}
}
} else {
NSLog(#"Error fetching data.");
NSLog(#"%#, %#", fetchError, fetchError.localizedDescription);
}
}
but now I want to add a filter based on the condition I describe above, how can I write predicates based on this. any help or a direction to this will be appreciated.
**
UPDATE:
As describe in comments if i'm using predicate like this :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deviceBrand.brandName CONTAINS[cd] %#", #"Apple"]; [fetchRequest setPredicate:predicate]; and NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deviceColor.colorName CONTAINS[cd] %#", #"Blue"]; [fetchRequest setPredicate:predicate];
separately it given me correct result.
but if i try to combine both its not providing me any result. i try this predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deviceBrand.brandName == %# AND ANY deviceColor.colorName == %#", #"Apple", #"Blue"];
any idea whats i'm doing wrong here.
**

Suppose you have two variables, selectedBrand and selectedColor which are the brand and color details you want to match. Then your predicate would look like this:
fetchRequest.predicate = [NSPredicate predicateWithFormat:#“deviceBrand.brandName == %# AND ANY deviceColor.colorName == %#“, selectedBrand, selectedColor];
This assumes you want to want to return results where both brand and color match - if you want results where either matches, change AND to OR.

If the only property of your color and brand models are their name and the only usage of these datas are for filtering and search, modeling your data in such situation has a low performance. So its better you add just brandName instead of brand model and colorName instead of color model, and filter your devices based on them.
Even if you want infos more than name into brand and color, its better to save the brandName and colorName in device model separately, to have an efficient filtering and searching results and then use the brand model only when you want to show more info about a particular brand to user.

Related

Accessing objects using NSPredicate from Core Data

I tried to find answer to my simple question with no luck... I'm working on CoreData and i have two entities lets take an example of "Photo and Photographer" one to many relationship, means one photographer can have multiple photos... Now i did store objects using
[NSEntityDescription insertNewObjectForEntityForName...]
I'm having issues retrieving "all photos" from a specific photographer. I'm using this code
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Photographer"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"name = %#",#"photographerName"];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"timeStamp" ascending:YES]];
this code is returning photographer however i only need ALL photos taken by this photographer.
I'm new to CoreData, i'm not sure if i should be using "Photos" entity?
thanks in advance.
Your fetch request should request Photo entity.
Then, in your predicate, you can specify the name of the photographer like so:
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"photographer.name = %#",#"photographerName"];
Or better yet, it would be much faster to first get the photographer object, and then test the relationship in the predicate:
Photographer* photographer = ... //Get required photographer here
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"photographer = %#", photographer];
Use this to sort the photographer.photos array.
NSSortDescriptor *sortKey = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:YES];
NSArray *sorters = [NSArray arrayWithObject:sortKey];
NSArray *sortedArray = [photographer.photos sortedArrayUsingDescriptors:sorters];

Core Data fetching relationship objects

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).

NSPredicate Core Data to-many

This is the Core Data model. Image
DiaCD <--->> HoraCD <<---> ActividadCD
In the entity "Activity" is a category called attribute to filter the activities. My question is: How could I make a query to give me back the days with activities where the category is "X"?
Try one:
NSEntityDescription *entityDescriptionDia = [NSEntityDescription entityForName:#"DiaCD" inManagedObjectContext:managedObjectContext];
NSFetchRequest *requestDia = [[NSFetchRequest alloc] init];
NSPredicate *predicateDia = [NSPredicate predicateWithFormat:#"ANY relDiaHora.relHoraActividad.categoria == %#", categoria];
[requestDia setEntity:entityDescriptionDia];
[requestDia setPredicate:predicateDia];
NSError *errorDia;
NSArray *arrayDia = [managedObjectContext executeFetchRequest:requestDia error:&errorDia];
if ([arrayDia count] > 0) {
for (DiaCD *dia in arrayDia) {
NSSet *setHora = dia.relDiaHora;
HoraCD *horaQuery = [setHora anyObject];
ActividadCD *actividadQuery = horaQuery.relHoraActividad;
NSLog(#"Act --> %# y la categoria --> %# y la categoria --> %#", actividadQuery.titulo, actividadQuery.categoria, categoria);
}
}
If I do this query does not return good data that does not respect the category, I'm guessing, do not know why :S.
Try 2:
NSPredicate *predicateDia = [NSPredicate predicateWithFormat:#"relDiaHora.relHoraActividad.categoria == %#", categoria];
If I do the same query but only removing the "ANY" fails. Error: "reason: 'to-many key not allowed here'"
Your predicate
[NSPredicate predicateWithFormat:#"ANY relDiaHora.relHoraActividad.categoria == %#", categoria];
returns all days that have any hour with the given activity category. Your problem seems
to be that
NSSet *setHora = dia.relDiaHora;
returns all hours of the fetched day, not only the hours with the given activity category.
Therefore
HoraCD *horaQuery = [setHora anyObject];
is any hour of the fetched day, and need not have the given activity.
If you need the days and matching hours, you should execute a fetch request on the hours
instead:
NSEntityDescription *entityDescriptionHour = [NSEntityDescription entityForName:#"HourCD" inManagedObjectContext:managedObjectContext];
NSFetchRequest *requestHour = [[NSFetchRequest alloc] init];
NSPredicate *predicateHour = [NSPredicate predicateWithFormat:#"relHoraActividad.categoria == %#", categoria];
[requestHour setEntity:entityDescriptionHour];
[requestHour setPredicate:predicateHour];
NSError *errorHour;
NSArray *arrayHours = [managedObjectContext executeFetchRequest:requestHour error:&errorHour];
if ([arrayHours count] > 0) {
for (HourCD *hour in arrayHours) {
DiaCD *dia = hour.relHoraDia;
ActividadCD *actividad = hour.relHoraActividad;
// ...
}
}
OK, the first bit was wrong. However, still follow this note about naming.
A QUICK NOTE
You should not be calling your attributes "relItem1Item2". This is something that comes from relational databases. CoreData is not a relational database.
You should name them descriptively as to what they point to.
i.e.
relHoraActividad should be called actividad. As it is pointing to the actividad entity and it is a "to-one" relationship.
Also...
relActividadHora should be called horaCDs. It is pointing to the horaCD entity and it is a "to-many" relationship.
I think your setup is absurd. Why have complicated relationships to days and hours when you can simply use NSDate attributes?
From your image I see that you store other things in these entities that do not seem to have anything to do with hours or days.
To filter by day or time, you typically use a pattern like this
[NSPredicate predicateWithFormat:#"time > %# && time < %#", min, max];

Fetchrequest with NSPredicate not returning right values

I'm having a strange problem with Core Data.
The problem is that I'm looking for objects with a property less than X and it isn't returning all the values that matches.
There is no cache and I'm not using //[fetchRequest setFetchBatchSize:50];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.level <= [cd] %#", [self.filters objectForKey:#"level"]];
I add it to a MutableArray of predicates and later I execute
NSPredicate *myPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: subPredicates];
This one it's in a function that returns myPredicate called preparePredicates. For the moment there aren't more predicates, only one.
It's NSLog returns: level <=[cd] "30".
I have tried it also with intValue of the string and %i
The main function:
NSError* error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myEntity"
inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate* predicate = [self preparePredicates];
[fetchRequest setPredicate:predicate];
//[fetchRequest setFetchBatchSize:50];
NSArray *fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
NSLog (#"Error: %#", error);
NSLog(#"los fetchedobjects: %i",[fetchedObjects count]);
return fetchedObjects;
It doesn't return any error.
If I look at the results there isn't one having a level of 6, but there are others that matches (all are less than the specified). I know there is one with level 6.
If I open the .sqlite in SQLite Database Browser, I can see it there.
If I do with this program a
SELECT * FROM zmyentity WHERE zlevel <30;
it doesn't appear, same as in Core Data do. But if I do a
SELECT * FROM zmyentity WHERE zlevel == 6;
it appears.
I really don't know what is happening. Maybe I'm making a rookie mistake, so please point me in the right direction.
Thank you very much in advance.
Probably you have stored the level as a String attribute, because
"30" < "6"
when comparing strings. Choosing a numeric attribute type (Integer 16/32/64) should solve the problem. The corresponding level property in the myEntity class has then the NSNumber type.
You should also omit the [cd] modifier in the predicate, because that enforces a string comparison.
You predicate could then look like this:
[NSPredicate predicateWithFormat:#"self.level <= %d", [[self.filters objectForKey:#"level"] intValue]]

ios NSFetchRequest for two related tables

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).

Resources