Reputation: 5960
(Answer found. See below.)
In the following code, I am updating about 350,000 records by linking some of the relationships. However, in the end, I examine the sqlite database, and only a fraction of the relationships are saved. The rest are still nil.
-update-
Before describing the code in question, I should explain that the dictionaryOfSynsetDictionaries
contains prefetched synset
objects. It is organized as a dictionary containing four dictionaries, where the keys are 'n', 'v', 'a', and 'r' (for the four parts of speech, or pos
). Each of the inner dictionaries contains references to the synset
objects, which are subclassed from NSManagedObject.
Each inner dictionaries is keyed by what is called synsetOffset
.
The code below fetches all of the SYNSET_POINTER
objects from the store and puts them into an array. Each SYNSET_POINER
object refers to a synset
by the synsetOffset
and partOfSpeech
(pos
) properties. It also contains a relationship that links the SYNSET_POINTER
object to the synset
by matching synsetOffset
and pos
to the corresponding synset
in dictionaryOfSynsetDictionaries
So now, after having prefetched and organized the synset
objects into the dictionary, the following code fetches all of the SYNSET_POINTER
objects into an array, iterates over the array, linking synset pointers to their synset objects directly through the corresponding relationship.
(The picture above shows two relationships between the SYNSET
and SYNSET_POINTER
objects. This is based on the original dataset organization. They serve two different purposes. For this question, I am referring to the one-to-one relationship.)
-end update-
Here is the code doing the updates:
[request setEntity:[NSEntityDescription entityForName:@"SYNSET_POINTER" inManagedObjectContext:[ManagedObjectContext moc]]];
predicate = nil;
[request setPredicate:predicate];
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"synsetOffset" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSArray *synsetPointersArray = [[ManagedObjectContext moc] executeFetchRequest:request error:&error];
int i = 0;
int j = 0;
for(SYNSET_POINTER *pointer in synsetPointersArray) {
NSString *pos = pointer.partOfSpeech;
NSString *offset = [pointer.synsetOffset stringValue];
pointer.synsetPointer = [[dictionaryOfSynsetDictionaries objectForKey:pos] objectForKey:offset];
error = nil;
if (![[ManagedObjectContext moc] save:&error]) {
NSLog(@"error with save\n%@\n%@",error.localizedFailureReason, error.localizedDescription);
NSLog(@"pause and quit");
}
NSLog(@"pos %@, offset %@, pointer %@", pos, offset, pointer);
if (j==100) {
NSLog(@"%@ %d", pos, i);
j=0;
}
i++;
j++;
}
Here, I am updating the property synsetPointer
for the pointer of class SYNSET_POINTER, a subclass of NSManagedObject. I can see that in each iteration of the loop, the synsetPointer
relationship does point to a proper object, as in this debugger output:
2012-11-26 22:03:08.753 [26156:fb03] pos v, offset 5815, pointer <SYNSET_POINTER: 0x404e84f0> (entity: SYNSET_POINTER; id: 0x404d00d0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET_POINTER/p282133> ; data: {
partOfSpeech = v;
pointerSymbol = "~";
reverseRelatedSynset = "0x244cd880 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p85476>";
sourceTarget = 0000;
synsetOffset = 5815;
synsetPointer = "0x244cdda0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p83738>";
})
2012-11-26 22:03:08.822 [26156:fb03] pos v, offset 5815, pointer <SYNSET_POINTER: 0x404e8530> (entity: SYNSET_POINTER; id: 0x404d00e0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET_POINTER/p285862> ; data: {
partOfSpeech = v;
pointerSymbol = "@";
reverseRelatedSynset = "0x244cd870 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p86470>";
sourceTarget = 0000;
synsetOffset = 5815;
synsetPointer = "0x244cdda0 <x-coredata://9136BC94-4D77-4DB6-B03F-4F3AA35E2E49/SYNSET/p83738>";
})
The synsetPointersArray
is sorted by the synsetOffset
value. I can sort the table in the sqlite viewer in Firefox, and I see that most of the values remain nil. The debug output above shows they are all assigned. For some reason, they are not being saved.
Can anyone see a problem with this code that would prevent some of the updates?
Upvotes: 1
Views: 362
Reputation: 5960
Solved!
It wasn't obvious from all of the information I provided, but I discovered that the one-to-one relationship shown in the picture was the problem.
SYNSET_POINTER.synsetPointer should point to one and only one SYNSET object.
But it tuns out that more than one SYNSET_POINTER object can point to an individual SYNSET object.
Therefore, the reverse relationship, SYNSET.reverseSynsetPoint, should have been configured as part of a one-to-many bidirectional relationship.
Since the managed object context takes care of resolving reverse relationships, the one-to-one relationship implies that previously assigned links were being nullified automatically. Assinging a new SYNSET_POINTER to a SYNSET that already has a SYNSET_POINTER-to-SYNSET relationship set will nullify the existing link in both directions every time the SYNSET.reverseSynSetPointer was changed. This would have left a dangling link on the SYNSET_POINTER side, but the managed object context nullifies it automatically.
So Core Data had been setting and saving the changes. But after many of these links were configured, as I expected, they also were being nullified.
In my case, I really didn't need the reverse relationship, and included it as an afterthought, thinking I might use it at a later time. My advice is to think carefully about these types of reverse relationships when they are initially set up. Otherwise, they may create trouble later.
Upvotes: 1
Reputation: 80265
Your code uses very poor variable names which makes it difficult to understand what you want to achieve. However, it seems you are making a fundamental conceptional mistake.
In general, to establish relationships in core data, you do not save references to other Core Data objects as attributes in those objects. Rather, you use relationships and let core data handle the technicalities of foreign keys. In order to add an object to a relationship, use the Core Data generated accessors in your object classes.
Check out these sections of the Core Data Programming Guide:
Relationships and Fetched Properties
Managed Objects
Upvotes: 0