Subquery and IN Statement in realm - ios

I have three realm objects. Department, section and user. section is a kind of sub department in a department. But there will be users under each section and each department.
#interface Department : RLMObject
#property NSString *name;
#property BOOL isCollapsed;
#property (nonatomic, strong) RLMArray<Section> *sections;
#property (nonatomic, strong) RLMArray<User> *users;
#end
#interface Section : RLMObject
#property NSString *name;
#property BOOL isCollapsed;
#property RLMArray<User> *users;
#end
#interface User : RLMObject
#property NSString *department;
#property NSString *email;
#property NSString *firstname;
#property NSString *lastname;
#property NSString *fullname;
#end
What i want to do is, I want to search all the user whose first name and last name contains "a" and linked with department and section.
Now what i am doing is
searchText = [searchText lowercaseString];
RLMResults *sections = [Section objectsWhere:
[NSString stringWithFormat:#"SUBQUERY(users, $user, $user.fullname contains '%#' OR $user.nickname contains '%#').#count > 0",searchText,searchText]];
NSMutableArray *sectionNames = [NSMutableArray array];
for (Section *section in sections)
{
[sectionNames addObject:section.name];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(sections, $section, $section.name IN %#).#count > 0", sectionNames];
RLMResults *filteredDepartments = [[Department objectsWithPredicate:predicate] sortedResultsUsingProperty:#"name" ascending:YES];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SUBQUERY(users, $user, $user.fullname contains %# OR $user.nickname contains %#).#count > 0",searchText,searchText];
RLMResults *departments = [filteredDepartments objectsWithPredicate:pred];
Right now the issue is there is two sections in sections array but in the deparments results, it returns null. please help. thanks.
Note: If one user already belongs to section then that user will not be include in department.

Maybe you do not need to use subquery. More simply, you can use ANY in query like the following:
[Department objectsWhere:
#"ANY users.firstname CONTAINS %# OR ANY users.lastname CONTAINS %# OR ANY sections.users.firstname CONTAINS %# OR ANY sections.users.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 User : RLMObject
...
#property (readonly) RLMLinkingObjects *departments;
#property (readonly) RLMLinkingObjects *sections;
#end
#implementation User
+ (NSDictionary *)linkingObjectsProperties {
return #{#"departments": [RLMPropertyDescriptor descriptorWithClass:Department.class propertyName:#"users"],
#"sections": [RLMPropertyDescriptor descriptorWithClass:Section.class propertyName:#"users"]};
}
#end
Then you can get departments and sections where the user belongs to from User's property, like the following:
RLMResults *users = [User objectsWhere:#"firstname CONTAINS %# OR lastname CONTAINS %#" , searchText, searchText];
for (User *user in users) {
NSLog(#"%#", user.departments);
NSLog(#"%#", user.sections);
}

Related

Apply NSPredicate to Array which is inside an a dictionary IOS

#interface Contact : NSObject
#property (assign, nonatomic) NSInteger *cid;
#property (strong, nonatomic) NSString * name;
#property (strong, nonatomic) NSMutableArray *arrPhone;
#end
I have created "Contact" NSObject for above dictionary which has arrPhone array.
Now I have an array which has list of contact object. Now i need those contact object whose phone array contains number which starts with #"11" (my search string)
I have tried the following:
NSPredicate *p1 =[NSPredicate predicateWithFormat:#"arrPhone contains[cd] %#)",searchText];
But its not working. Please help.
Since arrPhone is itself an array, you need to test whether any of its members matches your search criteria, not whether it does. So amend your predicate to use ANY:
NSPredicate *p1 =[NSPredicate predicateWithFormat:#"ANY arrPhone contains[cd] %#)",searchText];
Try this
[collection[#"phone"] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF beginswith[c] '11'"]];

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.

Find objects which contains subjects with given parameter

I'm trying to figure out NSPredicate which will find all objects which contains subjects with given parameter.
I have this objects:
#interface User : NSManagedObject
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSMutableOrderedSet * items;
#end
and:
#interface Item : NSManagedObject
#property (nonatomic, strong) NSNumber * itemId;
#end
I have array of users and I want to find all users which contain item with itemId == 1.
Have no more clues how to get it
NSNumber *theItemId = #1; // The id that you are looking for
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY items.itemId == %#", theItemId];
should do it. You can use this predicate in a fetch request to fetch all matching users,
or with filteredArrayUsingPredicate: to filter an array of users.

Search NSArray of objects for String matching any property

I have an NSArray of objects, and those objects have 10 properties. I would like to do a text search of those objects.
I know how to search 1 property at a time, but is there an easy way to search ALL properties at once?
Here is a list of properties that my objects have:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * phone;
#property (nonatomic, retain) NSString * secondaryPhone;
#property (nonatomic, retain) NSString * address;
#property (nonatomic, retain) NSString * email;
#property (nonatomic, retain) NSString * url;
#property (nonatomic, retain) NSString * category;
#property (nonatomic, retain) NSString * specialty;
#property (nonatomic, retain) NSString * notes;
#property (nonatomic, retain) NSString * guid;
If I search for "doctor", I would like to see all results where 1 or more of these properties has the word "doctor" in it. For example, if 1 object has a category of "doctor", and another object has an email address of "smith#doctorsamerica.com", they should both show up in the results.
NSString *searchTerm = #"search this";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF LIKE[cd] %#", searchTerm];
NSArray *filtered = [array filteredArrayUsingPredicate:predicate];
If there is a specific property you can change the predicate to:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.propertyName LIKE[cd] %#", searchTerm];
To search all properties, you will have to bind them together with a logical operator
NSString *query = #"blah";
NSPredicate *predicateName = [NSPredicate predicateWithFormat:#"name contains[cd] %#", query];
NSPredicate *predicatePhone = [NSPredicate predicateWithFormat:#"phone contains[cd] %#", query];
NSPredicate *predicateSecondaryPhone = [NSPredicate predicateWithFormat:#"secondaryPhone contains[cd] %#", query];
NSArray *subPredicates = [NSArray arrayWithObjects:predicateName, predicatePhone, predicateSecondaryPhone, nil];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Add a matches: method to your class:
- (BOOL)matches:(NSString *)term {
if ([self.name rangeOfString:term options:NSCaseInsensitiveSearch| NSDiacriticInsensitiveSearch].location != NSNotFound) {
return YES;
} else if ([self.phone rangeOfString:term options:NSCaseInsensitiveSearch| NSDiacriticInsensitiveSearch].location != NSNotFound) {
return YES;
} else if (...) { // repeat for all of the properties
return YES;
}
return NO;
}
Now you can iterate over your objects checking each one:
NSArray *peopleArray = ... // the array of objects
NSString *someTerm = ... // the search term
NSMutableArray *matches = [NSMutableArray array];
for (id person in peopleArray) {
if ([person matches:someTerm]) {
[matches addObject:person];
}
}
Instead of hardcoding each property name individually, you could get an array of the class's property names: List of class properties in Objective-C, (assuming they were all strings, or you could check if each property's type is of class NSString but I'm not sure how to do this)
and then on each of the objects you'd like to search, you could iterate through each of the object's properties and inspect the value on each:
id objectValue = [object valueForKey:[NSString stringWithUTF8String:propertyName]];
// let's say the property is a string
NSString *objectValueString = (NSString *)objectValue;
Then you could check if the property matches your searchTerm with something like:
BOOL propertyValueMatchesSearchTerm = [objectValueString rangeOfString:mySearchTermString].location != NSNotFound;
if (propertyValueMatchesSearchTerm) {
// add object to search results array
}

NSPredicate and custom NSObject

I have a custom NSObject which I need to filter. I have been trying to user NSPredicate to do this, but was unable to so far. Here is my object's structure:
#interface MyBigObject : NSObject
#property (nonatomic, strong) NSString *firstAttribute;
#property (nonatomic, strong) NSString *secondAttribute;
#property (nonatomic, strong) NSMutableArray *featuresArray;
#end
The featuresArray contains other custom objects:
typedef enum {
FeatureExists = YES, //Default
FeatureDoesNotExist = NO,
FeatureNotAvailable
} FeatureValue;
#interface MySmallObject : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic) FeatureValue feature;
#end
I want to only return the objects containing a MySmallObject with a certain title and which has feature == FeatureExists.
I've tried something like (and other variations) but to no success:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(featuresArray, $object, $object.title CONTAINS[c] %# AND $object.feature = %d).#count > 0)", #"Fenced", FeatureExists];
NSLog(#"predicate = %#", predicate);
If I understood correctly, the format of your predicate is wrong. It should be [NSPredicate predicateWithFormat:#"SUBQUERY(featuresArray, $object, $object.title CONTAINS[c] %# AND $object.feature = %d).#count > 0", #"some string", FeatureExists]; where you check in your array if any object matches the requirements.

Resources