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];
Related
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.
I am unable to fetch the results of a 1-1 relationship Entity's attribute?
BACKGROUND INFO
I have three UITableViewControllers, The grandfather TVC has 10 cells, Day 1, Day 2..Day 10. The father TVC has 3 cells, Workout 1, Workout 2, Workout 3. And in the child TVC I set the attributes of each workout and its "workoutscore". So each day Cell has 3 workoutcells after segue.
Workout1, Workout2, Workout3 each have a workoutscore attribute. I would like to display the workout score on the workout cells.
So I have a Day Entity with a one-one relationship with Workout1 Entity with Workout2 Entity and Workout 3 Entity.
Day <---> Workout1 Day<----->Workout2 Day<---->Workout3
After back navigation from the child to parent I correctly pass the workoutscore and save the context in the parent. The labels on the WorkOut cells correctly display the workoutscore.
Of course when I navigate back to the grandfather TVC and back to parent the parent TVC deallocs and I would like to fetch the previous scores based on the Day of the Grandfather TVC.
SAVING APPEARS IN VIEWDIDAPPEAR (This is correct)
In the parent TVC (Note: I am only showing the code for one workout as it is the same for the other 2)
AppDelegate *ad = [[UIApplication sharedApplication] delegate];
Day *myDay = [NSEntityDescription insertNewObjectForEntityForName:#"Day"
inManagedObjectContext:ad.managedObjectContext];
Workout1 *myWorkout1 = [NSEntityDescription insertNewObjectForEntityForName:#"Workout1"
inManagedObjectContext:ad.managedObjectContext];
NSNumber *theNumber = myWorkout1.workoutscore; //get old score
theNumber = [self updateNumber:theNumber forIndex: 11];
myWorkout1.workoutscore = theNumber; //update for new score
[myDay setWorkout1:myWorkout1]; //set Workout Entity to day Entity
NSError *savingError = nil;
myDay.dayname = [NSString stringWithString:self.dayname];
[ad.managedObjectContext save:&savingError];
}
FETCHING APPEARS IN VIEWDIDLOAD (This is incorrect, something is not working here)
In the parent TVC (Note: I am only showing the code for one workout as it is the same for the other 2)
AppDelegate *ad = [[UIApplication sharedApplication] delegate];
Day *day = [NSEntityDescription insertNewObjectForEntityForName:#"Day"
inManagedObjectContext:ad.managedObjectContext];
Workout1 *myWorkout1 = [NSEntityDescription insertNewObjectForEntityForName:#"Workout1"
inManagedObjectContext:ad.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Workout1" inManagedObjectContext:ad.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:#"Day",nil]];
[fetchRequest setIncludesSubentities:YES];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"day.daynumber contains %#", self.dayLabel.text]; //self.dayLabel.text is Day# in string form.
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [ad.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Nothing was fetched");
}
NSLog(#"number of fetched objects is %lu", (unsigned long)[fetchedObjects count]);
myWorkout1 = [fetchedObjects lastObject]; //most recent update of score
if ([fetchedObjects count] > 0) {
stringWS1 = [[NSMutableString alloc] init];
stringWS1 = [NSMutableString stringWithFormat:#"%#",myWorkout1.workoutscore];
self.workoutScoreLabel1.text = [NSMutableString stringWithString:stringWS1]; //Put score on label
Before the If statement of [fetchedObjects count] > 0 all I get is (null) on the labels. Now i get nothing because the array == 0. yet I save correctly?.
You have so many issues here its really hard to point in the right direction.
CoreData does not have a built-in method for "create-or-update". when you call insertNewObjectForEntityForName:inManagedObjectContext: you are introducing a new object to the context. this mean that in your current state you are inserting an empty Day and WorkoutX in every viewDidLoad call of the "parent TVC". this also happens in your viewDidAppear, but there you set values for your inserted objects.
Your fetch request could be much simpler if you'd send the Day.objectID to the "parent TVC", your predicate will be: [NSPredicate predicateWithFormat:#"day = %#",dayObjectID]
Why are you splitting the Workout entity to 3 separate entities? You could have a single Workout entity and 3 to-one relationships in the Day entity (or a to-many relationship if you like to have a variable number of workouts).
Keep references to what you are updating (day, workout) and update directly these objects (as long as you work only on the main thread there is no need to get them by their ID)
read the CoreData documentation HERE, specifically about creating, deleting and fetching managed objects.
look at some examples (like a blank CoreData project in Xcode), and see how they work with 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).
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]]
I am developing an application where i used core data framework for the purpose of maintaining a database. My entity contains three attributes called: name, start time and end time of a list of applications. I am getting the correct values for name and start time attribute.
Now my problem is my end time attribute should contain the value of the next entries start time value. If anybody having any idea about this please let me know.
Thanks
You can leave the endTime attribute blank until you create the next entity. In the +Create category on the entity, get the last/first object (assuming you are using ordered entities) and update the endTime with the same value used for the new startTime.
If your objects are not ordered it could be a bit tricky since all the entities are in a set. But if ordered, you are good since NSOrderedSet responds to lastObject (and firstObject).
Enjoy,
Damien
EDIT: Here is an example factory method that either 1) returns the existing stock entity for a stock symbol or 2) creates a new entity for that symbol. Pretty easily modified to get entities and select the first/last depending on your sort order. Again see the Core Data classes from Prof. Hegarty.
+ (Stock *)stockForSymbol:(NSString *)symbol inManagedObjectContext:(NSManagedObjectContext *)context {
Stock *stock = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Stock"];
request.predicate = [NSPredicate predicateWithFormat:#"symbol = %#",symbol];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"symbol" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || [matches count] > 1) {
// handle error
} else if ([matches count] == 0) {
stock = [NSEntityDescription insertNewObjectForEntityForName:#"Stock" inManagedObjectContext:context];
stock.symbol = symbol;
stock.strategyPosition = [NSNumber numberWithInt:StrategyPositionFlat];
stock.userPosition = stock.strategyPosition;
stock.userOwns = [NSNumber numberWithBool:NO];
} else {
stock = [matches lastObject];
}
return stock;
}