Creating NSPredicate for external primary key - ios

I looked at other questions and I saw glimpses of answers but none worked.
Here is my situation, I have two objects "Menu" and "Restaurant". Menu is where I have all menu items, and "Restaurant" is the name of different restaurants. Therefore:
Menu Object
- menuId
- name
- protein
- restaurantID (one-to-one)
Restaurant Object
- restaurantID (one-to-many)
- name
When the user gives me the restaurant name, I want to return the menu items. In SQL, it would be:
SELECT * FROM ZMENU WHERE zrestaurantId = (SELECT Z_PK FROM ZRESTAURANT WHERE zname="Starbucks")
I tried
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *e = [[model entitiesByName] objectForKey:#"Restaurant"];
[request setEntity:e];
*NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(restaurant = (SUBQUERY(Restaurant, $x, $x.name like[cd] %#).#count))", restaurantName];
But I get the error
'NSInvalidArgumentException', reason: 'Unable to generate SQL for predicate (restaurant == SUBQUERY(Restaurant, $x, $x.name LIKE[cd] "Starbucks").#count) (problem on RHS)'
Can someone please help? I want to avoid having to do two fetches (one for restaurant PK, other for the menu item), I believe there should be a more proper solution.

I got it!!!!!
Creating element was correct, just change the subquery to
NSEntityDescription *e = [[model entitiesByName] objectForKey:#"Menu"];
[request setEntity:e];
NSString restaurantName = #"Starbucks";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(restaurantId, $x, $x.name == %#).#count != 0", restaurantName];
So the way subquery works is - you establish the object you want to get(Select on table) by setting the entity. Then on the predicate you use subquery on the field that references the external table. Then you specify the field in that table that you want to check, and that's it! I have no idea why there is a "count", but it works.
By the way per documentation, inside the subquery you can use %# for ints or strings values you want to check, and you can use %K for field names you want to check. Pretty cool! :-D

Related

NSPredicate and BEGINSWITH with CloudKit : Field value type mismatch

I have a table "Store" with the attributes "name" of type String (indexes checked for sort query and search).
I want to execute a SQL like query to find all stores with name beginning with a substring.
So I tried this predicate :
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ANY name BEGINSWITH %#",substring];
CKQuery *query = [[CKQuery alloc]initWithRecordType:#"Store" predicate:pred];
CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.desiredKeys = #[#"name",#"category"];
NSMutableArray *results = [[NSMutableArray alloc] init];
queryOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record];
};
queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
//my own code
}
[publicDatabase addOperation:queryOperation];
I have this error :
<CKError 0x1887a540: "Invalid Arguments" (12/1009); "Field value type mismatch in query predicate for field 'name'"> for query : <CKQuery: 0x18882650; recordType=Store, predicate=ANY name BEGINSWITH "mysubstring">
My guess is that you need to get rid of the "ANY".
The ANY and SOME aggregate operators may be combined with the IN and
CONTAINS operators to perform list membership tests.
So ANY is for lists (Arrays). Since 'name' is a String, it is not a list, hence the mismatch error: "Field value type mismatch in query predicate for field 'name'"
Wild guess...try:
[NSPredicate predicateWithFormat:#"ANY {'name','category'} BEGINSWITH %#",substring];
That may be an incorrect reversal of key/value but it might work.
A completely different approach would be to create a third field called "nameAndCategory", append the two strings and add them to the field. Then figure out how to do the full text search (tokenized string search) with the predicate:
[NSPredicate predicateWithFormat:#"self contains '%#'",substring];
or
[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"self contains '%#'",substring];
But perhaps the only sure approach is to do two searches and combine the results.

CoreData - Fetch request with relationship object

please confirm if i understand that right...
Imagine there is an entity 'person' and an entity 'credit_card'.
So one 'person' can have many 'credit_card's.
The person entity has the attributes: name: STRING and age: INT
and relationship: creditcards (to many) inverse
and the credit card entity has: card_number: INT and valid_date: DATE and relationship: card_user (to one) inverse
In my code i have a specific person (ManagedObject) called f.e. Person *currentUser. If i now want to get all credit cards of this specific person with a specific 'valid_date' i would create a fetch request (for Entity 'credit_card') with following predicates:
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:
#"valid_date == <NSDate object>"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:
#"ANY card_user like currentUser"];
This predicate works well for me, but is that the right way? Should i really ask for: "ANY relationship name like ManagedObject" ?
If I'm understanding what you want correctly, all you need to do is use the creditcards property on your Person *currentUser and filter it down:
NSSet *setOfCreditCards = currentUser.creditcards;
NSPredicate *filter = [NSPredicate predicateWithFormat: #"valid_date == %#", date];
NSSet *cardsWithValidDates = [setOfCreditCards filteredSetUsingPredicate:filter];
The reason you tell CoreData about relationships is to avoid making another query from scratch.
You do not need to use the ANY key word if you have set up you core data model correctly. Specifically Credit cards need to have a person relationship back to the person object (you should get a warning if you didn't do this). Then you could combine both predicates into one like this
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"person == %# AND valid_date == %#", currentUser, valid_date];

NSPredicate filter to-Many with child object property

I have what I thought was a simple problem. I am attempting to filter some core data where I have a Parent object which has a to-many relationship with a child object and that child object has a string id. I want to get all the parent objects where no child object has a specific id.
I have tried !(ANY... LIKE) as well as !(ANY..==) and NONE with like and == and ALL children.id != otherid
My querying looks like:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Parent"];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"NONE children.id LIKE %#",otherID];
[fetchRequest setPredicate: predicate];
NSError* error;
NSArray* allParents = [[DataManager context] executeFetchRequest:fetchRequest error:&error];
//sanity check the predicate
for (Parent* p in allParents) {
for (Child* c in p.children) {
if([c.id isEqualToString:otherID]){
NSLog(#"PREDICATE FAIL!");
}
}
}
Am I missing something with NSPredicate? Is this type of filtering allowed for CoreData? Better solution?
I found a similar question although not easily apparent. It turns out the answer is the tricky SUBQUERY. Here is what led me on the chase:
NSPredicate Aggregate Operations with NONE
and an more open explanation about SUBQUERY here:
http://funwithobjc.tumblr.com/post/2726166818/what-the-heck-is-subquery
The resulting predicate is:
//in children get a $child and group all the $child objects together
//where the ids match, if that groups count is 0 we know
//the parent has no child with that id
[NSPredicate predicateWithFormat:
#"SUBQUERY(children, $child, $child.id == %#).#count == 0",objectId];

How to use multiple ANY operators within one predicate?

There are two entities C and P with a many-to-many relationship. I am trying to fetch the C entity, where C contains at least one P which consequently contains at least one C with a specific value for its attribute a.
I am trying something like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY p.c.a = %d ", someValue];
However this obviously does not work since P has also a to-many relationship to C. I would need something like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY p.ANY(c).a = %d ", someValue];
How would you write such predicate in the correct way?
If you want to get all C entities that have a relation to at least one P entity, then you can do that with
NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:#"C"];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"count(p) != 0"];
request.predicate = predicate;
assuming that p is the one-to-many relationship from C to P.

How to create one fetchRequest for multiple entities?

Update to the question below:
We were able to query the child entity using the Father entity fetch request by using fatherChild relationship. Sample query is as follows:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"fatherChild.name LIKE 'fx'"];
Now what we are trying to do is to use the predicate above but another another condition where we want to find child for a given fathers name. We used the following code
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"name LIKE 'john' AND ANY fatherChild.name in 'fx'"];
But the program crashed with the exception:
[__NSCFString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xad49c40
Reading through examples we see we can use subquery but not sure what the syntax would be for our case where we have a entity with one to many relationship. Any help would be appreciated?
Thanks.
Question:
We have a data model with three entities: Father, Mother and Child. Please see the image for reference.
An example Query request we have on the father entity is given below :
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Father"
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSString *attributeName = #"name";
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"%K like %#",
attributeName, searchField.text];
We have similar query request for Mother and Child entities. What we would like to do is to create one query to combine Father and Mother entities. For example we want to search on Father's name =Mike and Mothers name =Jen in a single query. How do we do it?
Thanks for your response.
We found out that we can solve the problem using a SUBQUERY.
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"name LIKE %# AND fatherMother.name LIKE %# AND (0!=SUBQUERY(fatherChild,$varChild,$varChild.name=%#).#count)",dad.text, mom.text, baby.text];
Here fatherMother and fatherChild are names of relationships.
dad.text, mom.text and baby.text are UITextField values.

Resources