Fetching data using Core Data - objective-c

I have searched quite a lot on the internet but can't find what I'm looking for.
I have this model where it could be a lot of users. So I have an entity called User. The user has an NSSet of records. And I want to fetch records from given user. I'm trying to do it like this but it still returns records from all users.
NSManagedObjectContext *context = _backgroundContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Record"
inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date >= %#)",date];
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"User = %#",currentUser];
NSPredicate *predicates = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:predicate,predicate1, nil]];
[fetchRequest setPredicate:predicates];
[fetchRequest setPredicate:predicate];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
I know it shouldn't be hard, but I can't find what it is, and I'm hoping some of you could help. Thanks in advance!
EDIT:
As I said I have an entity User which has:
NSString name,
int age,
NSSet records, ...
Record has its own properties such as:
NSDate date,
NSString name,
NSString event,
...
I want to form a fetch request to get records just from specific user. And I don't know how to do it, because I'm getting all of the records from every user.
records has a To-Many relationship. I can get records like currentUser.records, but i can't get user using record.User.

I'm assuming your model looks roughly like this, i.e. the records relationship has an inverse relationship called user:
It's importantant that this relationship is an inverse relationship because otherwise Core Data will not automatically maintain it. You can then query all Records for the given User on/after the given date like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Record"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"user = %# AND (date >= %#)", currentUser, date];
NSError *error = nil;
NSArray *records = [_backgroundContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"records: %#", records);
I've rewritten your code to be more compact, but the important change is that this will create the query in just one go.

The error is here:
[fetchRequest setPredicate:predicates];
[fetchRequest setPredicate:predicate];
After setting the compound predicate, you overwrite it with the predicate for date alone. You probably want to delete the second line.
EDIT: The fetch request requires that you have defined a inverse relationship user from Record to User and use the exact name of this relationship in the predicate.
An alternative solution is to use the "forward" relationship from User to Record and filter the result:
NSSet *records = [currentUser.records filteredSetUsingPredicate:predicates];
or, if you prefer an array
NSArray *records = [[currentUser.records allObjects] filteredArrayUsingPredicate:predicates];

As Martin R said, you're replacing your predicate.
As he also mention, the user property should be lowercase, as I'm pretty sure that core-data enforces this. It should give you an error if you try creating a relationship with an upper case letter, so try
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"user = %#",currentUser];
instead. Obviously if your model doesn't define a property for user then that won't work either and you'll need to change it to whatever your model actually has, but that should work otherwise.
Edit:
Try adding an inverse relationship in your model. So your structure would look like :
user
NSString name,
int age,
NSSet records, ...
Record
NSDate date,
NSString name,
NSString event,
User user,
...
Then every time you create a new record, as well as adding the record to the users record set, set the user on the record object as well. This will make fetching the records a lot easier, and also it allows core data to keep data integrity:
"You should typically model relationships in both directions, and specify the inverse
relationships appropriately. Core Data uses this information to ensure the consistency of the
object graph if a change is made"
(Core Data programming guide)
Once this is setup, you should easily be able to get the records user by simply calling record.user
If you need multiple users for multiple records, then simply setup the relationship as a to-many relationship, and change the User user property to NSSet user.
If you don't want to change your models this much, then I'm a bit confused with your question. You say you want to get the records for a given user? If so, then why don't you simply call user.records as you have that relationship already defined? There's no need for an entire fetch request for that, let core data manage that one for you.
If you then want to filter the resulting Records based on the date, you can apply a predicate to the resulting array yourself, again without the need for a fetch request.

I have found what I've searched! I just needed to form a predicate using this
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"SELF = %#",delegate.currentUser];
EDIT
Well it seems that i was wrong :/ That only seemed to work.

Related

NSPredicate with Core Data

I've look through apple documentation and SO and I cannot find this simple code.
I will accept any down votes I get for such a question, but what is the predicate string that will return a given entity where that entity's property is equivalent to a given string.
I have company and product entities. At the moment, they are simple. Company has name and some other attributes. Product has manufacturer and some other attributes.
I want to do a fetch for all products where the products manufacturer attribute is equivalent to the name of the company that was selected in the company's view controller.
Here is the method in my data model that handles updating the products when a company is selected. It uses the title of the product view controller because that property is assigned the company name in didSelectItemAtIndexPath.
-(void)updateProducts {
NSFetchRequest *reqProd = [[NSFetchRequest alloc] init];
NSEntityDescription *entProd = [NSEntityDescription entityForName:#"Product" inManagedObjectContext:self.context];
[reqProd setEntity:entProd];
NSString *companyToFilter = self.productsViewController.title;
reqProd.predicate = [NSPredicate predicateWithFormat:#"manufacturer like %#", companyToFilter];
NSError *errorProd = nil;
self.productList = [self.context executeFetchRequest:reqProd error:&errorProd];
}
The problem is that this array always returns 0 elements. I know I have a product named iPhone who's manufacturer is apple. Therefore, it must be the predicate that is generating inaccurate SQL.
Again, I spent way too much time search for the answer to no avail. would somebody help me and explain the proper string for the predicate or direct me to the proper documentation?
Predicates are not exactly like SQL, but you can get it to output the SQL being used by adding -com.apple.CoreData.SQLDebug 1 in the startup options.
Also, the predicate format is documented. The short answers to your question is as John says: use CONTAINS instead of LIKE.
The semi-equivalent of the SQL LIKE operator in CoreData is BEGINSWITH for prefixes, ENDSWITH for suffixes, or CONTAINS for matches anywhere in the string.
[NSPredicate predicateWithFormat:#"manufacturer CONTAINS %#", companyToFilter]
You can also write CONTAINS[cd] to indicate "case and diacritic insensitive", or just CONTAINS[c] or CONTAINS[d].

Complex NSPredicate traversing multiple Entities and relationship types

I'm having a tough time solving this predicate issue since the database structure is a bit complex. I have the following database structure, or at least what is of concern for this question:
PUBLISHER<< --- >>PUBLICIST<<--BOOK<<--->AUTHOR<<-->>AGENTS
I tried the following predicate that I have used when I traversed relationships in the past, but not to this degree. I should mention that I have an NSArray with agent names that I want to query against the database to determine a list of publishing houses the agent works with:
NSArray *agentNames = [NSArray arrayWithObjects:Dan, Hunter, Sloan, Jackson];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Publisher" inManagedObjectContext:context];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor
sortDescriptorWithKey:#"publisherName" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] ];
request.predicate = [NSPredicate predicateWithFormat:#"ANY pubHouse.publicist.assignedBook.authorRep.agentName IN %#", [agentNames valueForKey:#"agentName"]];
request.fetchBatchSize = 20;
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
When I run the previous predicate I get the following warning:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : ANY pubHouse.publicist.assignedBook.authorRep.agentName IN
I believe the predicate breaks when I travel the Book Entity relationship to the Author Entity and then onto the Agent Entity. At this point suggestions would help. Thanks
Your problem is that the predicate parser has no clue what set ANY should be applied to.
With this data model:
PUBLISHER<< --- >>PUBLICIST<<-->BOOK<<--->AUTHOR<<-->>AGENTS
… your keypath:
pubHouse.publicist.assignedBook.authorRep.agentName
… in terms of objects and set of the relationship looks something like:
object.set.object.object.set
So, that is two sets that the ANY could apply to.
You could try to build a subquery to handle the predicate but if you have to transverse that many relationships, your fetch will involve a big chunk of your data and will be very, very slow (assuming you get it work in the first place.)
Usually when you end up with a convoluted predicate like this it indicates that you are approaching the problem from the wrong end. In this case, it would be easier to start with a simple predicate like:
NSArray *agentNames = [NSArray arrayWithObjects:Dan, Hunter, Sloan, Jackson];
request.entity = [NSEntityDescription entityForName:#"Agent" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:#"agentName IN %#", agentNames];
Then you would walk the relationship keypath of:
authors.books.publicist.publishers
… to find all the related publishers.
I think that you will have trouble no matter what you do because having more than one to-many-to-many relationship e.g.
PUBLISHER<<--->>PUBLICIST
… increases the complexity of predicates and relationship walks exponentially. Usually, in such a case, you may need an additional entity to more thoroughly model one of the relationships. That usually reduces the complexity of the data model itself which simplifies fetches and walks.
Perhaps NSArray has a problem with valueForKey:. I have seen a solution that uses NSCompoundPredicate adding the array items in a loop.
BTW, in your chain of multiple relationships, aren't you missing your authors?
Just wrap the IN clause of your predicate string in parentheses:
#"ANY (pubHouse.publicist.assignedBook.authorRep.agentName IN %#)"
Would be glad to know if it works.

CoreData's NSFetchrequest: Provide classname and propertynames moredynamically

In CoreData, when you want to query for an object, you have to specify the name of the entity and the names of the properties in strings like that:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"Name=%# AND Forename=%#", name, vorname];
I don't really like this approach, because in doing so, the IDE can't help me in scenarios like renaming one of the properties or renaming the classname.
Using this code-block, I made my call a little more dynamic by getting the classname by introspection:
NSString *classname = [[Person class] description];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:classname inManagedObjectContext:context];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"Name=%# AND Forename=%#", name, vorname];
Is there any way to get the name of the properties (Name and Forename) the same way?
I don't know how to handle properties dynamically.
Do you get my point? I'd be happy, if someone could point me into the right direction.
I've used plists in the resource bundle in this way, sort of-- basically, create an NSDictionary that maps keys that are your parameters to the names of the properties on the object at hand, then just name it MyClassname.plist for easy loading into memory. You'd have to write one up for every class you want this for, though. (and edit them if you change the property names)
Not sure if there is an easier way.
And, IIRC, you sub in the keys as so:
[NSPredicate predicateWithFormat:#"%k=%#",[plist objectForKey:#"param1"],[personObject valueForKey:[plist objectForKey:#"param1"]]]
I'm not sure I see a big advantage of entityForName:[[Person class] description] over entityForName:#"Person" (or maybe entityForName:PersonEntityName where PersonEntityName is just a constant). It works fine provided that the name of the class that represents an entity is the same as the name of the entity, but that's often not the case. It's quite common to use plain old NSManagedObject for simple entities, and that obviously doesn't match the entity name.
Likewise, if you're creating a fetch request, the names of the properties that you're interested in are usually already determined and unlikely to change, so specifying them by name is usually the easiest thing to do. You can get an array of properties from the entity description, but how will you know which property to use if you don't already know its name? And if you know the name of the property, there's not much sense in retrieving the property description just so you can get its name. ;-)
There are times when it's helpful to leave the entity and properties undetermined until the application runs, such as when you want to let the user specify the parameters of the search. That's clearly not the case in the example you gave, though, because you've given the format of the predicate.

Core Data NSFetchRequest for Specific Relationship?

I'm transitioning an existing data model that was previously stored in XML to Core Data, so I'm trying to learn the ropes as properly as possible. Core Data is obviously one of those technologies that isn't going anywhere anytime soon, so I might as well "learn it right."
Take for example a Core Data model with two Entities:
Person
Food
Person has 2 one-to-many relationships with Food:
favoriteFoods (1-to-many)
hatedFoods (1-to-many)
(Both Person and Food are subclasses of NSManagedObject as well.)
In the previous data model, Person maintained two NSArray instance variables. If I wanted favorite foods, I could call:
Person *fred = [[Person alloc] init];
NSArray *fredsFavorites = fred.favoriteFoods;
Easy squeezy.
I'm reading through the documentation for Core Data, and I can't seem to find the right way to obtain this NSArray given an NSFetchRequest, because I can't define which relationship I want to obtain objects from.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Food" inManagedObjectContext:[fred managedObjectContext]]];
[request setIncludesSubentities:NO];
NSArray *fredsFavoriteAndHatedFoods = [[fred managedObjectContext] executeFetchRequest:request error:nil];
This returns all of the Food items stored in both favoriteFoods and hatedFoods. How can I split these up? Surely there's a simple explanation, but I don't currently grasp the concept well enough to explain it in Core Data jargon, thus my Google searches are fruitless.
The most straightforward way to get it is to simply access the relationship NSSet directly:
NSArray *fredsFavorites = [fred.favoriteFoods allObjects];
(I've shown how to get an NSArray from the resulting NSSet).
Alternatively, if you haven inverse relationship set up (which you should) you could use a fetch request like this:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Food" inManagedObjectContext:[fred managedObjectContext]]];
[request setPredicate:[NSPredicate predicateWithFormat:#"ANY personsWithThisAsFavorite == %#", fred]];
NSArray *fredsFavoriteFoods = [[fred managedObjectContext] executeFetchRequest:request error:nil];
This assumes that personsWithThisAsFavorite is the inverse relationship to favoriteFoods. Unless I'm reading your example wrong, favoriteFoods should really be a many-to-many relationship, since a person can have multiple favorite foods, and a food can have multiple people with that food as a favorite.
(Note that I haven't tested this, so the NSPredicate might not be 100% correct)

CoreData many-to-many relationship NSPredicate Exceptions

I'm trying to model a Person/Team relationship. It's a many to many relationship since a person can belong to multiple teams and a team can have multiple people. As suggested by the doc I created an intermediate entity called TeamMember. I am now trying to run a query to see if I have a list of people, whether a pre-existing Team already exists for them so I'm not storing duplicate Teams in the database
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"Team"
inManagedObjectContext:[pm managedObjectContext]];
[request setEntity:entity];
NSPredicate *predicate = nil;
predicate = [NSPredicate predicateWithFormat:#"ALL %K IN %#", #"teamMembers.person", players];
players is an NSSet of people that I'm trying to search
I'm getting the following exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate ALL teamMembers.person IN { (entity: Person; id: 0x1334470 ; data: {
Ideally I would like them to match exactly and not just do an IN as well.
Any help would be greatly appreciated
First, seeing your data model would be helpful but I am guessing you are doing something like:
Person <-->> TeamMember <<--> Team
I realize you saw this in the docs (which tend to make simple things complicated for unknown reasons), I have to ask; why are you doing this? Unless there is data specific to that TeamMember object then you can just do:
Person <<-->> Team
Second, your predicate is incorrect and likely unnecessary. Since you already have a reference to the person, you can query the teams that the person belongs on directly via:
NSSet *teams = [person valueForKeyPath:#"teamMembers.#distinctUnionOfSets.team"];
Which will give you back a set of Team objects. If you, however, remove the TeamMember object then you can simplify this query with:
NSSet *teams = [person valueForKey#"teams"];
Because Core Data uses NSSet as opposed to NSArray you don't even need to worry about duplication at this level because if you try to add the same team to the set more than once it will be ignored.
And to round out the answer, the issue with your predicate is that you cannot mix IN and ALL together in a predicate against Core Data. This is a known limitation. Therefore I recommend going the approach I described above.