how to implement that in core data - iphone

in SQL we use the following clause
where studentName like '%a' and StudentID = 1
how to do that in objective c and core data using setpredicate for fetchrequest
I have only two inputs part of the name #"a" and the ID #"1"
best regards

Take a look at the Predicate Programming Guide.
I would also like to add the Core Data Programming Guide - Fetching Managed Objects.

it worked with contains[cd] but I want
to know what if I passed the string
empty and want to search by ID only
Normally, you don't use empty strings in a fetch predicate because core data will try to match the empty string. Instead, you should create different fetch request for different circumstances. Fetch request are lightweight objects than can be stored in arrays (or even the data model itself.)
If this case, you would test for an empty string and if true use a fetch predicate that only looks for the StudentID.
In general, try to avoid thinking of Core Data in SQl terms. It's natural but dangerous. Core Data is not SQL. Entities are not tables. Objects are not rows. Columns are not attributes. Core Data is an object graph management system that may or may not persist the object graph and may or may not use SQL far behind the scenes to do so. Trying to think of Core Data in SQL terms will cause you to completely misunderstand Core Data and result in much grief and wasted time.

I agree with TechZen, you should be thinking of core data in terms of objects, as opposed to being sql backed. CD uses SQL as an implementation detail, not necessarily as a defining feature.

This is an example from the developer library
Creating Fetch Request Templates Programmatically
basicly it looks something like this using predicated...
NSManagedObjectModel *model = <#Get a model#>;
NSFetchRequest *requestTemplate = [[NSFetchRequest alloc] init];
NSEntityDescription *publicationEntity =
[[model entitiesByName] objectForKey:#"Publication"];
[requestTemplate setEntity:publicationEntity];
NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:
#"(mainAuthor.firstName like[cd] $FIRST_NAME) AND \
(mainAuthor.lastName like[cd] $LAST_NAME) AND \
(publicationDate > $DATE)"];
[requestTemplate setPredicate:predicateTemplate];
[model setFetchRequestTemplate:requestTemplate
forName:#"PublicationsForAuthorSinceDate"];
[requestTemplate release];
so by looking at that your probably after something like this
NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:
#"(mainAuthor.firstName like[cd] $FIRST_NAME) AND \
(mainAuthor.lastName like[cd] $LAST_NAME) AND \
(publicationDate > $DATE)"];
[requestTemplate setPredicate:predicateTemplate];
hope this helps I'm currently setting up core data in my project and have been reading through this stuff all morning.

Related

How to model my Core Data entity?

I want to store NoteObjects in Core Data. Normally, a NoteObject has a NSString *mainText and an NSMutableArray *arrayOfTags (an array of NSStrings). I want to now use Core Data, but arrays are a tricky matter with core data. Typically a NoteObject won't have more than 50 tags in its array. So how should I model this? I have two options:
Use a transformable property to store the array
Use a to-many relationship, which I've read is the more "legit" way to do it.
Which one should I use and why? And how would I implement a to-many relationship with my simple structure? I can't seem to wrap my fingers around that concept.
Use to-many relationship. Because it's way better and easier during fetch requests. See the screenshots below. Pay attention to the Relationship manager on the right side, set "To-Many Relationship" from your NoteObject to Tags. Ignore the Player entity.
Oh and pay attention to the "Delete Rule". You might want to delete all the tags associated with a given NoteObject. So set it to Cascade in that case.
NoteObject entity
Tag entity
--Edit:
To add multiple tags you need to first fetch your NoteObject - I assume there will be some sort of ID parameter which you'll use to distinguish NoteObjects. CoreData will automatically generate the add/remove methods for Tags. You'll need to use code similar to the one below:
- (void)addTags:(NSArray *)tags toNoteObjectWithID:(NSString *)noteID {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"NoteObject"];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"noteID == %#", noteID];
[fetchRequest setPredicate:pred];
NoteObject *noteObject = [[[self managedObjectContext] executeFetchRequest:fetchRequest error:nil] lastObject];
for (NSString *tag in tags) {
Tag *t = [NSEntityDescription insertNewObjectForEntityForName:#"Tag"
inManagedObjectContext:[self managedObjectContext]];
t.body = tag;
t.noteObject = noteObject;
[noteObject addTagsObject:t];
}
[self saveContext];
}
You could use a transformable property, but then you need to write the transformer.
If you use a toMany relationship, you have to create an additional entity for tags, which presumably has only one attribute - the string value, and a single relationship. Extrapolating a little, I would guess that you have a finite set of values tags can take on, and you might someday want all notes that have tag X - then you would be able to fetch the entity containing the string value for X and then use that to fetch the NSArray of objects that have X in the relationship (whatever you called it).
Arrays were only tricky in Core Data because they weren't supported prior to iOS 5, so you had to include some attribute (like creation date) by which they could be sorted. If you don't mind requiring iOS 5, you can use ordered relationships. Results are returned in an NSOrderedSet, which is a lot like (and can can be converted to) an array. Or, just re-think the problem -- is the order of the tags on a note important to the note or the user? Would it be okay if you just display the tags in alphabetical order? If so, even better -- just use a plain old (unordered) to-many relationship.

Filtering relationships with Core Data

I have a Core Data object graph set up such that
Grandparent--(hasMany)-->>Parent--(hasMany)-->>Child
and each entity has a 'modified' property. I'd like to perform a fetch that returns each Grandparent entity with a modified date after a certain date. This should contain all Parent entities with the modified date after that point, and the same with the Child entity.
I can easily grab the list of Grandparents using
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"modified > %#", lastSyncDate];
Is there any way to write a predicate so that I can filter its relationships in the same way? Thanks!
You should be able to do something like this:
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"modified > %# OR ANY parents.modified > %# OR ANY parents.children.modified > %#",
lastSyncDate,
lastSyncDate,
lastSyncDate];
(Where the properties for the Parent and Child are called parents and children respectively. Just change them for whatever it is you have called them.)
I suggest you have a read of this:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html#//apple_ref/doc/uid/TP40001795-215891
UPDATE
It appears you can't do that. I think you actually need to use SUBQUERY. This question is similar to what you want to do:
What's better way to build NSPredicate with to-many deep relationships?
I ended up just grabbing my whole object graph and filtering the relationships using
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"modified > %#", lastSyncDate];
NSArray *modifiedChildren = [[parent.children allObjects] filteredArrayUsingPredicate:predicate];
It's not terribly efficient but it seems to do the trick.

Core data predicate with group and MAX()

I have an object in core data that has 2 fields: Name and Date. Each time a person checks in it saves their name and the time they did it.
Is there a way to get a list of all unique people with their last check in time using a predicate in core data?
Note: i do know that 2 tables would make it easier.
the sql for this would be
select
clientName
, MAX(ModifiedDate)
from
Client
group by
clientName
Beginning from iOS 5.0 / OS X 10.7 NSFetchRequest class includes the setPropertiesToGroupBy: instance method, which allow to create aggregated requests on persistent storage level.
You would have to set NSDictionaryResultType as the result type, thus telling NSFetchRequest object to return NSDictionary objects with aggregated data instead of managed objects.
Example code for max() requests:
[request setResultType:NSDictionaryResultType];
NSExpression *dateKeyExpression = [NSExpression expressionForKeyPath:#"date"];
NSExpression *maxDateExpression = [NSExpression expressionForFunction:#"max:" arguments:[NSArray arrayWithObject:dateKeyExpression]];
NSExpressionDescription *maxDateED = [[NSExpressionDescription alloc] init];
[maxDateED setExpression:maxDateExpression];
[maxDateED setName:#"maxDate"];
[maxDateED setExpressionResultType:NSDateAttributeType];
NSAttributeDescription *clientName = [[self entityDescription].attributesByName objectForKey:#"clientName"];
[request setPropertiesToFetch:[NSArray arrayWithObjects:clientName, maxDateED, nil]];
[request setPropertiesToGroupBy:[NSArray arrayWithObject:clientName]];
No not easily since Core Data is an object graph first. You could do it by grabbing a distinct set of the names and then querying for the max for each via a sort.
The best answer is to break it into two tables otherwise it is going to be computationally expensive each time you try and resolve this.
OR/M Layer
Don't think of it as an OR/M layer. Core Data is an object graph; full stop.
The fact that it happens to persist to a database structure as one of its persistence options is secondary.
The issue in your question is that you are trying to use an Object Graph as a database. In that situation I get to use a phase that is very frequently attributed to me:
"You're doing it wrong." :)

Editing Core Data row from entity linked to managedObjectContext via relationship

I need to edit a row of data in an Entity that has a relationship with my main Entity from my fetchedResultsController, in this case "theUser" being an instance of my User entity.
I basically need to edit one of the CannedMessage rows that already exist and save it. I can access the "Messages" fine as you see below, but am unsure once I have found the CannedMessage I want as to how I save it back into the managedObjectContext for "theUser"
Any advice?
NSArray *msgs = [theUser.Messages allObjects];
NSPredicate *activeMatch = [NSPredicate predicateWithFormat:#"defaultMessage == 1"];
NSArray *matched = [msgs filteredArrayUsingPredicate:activeMatch];
CannedMessage *msgToEdit;
for(CannedMessage *msg in matched) {
msgToEdit = msg;
}
Your trouble is that your thinking in SQL terms instead of Core Data's object oriented terms. The data you are looking for is not in an SQL row but in the attribute of a managed object. In this case (I assume) you are looking for an attribute of a CannedMessage instance.
The matched array will contain either managed objects initialized with the CannedMessage entity or an instance of a dedicated NSManagedObject subclass (if you setup one which it looks like you did.)
Lets say the attribute is named theMsg. To access the attribute in the generic managed objects:
for(CannedMessage *msg in matched) {
msgToEdit = [msg valueForKey:#"theMsg"];
}
... to access a custom class:
for(CannedMessage *msg in matched) {
msgToEdit = msg.theMsg;
}
It's really important when learning Core Data to simply forget everything you know about SQL. Nothing about SQL truly translates into Core Data. Core Data is not an object-oriented wrapped around SQL. Entities are not tables, relationships are not link tables or joins, attributes are not columns and values are not rows. Instead, Core Data creates objects just like you would if you manually wrote a custom class to model a real world object, event or condition. Core Data uses SQL almost as an after thought as one of its many persistence options.
In my experience, the more you know about SQL, the harder it is to shift gears to Core Data and other object graph APIs. You want to translate the new stuff to what you have already mastered. It is natural but resist the urge.

Unique Values from Core Data

I have a core data-based app that manages records of auto dealerships. Each record stores the dealer's address, which is broken into addressLine1, addressLine2, city, state, and zip components, each stored as a string in the data store.
I would like to present a list of cities with dealerships to the user, so I'm trying to figure out if it is possible to get a list of every unique city name that has been entered into the store. I other words, is it possible to issue some sort of query against all of the dealership records that will return a list of distinct city names?
I would know how to do this easily with a SQL query, but (how) is this done in Core Data?
Thanks very much!
Core Data have the option to get distinct record. The method of getting unique results using NSArray and NSSets are not recommend.
[fetchRequest setResultType:NSDictionaryResultType];
NSDictionary *entityProperties = [entity propertiesByName];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:[entityProperties objectForKey:#"<<yourattrib>"]]];
[fetchRequest setReturnsDistinctResults:YES];
Refer Apple documentation and check the answers for How to fetch distinct values in Core Data?
You're right, there isn't an "easy" way to do this with Core Data, because Core Data is not a database. However, it is possible. Here's the general idea:
Fetch all your Dealer objects via an NSFetchRequest. To simplify the query, you can set the fetch request to only fetch the city attribute.
Execute NSArray * uniqueCities = [fetchedDealers valueForKeyPath:#"#distinctUnionOfObjects.city"];
A quick way to ensure a unique set of things is to use NSSet. Once you have the results for a query on city take your NSArray and do
NSSet* uniqueResults = [NSSet setWithArray:resultsArray];
You can transform the set into another collection class if more convenient or just the object enumerator to do something with all of them. I do not know if this approach or the valueForKeyPath method from Dave DeLong is more efficient.