Reputation: 3
I am working on a predicate to search for users with the same preferences as any other user
Users <<->> Preferences
Given the following state; I wish to check for a new registering user
(Sam, nickname: "foo", language: "english")
to see if any existing users have the same preferences. There can be any number of preference types. In this case it would detect that Sally has the same selections
Users
| Name |
| Sally |
| John |
PreferenceTypes
| Name |
| nickname |
| language |
| .... |
UserPreferences
User PreferenceTypes Selection
Sally | nickname | "foo"
Sally | language | "english"
John | nickname | "bar"
John | language | "english"
Upvotes: 0
Views: 96
Reputation: 80265
This is how you could set up the model:
User <---->> Preference <<-----> PreferenceType
User.name // name string
User.preferences // to-many to Preference
Preference.value // the value, or "selection"
Preference.user // to-one to User
Preference.type // to-one to PreferenceType
Preference.name // name of preference, such as "language"
PreferenceType.preferences // to-many relationship to Preference
Your predicate will need a subquery and will be quite "expensive" in terms of resource usage. I assume that it is allowed that some users have preferences that others do not have, i.e. while we do know the total number of possible preferences, we do not know the number of preferences one particular user has. I cannot write that query from the top of my head, I would have to think about it. --- Anyone?
One practical approach would be to first fetch a subset of users, say, those with the exact same nickname. It would be simpler to just fetch preference objects with a predicate like this:
[NSPredicate predicateWithFormat:
@"type.name = 'nickname' && value = %@ && not (user = %@)", userNickname, user];
On the small subset of users (referenced by foundPreference.user
) you could run a custom method you write (hasSamePreferences
) where you can iterate through the preferences and check for existence and equality.
-(BOOL)hasSamePreferencesAsUser:(User*)otherUser {
NSSet *myPreferenceKeys = [self.preferences valueForKeyPath:@"type.name"];
NSSet *otherPreferenceKeys = [otherUser.preferences valueForKeyPath:@"type.name"];
if (myPreferenceKeys.count != otherPreferenceKeys.count) {
return NO;
}
for (NSString *key in myPreferenceKeys) {
if (![otherPreferenceKeys containsObject:key]) {
return NO;
}
NSPredicate *keyFilter = [NSPredicate predicateWithFormat:@"type.name = %@", key];
// String only for demonstration
NSString *myValue = [self.preferences
filteredArrayUsingPredicate:keyFilter].firstObject;
NSString *otherValue = [otherUser.preferences
filteredArrayUsingPredicate:keyFilter].firstObject;
if (![myValue isEqualToString:otherValue]) {
return NO;
}
}
return YES;
}
BTW, if you do not expect more than, say, 20 preferences, it would be even more pragmatic and hardcode these preferences as properly named attributes to User
(or a Settings
entity with a one-to-one relationship if you prefer). Your code will be very readable, and your predicates fairly trivial.
Upvotes: 1
Reputation: 1791
If you want to check if the nickname already exists, you can try something like this (if I unterstood your model correct)
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"UserPreferences"
inManagedObjectContext:context]];
[NSPredicate predicateWithFormat:@"Selection == %@ AND PreferenceTypes == 'nickname'", newUser.nickname]
NSArray *ary = [context executeFetchRequest:fetchRequest error:&err];
if(ary && ary.count > 0)
{
// handle "user exists" case
}
else
{
// handle "create user" case
}
I hope that was what you was looking for. If I understood your question wrong or it doesn't work, let me know ;)
Upvotes: 0