Subquery between three objects [duplicate] - objective-c

This question already has an answer here:
Subquery and IN Statement in realm
(1 answer)
Closed 6 years ago.
I have three different models Department ,subdepartments and employee
#interface Department : RLMObject
#property NSString *name;
#property (nonatomic, strong) RLMArray<SubDepartment> *subDepartments;
#property (nonatomic, strong) RLMArray<Employee> *employees;
#end
#interface SubDepartment : RLMObject
#property NSString *name;
#property (nonatomic, strong) RLMArray<Employee> *employees;
#end
#interface Employee : RLMObject
#property NSString *department;
#property NSString *email;
#property NSString *firstname;
#property NSString *lastname;
#property NSString *fullname;
#property NSString *imgUrl;
#property NSString *imgWall;
#property NSString *nickname;
#end
I want to search which department of employee has contains "a" in their first name and last name and also want to search through subdepartment employees whose name contains "a" in their first name and last name.
I was trying to do like that.
RLMResults *subdepartments = [SubDepartment objectsWhere:
[NSString stringWithFormat:#"SUBQUERY(employees, $e, $e.firstname contains '%#' OR $e.lastname contains '%#' OR $e.fullname contains '%#' OR $e.nickname contains '%#').#count > 0",searchText,searchText,searchText,searchText]];
NSMutableArray *subDepartmentNames = [NSMutableArray array];
for (SubDepartment *subDepartment in sections)
{
[subDepartmentNames addObject:subDepartment.name];
}
RLMResults *departments = [Department objectsWhere:
[NSString stringWithFormat:#"SUBQUERY(employees, $e, $e.firstname contains '%#' OR $e.lastname contains '%#' OR $e.fullname contains '%#' OR $e.nickname contains '%#').#count > 0",searchText,searchText,searchText,searchText]];
RLMResults<Department *> *filteredDepartments = [departments objectsWhere:
[NSString stringWithFormat:#"SUBQUERY(subDepartments, $d, $d.name IN '%#').#count > 0",sectionNames]];

Maybe you do not need to use subquery. More simply, you can use ANY in query like the following:
[Department objectsWhere:
#"ANY employees.firstname CONTAINS %# OR ANY employees.lastname CONTAINS %# OR ANY subDepartments.employees.firstname CONTAINS %# OR ANY subDepartments.employees.lastname CONTAINS %#", searchText, searchText, searchText, searchText];
But I think using inverse relationships is more easier. In Realm, inverse relationships is defined with RLMLinkingObjects.
You can add inverse relationships to User class as follows:
#interface Employee : RLMObject
...
#property (readonly) RLMLinkingObjects *departments;
#property (readonly) RLMLinkingObjects *subDepartments;
#end
#implementation User
+ (NSDictionary *)linkingObjectsProperties {
return #{#"departments": [RLMPropertyDescriptor descriptorWithClass:Department.class propertyName:#"employees"],
#"subDepartments": [RLMPropertyDescriptor descriptorWithClass:SubDepartment.class propertyName:#"employees"]};
}
#end
Then you can get departments and sections where the user belongs to from User's property, like the following:
RLMResults *employees = [Employee objectsWhere:#"firstname CONTAINS %# OR lastname CONTAINS %#" , searchText, searchText];
for (Employee *employee in employees) {
NSLog(#"%#", employee.departments);
NSLog(#"%#", employee.subDepartments);
}

Related

Why is my JSONModel Class being treated as a NSDictionary?

This is my JSON fetched from the API:
{
categories = (
{
icon = {
prefix = "https://ss3.4sqi.net/img/categories_v2/food/italian_";
suffix = ".png";
};
id = 4bf58dd8d48988d110941735;
name = "Italian Restaurant";
pluralName = "Italian Restaurants";
primary = 1;
shortName = Italian;
}
);
hasPerk = 0;
id = 4b5bed2ef964a520971d29e3;
location = {
address = "HS-5";
cc = IN;
city = "New Delhi";
country = India;
crossStreet = "Kailash Colony Market";
distance = 4061;
formattedAddress = (
"HS-5 (Kailash Colony Market)",
"New Delhi 110024",
Delhi,
India
);
labeledLatLngs = (
{
label = display;
lat = "28.55272788981442";
lng = "77.24192322690806";
}
);
lat = "28.55272788981442";
lng = "77.24192322690806";
postalCode = 110024;
state = Delhi;
};
name = "The Big Chill Cafe";
referralId = "v-1546605733";
}
And here is my model class:
#interface LocationModel : JSONModel
#property (nonatomic) NSInteger distance;
#property (nonatomic) NSInteger postalCode;
#property (nonatomic) NSString *address;
#property (nonatomic) NSString *cc;
#property (nonatomic) NSString *city;
#property (nonatomic) NSString *country;
#property (nonatomic) NSString *crossStreet;
#property (nonatomic) NSString *lat;
#property (nonatomic) NSString *lng;
#property (nonatomic) NSString *state;
#property (nonatomic) NSArray *formattedAddress;
#property (nonatomic) NSArray *labeledLatLngs;
#end
#protocol VenueModel;
#interface VenueModel : JSONModel
#property (nonatomic) NSArray *categories;
#property (nonatomic) BOOL hasPerk;
#property (nonatomic) NSString *id;
#property (nonatomic) LocationModel *location;
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *referralId;
#end
#interface Restaurant : JSONModel
#property (nonatomic) NSInteger confident;
#property (nonatomic) NSArray <VenueModel*> *venues;
#end
Now, I am trying to get the values like this:
restaurant = [[Restaurant alloc] initWithDictionary
[NSJSONSerialization JSONObjectWithData:JSON
options:NSJSONReadingAllowFragments error:nil][#"response"] error:&error];
VenueModel *obj = [restaurant.venues firstObject];
LocationModel *locationObj = obj.location;
The values are coming fine till the VenueModel object, after that when I am trying to access the LocationModel object, i.e
LocationModel *locationObj = obj.location
, its throwing the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI location]: unrecognized selector sent to instance 0x600001b82880
Somebody please help me out, I need the location object.
NSJSONSerialization will produce NSDictionaries, NSArrays, NSStrings, NSNumbers and a few other basic types from a JSON string.
Your idea to initialize a custom object ([[Restaurant alloc] initWithDictionary...) is the right idea, but that idea must be applied to nested structures as well. So, for example, the Restaurant initializer must apply that idea to its venues array.
Otherwise, you'll get the crash you're getting, treating one of the venue objects as if its a VenueModel instead of what it really is (an NSDictionary).
You didn't post initWithDictionary, but it needs to look something like this, especially the part where the objects it contains are initialized...
- (id)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
// ...
self.venues = [NSMutableArray array];
if (self) {
for (NSDictionary *venueDictionary in dictionary[#"venues"]) {
VenueModel *venue = [[VenueModel alloc] initWithDictionary:venueDictionary];
[self.venues addObject:venue];
}
}
// and so on, for every other object type nested in dictionary
return self;
}
Moreover, as you can see, VenueModel must also follow this practice, turning the location data into a LocationModel, and so on...

Subquery between two objects

I have two different models Department and employee
#interface Department : RLMObject
#property NSString *name;
#property (nonatomic, strong) RLMArray<Employee> *employee;
#end
#interface Employee : RLMObject
#property NSString *department;
#property NSString *email;
#property NSString *firstname;
#property NSString *lastname;
#property NSString *fullname;
#property NSString *imgUrl;
#property NSString *imgWall;
#property NSString *nickname;
#end
I want to search which department of employee has contains "a" in their first name and last name. please help. thanks.
What you'd like to do is as follows:
RLMResults *r = [Department objectsWhere:
#"SUBQUERY(employee, $e, $e.firstname CONTAINS 'a' AND $e.lastname CONTAINS 'a').#count > 0"];

CoreData and Entity Relationship Issue

In my core data model I have two entities:
InventoryItem
InventoryAction
InventoryItem has the following attributes:
Attributes:
#property (nonatomic, retain) NSNumber * allowsActions;
#property (nonatomic, retain) NSNumber * assetID;
#property (nonatomic, retain) NSNumber * inventoryObjectID;
#property (nonatomic, retain) NSString * objectDescription;
#property (nonatomic, retain) NSNumber * quantity;
#property (nonatomic, retain) NSNumber * retired;
#property (nonatomic, retain) NSString * serialNumber;
Relationships:
#property (nonatomic, retain) NSSet *action;
InventoryAction has the following attributes
Attributes:
#property (nonatomic, retain) NSDate * actionDate;
#property (nonatomic, retain) NSNumber * actionID;
#property (nonatomic, retain) NSString * actionLongValue;
#property (nonatomic, retain) NSString * actionShortValue;
#property (nonatomic, retain) NSString * notes;
#property (nonatomic, retain) NSNumber * userActionID;
#property (nonatomic, retain) NSString * userAuthorizingAction;
#property (nonatomic, retain) NSString * userPerformingAction;
#property (nonatomic, retain) NSNumber * userPerformingActionExt;
#property (nonatomic, retain) NSNumber * inventoryObjectID;
Relationships
#property (nonatomic, retain) InventoryItem *object;
Now that you have all that information I will tell you that all of the values that are being stored in the CoreData model are coming from a web service in JSON format. All of the values are storing properly.
The problem that I'm having is that when I fetch the data from CoreData it only brings back values that are unique.
For instance:
The last InventoryItem has 6 actions associated with it:
"",
"",
"it looks kinda slimy",
"oh god....why would you do that to an inanimate object you sicko",
"its all slimy now",
"everything looks good"
As 1 and 2 have the same value it only retrieves value 1, 3, 4, 5, and 6.
Here is the code I'm using to fetch the data:
- (void)loadDetails
{
_fetchRequest = [[NSFetchRequest alloc] init];
_entity = [NSEntityDescription entityForName:#"InventoryObject" inManagedObjectContext:[self managedObjectContext]];
_sort = [NSSortDescriptor sortDescriptorWithKey:#"inventoryObjectID" ascending:YES];
_sortDescriptors = [[NSArray alloc]initWithObjects:_sort, nil];
[_fetchRequest setEntity:_entity];
[_fetchRequest setSortDescriptors:_sortDescriptors];
NSError *error;
_fetchedObjects = [[self managedObjectContext] executeFetchRequest:_fetchRequest error:&error];
for (InventoryItem *inventoryItem in _fetchedObjects) {
NSLog(#"Object Description: %#", [inventoryItem valueForKey:#"objectDescription"]);
NSLog(#"Object ID: %#", [inventoryItem valueForKey:#"inventoryObjectID"]);
InventoryAction *action = (InventoryAction *)inventoryItem.action;
NSLog(#"Action Long Value: %#", [action valueForKey:#"actionLongValue"]);
NSLog(#"Notes: %#", [action valueForKey:#"notes"]);
}
}
Here is the JSON return:
[{"MediaInventoryObjectsId":1,"AssetId":15,"Quantity":1,"SerialNumber":"R45DFL5","Description":"Test Camera 1","AllowActions":true,"Retired":false,"Actions":[{"MediaInventoryActionsId":3,"MediaInventoryObjectsId":1,"UserPerformingActionExt":4444,"UserActionId":1,"ActionDate":"2014-05-19T15:31:45.6","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":4,"MediaInventoryObjectsId":1,"UserPerformingActionExt":4444,"UserActionId":2,"ActionDate":"2014-05-19T15:31:45.6","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":"everything looks good","ActionShortValue":"IN","ActionLongValue":"Check In"}]},{"MediaInventoryObjectsId":2,"AssetId":15,"Quantity":2,"SerialNumber":"IDKMYBFFJILL","Description":"Vanilla Ice Cream","AllowActions":true,"Retired":false,"Actions":[]},{"MediaInventoryObjectsId":3,"AssetId":15,"Quantity":1,"SerialNumber":"R2D23P0","Description":"Droid Bee Box","AllowActions":true,"Retired":false,"Actions":[{"MediaInventoryActionsId":5,"MediaInventoryObjectsId":3,"UserPerformingActionExt":4444,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.53","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":6,"MediaInventoryObjectsId":3,"UserPerformingActionExt":4444,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.58","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":"everything looks good","ActionShortValue":"IN","ActionLongValue":"Check In"},{"MediaInventoryActionsId":7,"MediaInventoryObjectsId":3,"UserPerformingActionExt":5555,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.6","UserPerformingAction":"farmer","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":8,"MediaInventoryObjectsId":3,"UserPerformingActionExt":5555,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.6","UserPerformingAction":"farmer","UserAuthorizingAction":"mccroskeyl","Notes":"its all slimy now","ActionShortValue":"IN","ActionLongValue":"Check In"},{"MediaInventoryActionsId":9,"MediaInventoryObjectsId":3,"UserPerformingActionExt":6666,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.61","UserPerformingAction":"intern1","UserAuthorizingAction":"mccroskeyl","Notes":"it looks kinda slimy","ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":10,"MediaInventoryObjectsId":3,"UserPerformingActionExt":6666,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.62","UserPerformingAction":"intern1","UserAuthorizingAction":"mccroskeyl","Notes":"oh god....why would you do that to an inanimate object you sicko","ActionShortValue":"IN","ActionLongValue":"Check In"}]}]
I read somewhere that NSSet possibly only brings back unique records. Is this true and if so what would be the solution as the relationship: action is an NSSet?
Your relationship is declared like this:
#property (nonatomic, retain) NSSet *action;
But you access it like this:
InventoryAction *action = (InventoryAction *)inventoryItem.action;
That's incorrect. The action relationship is an NSSet containing instances of InventoryAction, it's not an InventoryAction itself. Even though you assign this to an InventoryAction *, what you actually have is an NSSet. Then you do this:
NSLog(#"Notes: %#", [action valueForKey:#"notes"]);
If you call valueForKey: on an NSSet, it returns all unique values of that key for objects in the set. In this case it returns all unique values of the notes attribute on the InventoryAction objects in the set, which is what you're seeing. If you want to find every instance instead of every unique value, you need something like:
NSSet *actions = inventoryItem.action;
for (InventoryAction *action in actions) {
NSLog(#"Action note: %#", action.note);
}
[Warning, the above is just typed into my web browser....]

Creating Dictionary from a model that contains nil values

I have the following Model:
#interface Person : NSObject
#property (nonatomic, copy) NSString *firstName;
#property (nonatomic, copy) NSString *middleName;
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *status;
#property (nonatomic, copy) NSString *favoriteMeal;
#property (nonatomic, copy) NSString *favoriteDrink;
#property (nonatomic, copy) NSString *favoriteShow;
#property (nonatomic, copy) NSString *favoriteMovie;
#property (nonatomic, copy) NSString *favoriteSport;
-(NSDictionary *)getSomeInfo;
-(NSDictionary *)getAllInfo;
#end
Part 1:
I want getSomeInfo to return NSDictionary (e.g. {"firstName", self.firstName}) for all the fields that does not contain nil. How can I do that? (I could check every value but I wonder if there's a better way)
Part 2:
I want getAllInfo to return NSDictionary with all the property and if one contains nil then it should throw an error. Again do I have to write a long conditional statement to check or is there a better way?
Note: I want to do this without using external library. I'm new to the language so I'm open to suggestions if there's a better pattern in Objective-C.
There are two approaches.
1) Check each value:
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
if (self.firstName.length) {
res[#"firstName"] = self.firstName;
}
if (self.middleName.length) {
res[#"middleName"] = self.middleName;
}
// Repeat for all of the properties
return res;
}
2) Use KVC (Key-value coding):
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
NSArray *properties = #[ #"firstName", #"middleName", #"lastName", ... ]; // list all of the properties
for (NSString *property in properties) {
NSString *value = [self valueForKey:property];
if (value.length) {
res[property] = value;
}
}
return res;
}
For the getAllInfo method you can do the same but instead return nil if any value is missing. Treat the nil results as your indication that not all properties have a value.

Can't use NSMutableArray data after Parsing with NSXMLParser (objective-c)

Hi I did parsing with NSXMLParser of some xml :
<company>
<name>Idan</name>
<country>Israel</country>
.....
<gender>man</gender>
</company>
I see that parsing success , now I have the MutableArray with one object that contain all strings (Idan,Israel etc.) but when I want to use this array, I can't get strings it contain.
When I do :
NSMutableArray *use = [pars users ];
NSLog(#"%#",use );
(users it's my array with object) I see:
<List:03f5a78>
where List in my code is:
#import <Foundation/Foundation.h>
#interface List : NSObject{
NSString *name;
NSString *country;
NSString *status;
NSString *gender;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *country;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *gender;
#end
#import "List.h"
#implementation List
#synthesize name,date,city,country,status, gender;
#end
I try to do something like this:
NSMutableArray *use = [pars users.name ];
NSLog(#"%#",use );
but is not working, any ideas how to fix this?
Override the description method of your List class, and return a string which includes the values of all of the properties, then output it like you did the first time. The console will then print the value you returned.
Example:
#implementation List
...
...
-(NSString *)description
{
NSMutableString *desc = [NSMutableString string];
[desc appendFormat:#"name=%#, ", self.name];
[desc appendFormat:#"country=%#, ", self.country];
[desc appendFormat:#"status=%#, ", self.status];
[desc appendFormat:#"gender=%#", self.gender];
return desc
}
...
...
#end