Consider two entities Author and Book that are in a many-to-many relationship that are imported into my CoreData store from an external database. What I am confused about is, should I create a new NSManagedObject for each author, even if this author is already in the store? How do I even know that two authors with the same name are the same person? I could for instance end up with 10 John Smiths, and 5 of them are the same person, but there is no way to check this when importing the data, right? Suppose I want to do a fetchrequest for one of these John Smiths, I will still get 10 results. He may also appear as J. Smith, or J.A. Smith. But J. Smith could also be Jenny Smith.
Should I just create an NSManagedObject for each author, and not worry about possible duplicates, or are there other ways around this?
How do I even know that two authors with the same name are the same person?
You don't, and that's the core of your problem right there. You need to allow duplicate names, because names are (usually) not unique. Any technical solution to avoiding or removing duplicates based on name is virtually guaranteed to corrupt your data.
It's not clear where your data is coming from, so it's hard to say what the best fix is. If this is user-entered data, let the user edit an existing author to add or remove titles, to prevent a duplicate. Offer the option to merge two entries in case the user accidentally creates a duplicate.
If the data comes from an online service of some kind, you pretty much have to take what they give you. If they have duplicate entries for authors, you can't reliably do anything about it. You could easily find duplicate names, but that doesn't mean they're the same person.
use a fetch or create pattern as explained in the apple CoreData docs
Core Data doesnt have an implicit uniquing algorithm.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html
(they call it find or create) ;)
In order to disambiguate people (or authors) you would need either a "unique" attribute, say an author_id which is guaranteed to be unique when an author will be created.
The other approach is to use heuristics to determine if an object has possibly duplicates This second approach sounds more complex, and actually it IS more complex ;)
Unfortunately, Core Data does not support "unique attributes" (unique keys).
Both approaches can be implemented as proper managed object "validations", which get invoked when the context will be saved.
A sophisticated solution would use a separate index maintained per unique attribute and per context. Using Core Data queries as shown in the sample snippets "Implementing Find-or-Create Efficiently" in order to confirm that the "unique constraint" is fulfilled each time the context is saved, will become quite slow for large data sets.
With iOS 9, Apple introduced unique constraints to Core Data. Now you can specify an attribute that has to be unique.
Related
I'm trying to figure out what's the recommended way to implement enum with associated values in Core Data data model. Let's say I have a book entity and I want to save in database how I got the book, like:
it's bought by me (or other family members)
it's borrowed from someone (e.g., a colleague)
it's given as a gift by someone (e.g., a friend)
This would be an enum in swift:
enum WhereItCameFrom {
case Bought(who: String, date: Date, where: String)
case Borrorwed(who: String, date: Date, dueDate: Date)
case GivenAsGift(who: String, date: Date, forWhat: String)
}
I'm thinking to implement it in data model using inheritance , as below:
Introduce a parent entity WhereItCameFrom and define the above cases as its children entities.
Define a to-one relationship from Book to WhereItCameFrom. Its deletion rule is Cascade.
Define a to-one relationship from WhereItCameFrom to Book. Its deletion rule is Deny.
See the diagram:
I'm wondering if this this the right way to do it and I have a few specific questions.
1) What's the typical way to implement enum with associated values?
I think my above modal is good. But just in case, are there other better ways to do it?
2) Is entity with no attributes normal?
In above diagram, WhereItCameFrom doesn't have any attributes. At first I added a type attribute to it to indicate if it's a Bought, Borrowed, or GivenAsGift entity. But then I realized this information is implicit in its child entity class type, so I removed it. So the only purpose of the parent entity is to hold the relationship. Is this use typical in Core Data?
3) Will the old object be removed automatically when modifying relationship at run time?
Suppose I modify book.whereItCameFrom relationship value at run time. Its previous value is a Borrowed object. Its new value is a GivenAsGift object. Do I need to delete the Borrowed object manually (I mean, doing that explicitly in application code)?
I guess I should do it. But given Core Data is a framework helping to maintain data consistency in object graph, that seems awkward to me. I wonder if Core Data has some feature that can figure out the Borrowed object is not needed and delete it automatically?
Thanks for any help.
UPDATE:
In the third question, after the old Borrowed object is disconnected with Book object, is my understanding correct that, from the Borrowed object perspective, the peer object has been delete and hence the peer object's Cascade deletion rule is applied to the Borrowed object? If so, then it will be deleted automatically. I think the real question here is if deletion rule applies to relationship update or not. I'll do some experiments on this later today.
A few thoughts...
1) What's the typical way to implement enum with associated values?
I think my above modal is good. But just in case, are there other better ways to do it?
I can't comment on typical ways of implementing enums with associated values, but your model seems to make sense. One word of caution: if you search StackOverflow for questions regarding entity inheritance, you will find several answers advising against using it. The way CD implements subentities (at least for SQLite stores) is to add all the attributes of all the subentities to the parent entity SQLite table. That's handled for you "under the hood" by CoreData, but the SQLite table can potentially end up being very "wide", which can affect performance. I've never found it an issue, but you might want to have that in mind if you have lots of data and/or the entities are more complex than you indicate in the question. Subentities can also cause issues in some rare situations - for example, I've seen questions indicating problems with uniqueness constraints.
2) Is entity with no attributes normal?
It's unusual, but not a problem. However, as all three subentities have date and who attributes, it would be wise to move these from the subentities to the parent WhereItComeFrom entity. (Otherwise, as noted above, your parent entity table will have three columns for date (one for each subentity) and three for who).
3) Will the old object be removed automatically when modifying relationship at run time?
No. If you modify the book.whereItCameFrom relationship value at run time, with a GivenAsGift object replacing a Borrowed object, CD's graph management will ensure that the Borrowed object's book property is set to nil. The cascade rule does not prevent objects being "orphaned" in this way and you must manually delete the Borrowed object.
I am using Core Data to store objects. What is the most efficient possibility for me (i.e. best execution efficiency, least code required, greatest simplicity and greatest compatibility with existing functions/libraries/frameworks) to store different attribute values for each object depending on the context, knowing that the contexts cannot be pre-defined, will be legion and constantly edited by the user?
Example:
An Object is a Person (Potentially =Employer / =Employee)
Each person works for several other persons and has different titles in relation to their work relationships, and their title may change from one year to another (in case this detail matters: each person may also concomitantly employ one or several other persons, which is why a person is an employee but potentially also an employer)
So one attribute of my object would be “Title vs Employer vs Year Ended”
The best I could do with my current knowledge is save all three elements together as a string which would be an attribute value assigned to each object, and constantly parse that string to be able to use it, but this has the following (HUGE) disadvantages:
(1) Unduly Slowed Execution & Increased Energy Use. Using this contextual attribute is at the very core of my prospective App´s core function (so it would literally be used 10-100 times every minute). Having to constantly parse this information to be able to use it adds undue processing that I’d very much like to avoid
(2) Undue Coding Overhead. Saving this contextual attribute as a string will unduly make additional coding for me necessary each time I’ll use this central information (i.e. very often).
(3) Undue Complexity & Potential Incompatibility. It will also add undue complexity and by departing from the expected practice it will escape the advantages of Core Data.
What would be the most efficient way to achieve my intended purpose without the aforementioned disadvantages?
Taking your example, one option is to create an Employment entity, with attributes for the title and yearEnded and two (to-one) relationships to Person. One relationship represents the employer and the other represents the employee.
The inverse relationships are in both cases to-many. One represents the employments where the Person is the employee (so you might name it employmentsTaken) and the other relationship represents the employments where the Person is the Employer (so you might name it employmentsGiven).
Generalising, this is the solution recommended by Apple for many-many relationships which have attributes (see "Modelling a relationship based on its semantics" in their documentation).
Whether that will address all of the concerns listed in your question, I leave to your experimentation: if things are changing 10-100 times a minute, the overhead of fetch requests and creating/updating/deleting the intermediate (Employment) entity might be worse than your string representation.
What is the best practice for creating Unidirectional One to Many Relationships in Core Data?
For example...
Lets take two classic entity examples, "teacher" and "student".
Each student has one teacher, and each teacher has many students.
In CoreData right now you are forced to provide an inverse such that teacher is forced to have a reference to a 'student'. If you don't you get this nice warning that says something along the lines of...
file:///Users/josephastrahan/Documents/VisualStudioProjects/Swift3WorkOrders/WorkOrders/WorkOrders/WorkOrders.xcdatamodeld/WorkOrders.xcdatamodel/: warning: Misconfigured Property: Teacher.student should have an inverse
What if I don't want teacher to have a reference to student?
Some other posts have brought up that I should just allow the inverse anyways but I think this inverse may be causing an issue with one of my projects.
That said let me explain my exact issue.
Lets say that our teacher has a unique attribute int64 called 'id'. Lets say the students also have unique attribute int64 called 'id'.
The int64 is enforced to be unique by adding a constraint on the model for teacher on id. (refer to image below to see how that is done)
Every year there is new students but the teachers stay the same. So I decided that I want to delete all the students without deleting the reference to the teacher. So I set the delete rule to 'nullify' for the relationship for the teacher to student and 'nullify' for the student to teacher.
Now when I create a new student I want to assign one of the existing teachers to that student... (something like student.teacher = teacher object with id of 1 or the same id as before) however!! , because the teacher has the inverse relationship to a student that no longer exists (which in theory should be null) the program crashes!
I know this is the case as I've used print console logs to narrow it down the exact point that it occurs. Also I know this because if I add the delete rule of cascade for student the crash will go away but...then I lose my teacher! which I don't want...
Some things that I think might be the issue:
1.) When I do my testing I do it at the startup of the program which creates a new context everytime. Could it be that because I never deleted teacher it still thinks it refers to a student from a context that no longer exists? (if I'm even saying this right...)
I'm not sure the best solution to acheive what I'm trying to do with Coredata and any advice is much appreciated!
Note:
Forgot to mention I also have the Merge Policy of: NSMergeByPropertyObjectTrumpMergePolicy, which will overwrite the old data with the new. When I'm creating new students I'm creating new teachers also just using the same id which should follow this policy.
You are almost there.
The advice to keep the inverse relationship is a good one. Keep it.
Your issue is likely caused by different contexts. Instead of holding on to a teacher object in memory, you should fetch the teacher (based on the id) in the context in which you intend to use it.
Your nullified students should not have any impact. A to-many relationship is really a Set<Student>. Make sure the set is empty.
NB:
If you want to keep the student in the database (for historical purposes) - it seems from your description that this is the case - you might also consider another scheme: give your students another attribute (such as a year) and use that to filter the student list. You would not have to delete or nullify anything. You could also do some more interesting time-based queries on the data.
Unique Constraints are available with iOS9. Which have helped iOS Developers with adding and updating records in CoreData.
Unique Constraints make sure that records in an Entity are unique by the given fields. But unique constraints along with To-Many relationship leads to a lot of weird issues while resolving conflicts.
e.g. “Dangling reference to an invalid object.”
This post is basically focused to a small problem that may take days to fix.
http://muhammadzahidimran.com/2016/12/08/coredata-unique-constraints-and-to-many-relationship/
I am creating an application which requires the user to register. All data entered by user will be stored in this table called "customer". Now part of the information being collected is the address but I don't want to congest the table structure and would like to store address as an object (city, address, post code, etc).
What's the best practice: create an address table and refer the table through foreign key in the customer table or store the customer address as an object and store it in customer table?
I am not sure how parse fully functions so looking for your experience in the answer.
Thanks
I faced this exact problem a few months ago, and solved it by having a pointer in the customer object structure to the additional data. Note that if you do this, you'll need to make sure to include the pointed to field in future customer queries, or the data won't be fetched.
Retrospectively, I'm not sure I'd recommend splitting the objects up. It does create a more normalised data structure, but Parse fights against this in several ways:
You have to remember to include the pointed to field in all future queries. This is a pain.
You can only follow pointers up to a certain depth within a query (I think 3?)
Parse charges you by the database access, so denormalised data can be an issue.
Parse doesn't really support atomic operations or transactional queries, so it's easy to get your data into an inconsistent state if you're not careful about when you save. For example, you update your customer record, go to change the address record, and have the second query fail. Now you're in a "half updated state", and without transaction rollback, you'll have to fix it yourself (and you might not even know it's broken!).
Overall, were I to use Parse again (unlikely), I'd probably stick with giant denormalised objects.
Here is a solution to handle two table by the help of userId.
Note- You are creating a table of REGISTRATION and filling few data by your end(code).
so you can create an other one table for Address. and when you will create a new table of
Address a question will arise that how you manage these table
so its simple here you have same user id for both table "REGISTRATION & ADDRESS"
then by the help of that unique "userid" you can play. And as per your requirement find
the detail of both table and merge as well.
Hope it will resolve your problem .
I have an entity (Order) that has a to-many relationship with an entity Item, which has a to-many relationship with an entity Note. If the price changes for a Note, or a Note is added, the 'price' attribute for the associated Order must update.
Right now, my solution is to have all Order objects sign up for NSManagedObjectContextDidChange notifications, and check all the inserted/changed objects to see if any of those objects is one of the Order's Item's Notes. However, this is very inefficient and hacky, and is leading to a few more performance issues that can be optimized away, but I'm starting to realize that my solution is what's faulty, not necessarily the issues.
So, what's the best way to do this?
EDIT: To answer the questions brought up by Rog: I'm looking to propagate the changes to model data, which are observed by view controllers via KVO. The problem I'm noticing is, if the price of a Note related to an Item is adjusted, there's no facility to account for this in Core Data. If I use keyPathsForAffectingPrice on Item, and return "notes", that only accounts for if notes are inserted/deleted, not if the Note price is adjusted.
If this wasn't Core Data, I'd write my own accessor for note price to just say [self.item willChangeValueForKey:#"price"], self.price = x, [self.item didChangeValueForKey:#"price"]; but that isn't possible since I can't do custom accessors in Core Data, right?
We problably need more details about your code to be able to help - i.e. are you looking at ways to propagate changes to your model data or to the UI?
Are you using a fetchedResultsController at the moment?
The way I see it, if your Order contains Items and the Items contain Notes, any updates to your "child" attributes will be effective immediately (provided you have your reverse relationships setup properly).
Then if you're looking at ways to updating your UI accordingly, then we need to know how you are currently fetching and populating your views with your Coredata entities.