Realm subquery with count - ios

People and groups. How many people are in each group? A simple scenario for an SQL database. Please help fetching this in Realm.
I have People:
#interface PersonObject : RLMObject
#property int idx;
#property NSString *firstName;
#property NSString *lastName;
#property RLMArray <PersonGroupId *> <PersonGroupId> *connectedGroupIds;
And Groups:
#interface GroupObject : RLMObject
#property int idx;
#property NSString *name;
I need to get a list of all groups with a total number of people in each of them. Currently it's done by looping through groups and it's really slow:
for (GroupObject *group in groups)
{
RLMResults *result = [PersonObject objectsWhere:#"ANY connectedGroupIds.idx == %d",group.idx];
group.numberOfMembers = result.count;
}
Is it possible to fetch that with just one query/subquery without looping through groups?

It sounds like you're trying to model a scenario where a person can belong to multiple groups, and a group can contain multiple people. To do that I'd suggest a model that looks like:
#interface PersonObject : RLMObject
#property NSString *firstName;
#property NSString *lastName;
#property RLMArray<GroupObject> *connectedGroups;
#end
#interface GroupObject : RLMObject
#property NSString *name;
#property RLMLinkingObjects<PersonObject> *members;
#end
#implementation GroupObject
+ (NSDictionary *)linkingObjectsProperties
{
return #{ #"members": [RLMPropertyDescriptor descriptorWithClass:PersonObject.class propertyName:#"connectedGroups"] };
}
#end
This takes advantages of Realm's built in support for relationships rather than trying to mimic them using per-object identifiers. This model would allow you to determine the number of members in a group using group.members.count.

Related

How to do enumerateObjectsUsingBlock-like stuff with JSONModelArray?

I use JSONModel to hold my app datasource, and use -(id)initWithArray:(NSArray *)array modelClass:(Class)cls generated an JSONModelArray, now I want to do some search stuff like enumerateObjectsUsingBlock: method does. But I found that JSONModelArray is not inherited from NSArray.
So, how can I do this?
Try use BWJSONMatcher to convert json string to a NSArray.
For example, your json string seems like :
[{"name":"Arron","age":20,"grade":2},{"name":"Burrows","age":21,"grade":2}]
All you have to do is declare your own data model:
#interface Student : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, assign) NSInteger age;
#property (nonatomic, assign) NSInteger grade;
#end
BWJSONMatcher will help you convert it to a NSArray in a very neat way:
NSArray *students = [BWJSONMatcher matchJSON:jsonString withClass:[Student class]];

NSPredicate for property of object in NSArray of NSArray

I have an objects like the below structure: Transaction has an array of Items. Item has an array of SubItems
#interface Transaction : NSObject
#property (nonatomic, copy) NSString *id;
#property (nonatomic, assign) NSInteger status;
#property (nonatomic, strong) NSArray *items; // array of Item
#end
#interface Item : NSObject
#property (nonatomic, copy) NSString *identifier;
#property (nonatomic, assign) NSInteger price;
#property (nonatomic, strong) NSArray *subitems;
#end
#interface SubItem : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, assign) NSInteger price;
#end
I would like to create predicate to find name and price of Subitem from NSArray of Transaction
pred = [NSPredicate predicateWithFormat:
#"ANY SELF.%K.%K.%K contains %#",
#"items",
#"subitems",
#"name",
#"MY_SUB_ITEM_NAME"];
This generates the error below.
failed: caught "NSInvalidArgumentException", "Can't do regex matching
on object (
MY_SUB_ITEM_NAME )."
However when I use the similar format to find out properties in Transaction. It works fine.
pred = [NSPredicate predicateWithFormat:
#"SELF.%K LIKE %#",
#"identifier",
#"TX001"];
How should I correct my predicate to query property in Item and SubItem
I think that cannot work with -predicateWithFormat: for two aggregation levels. On each level you can have a ANY or an ALL aggregation. So the "right" syntax would be ANY items ANY subitems.… = …. Or ANY items ALL subitems.…= …" or …
You can try to build the predicate manually with NSExpression, probably using subqueries.
Oh, you are not using Core Data, what I assumed. So simply do it manually in a loop or use a computed property.

NString value of custom object gets lost after segue (Objective C)

Heyo Guys
So I am more or less new to Objective C and got to a problem which I seem unable to solve.
I created a class called "Students" which all have a name surname etc. All those Students are put into a NSMutableArray (before they get created from JSON , that seems to work without a problem though). Then all the names of the students are put into a ListView. Afterwards when a name is clicked (segue passes the object), one should see the details of said student (i.e. his full name, his id, his street).
The problem is that all the string values of the student object seem to get lost.
I checked that all the student object work just fine. I think the problem lies at the #property in my student class.
Any comments and suggestions are appreciated
This is an excerpt of student.h As said only the string values get lost, the int (here the plz value) remains correct
#property (nonatomic, weak)NSString *lastname;
#property (nonatomic, weak)NSString *surname;
#property (nonatomic, weak)NSString *street;
#property (nonatomic, assign)int plz;
EDIT:
Here is where i parse my json.
for (NSDictionary *dic in jsonArray){
NSNumber *identity = [dic valueForKey:#"id"] ;
NSString *firstName = (NSString*) [dic valueForKey:#"first_name"];
NSString *lastName = (NSString*) [dic valueForKey:#"last_name"];
NSString *street = (NSString*) [dic valueForKey:#"street"];
NSNumber *plz = [dic valueForKey:#"plz"] ;
NSString *birth = (NSString*) [dic valueForKey:#"date_of_birth"];
NSArray *bills = dic[#"bills"];
NSArray *hours = dic[#"hours"];
NSLog(#"First %#",firstName);
Student *student = [[Student alloc]initWithLastName:lastName withSurname:firstName withStreet:street withPLZ:plz withOrt:#"Uitikon" withBirthDate:birth withOccupation:#"Schüler"];
[ApprenticeList addObject:student];
}
EDIT 2 :
I found out that the string values get lost even before the segue. All these objects are created in
ViewDidLoad
But in
prepareforsegue
all the values are allready null (except for the int) .So the only place where the student objects work is in
ViewdidLoad
#property (nonatomic, weak)NSString *lastname;
#property (nonatomic, weak)NSString *surname;
#property (nonatomic, weak)NSString *street;
change to
#property (nonatomic, strong)NSString *lastname;
#property (nonatomic, strong)NSString *surname;
#property (nonatomic, strong)NSString *street;
You can also use 'copy'. It will cause the setter for that property to create a copy of the object, otherwise it is identical to strong.

Get Specific data of relational entity from CoreData

I am having Country, State and City entities in my CoreData model. Here is the structure:
#interface Country : NSManagedObject
#property (nonatomic, retain) NSString * cId;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSSet *states; // This will contain State object
#end
#interface State : NSManagedObject
#property (nonatomic, retain) NSString * sId;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSSet *cities; // This will contain City object
#property (nonatomic, retain) Country *country;
#end
#interface City : NSManagedObject
#property (nonatomic, retain) NSString * cityId;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) State *state;
#end
As you can see I have one to many relation between Country - State and State - City.
Now what I want is to fetch Country list with all states, but don't want city list associated with States.
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Country"];
[request setPropertiesToFetch:#[#"cId", #"title", #"states"]];
NSError *error = nil;
NSArray *aryCategories = [gblAppDelegate.managedObjectContext executeFetchRequest:request error:&error];
setPropertiesToFetch Setting this property allows to have only mentioned fields from the entity but how to set only sid and title for NSSet states which contain State object.
For getting city list I will send another request.
Please help me on this.
You do not need propertiesToFetch. This feature (which BTW needs the NSDictionaryResultType) is for special situations where you have lots of data and only need certain fields. This is definitely not necessary in your case!
Just fetch the Country objects. Core Data will automatically optimize to avoid fetching unnecessary data into memory.
NSFetchRequest *request = [NSFetchRequest fetchRequestsWithEntityName:#"Country"];
// consider sorting
NSArray *countryList = [context executeFetchRequest:request error:nil];
You now have a country list. It contains also the states. To get the states of a particular country could not be simpler.
NSSet *states = country.states;
NB: I think calling your to-one relationship from City to State "country" is problematic. I suggest renaming it to state.

Get related / associated entities from NSSet - CoreData

I have 2 autogenerated entities :
#interface ContactEntity : Entity
#property (nonatomic, retain) NSString *caption;
#property (nonatomic, retain) NSString *image;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *text;
#property (nonatomic, retain) NSSet *pointLink;
#end
#interface PointEntity : Entity
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#property (nonatomic, retain) NSSet *entityLink;
#property (nonatomic, retain) EntityEntity *entityTypeLink;
#end
They are linked between each other as ManyToMany, i.e. one contact has many points, one point has many contacts inside.
Then a i get first entity :
ContactModel *contact = [[[ContactModel alloc] init] autorelease];
// this is FetchRequest, returns array of all entities
self.items = [contact list:contact];
// i get only one, all is OK here, this entity has related PointEntity in DB
ContactEntity *contactEntity = [self.items objectAtIndex:self.selection];
And when i try to get related PointEntity using NSSet in selected ContactEntity i always get NULL or empty array. Neither of this works :
NSArray *points = [contactEntity.pointLink allObjects];
PointEntity *pointEntity = [contactEntity.pointLink anyObject];
NSInteger x1 = [points count]; // always 0
id x2 = pointEntity.latitude; // always 0
for (PointEntity *x in contactEntity.pointLink) // isn't enumerated because count = 0
{
id x3 = x.latitude;
}
Any thoughts are appreciated. Did i miss something, maybe i need to use NSPredicate to select entities from PointEntity that are related to ContactEntity?
Thanks.
P.S. My question is similar to this but that suggestion does not work for me, i cannot get loaded associated entities using NSSet of main entity :(
CoreData: many-to-many relationship
Answer is found ... i tried to use property of the autogenerated entities when created new records in CoreData, in the meantime the correct way is to use generated methods like - addPointLinkObject, addEntityLinkObject, etc
Example, i have 3 tables :
Contacts (one person may have many locations)
<< - >>
Points (one location can contain many people)
<< - >
EntityTypes (just a type of a location, in this case type is CONTACT)
One of the entities autogenerated by xCode :
#interface PointEntity : Entity
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#property (nonatomic, retain) NSSet *entityLink; // reference to Contacts table (ManyToMany)
#property (nonatomic, retain) EntityEntity *entityTypeLink; // reference to EntityType table (OneToMany)
#end
#interface PointEntity (CoreDataGeneratedAccessors)
- (void)addEntityLinkObject:(ContactEntity *)value;
- (void)removeEntityLinkObject:(ContactEntity *)value;
- (void)addEntityLink:(NSSet *)values;
- (void)removeEntityLink:(NSSet *)values;
#end
I tried to do the following :
// create 3 new instances - one for each entity
ContactEntity *contactEntity = [model create:model];
PointEntity *pointEntity = [point create:point];
EntityModel *entity = [[[EntityModel alloc] init] autorelease];
entity.name = model.table;
EntityEntity *entityEntity = [[entity list:entity] objectAtIndex:0];
// then i tried to use entity's properties directly to bind entities
// it works, but it works only on DB level when we add new records, but somehow something was missed and thus such selection did not work later - [pointEntity allObjects]
//pointEntity.entityTypeLink = entityEntity; // WRONG !!!
//pointEntity.entityLink = contactEntity.pointLink;
//contactEntity.pointLink = pointEntity.entityLink;
// then i replaced 3 lines above with these ones
[pointEntity addEntityLinkObject:contactEntity]; // CORRECT !!!
[contactEntity addPointLinkObject:pointEntity];
[entityEntity addPointLinkObject:pointEntity];
[context save]; // save changes made with entities in current CoreData context
// now [pointEntity allObjects] and [pointEntity anyObject] work as expected
Useful links -
Coredata and Generated subclass for NSManagedObject with relations
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html#//apple_ref/doc/uid/TP40002154

Resources