Query Parse containsString make insensitive - ios

I wonder if someone would be able to help. I am trying to search a Parse class for a term using SearchBar. The containsString however is case sensitive and i would like it to be case insensitive. Please see code below;
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName: #"Firefacts"];
[query whereKeyExists:#"Number"];
[query whereKey:#"Number" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
Any help would be greatly appreciated.

Use Regular Expression (?i) instead ! credit goes to https://stackoverflow.com/a/9655203/2398886 , https://stackoverflow.com/a/9655186/2398886 and http://www.regular-expressions.info/modifiers.html
So replace
[query whereKey:#"Number" containsString:searchTerm];
with
[query whereKey:#"Number" matchesRegex:[NSString stringWithFormat:#"(?i)%#",searchTerm]];
or you can also use
[query whereKey:#"Number" matchesRegex:searchTerm modifiers:#"i"];
Your final code should be :
PFQuery *query = [PFQuery queryWithClassName: #"Firefacts"];
[query whereKeyExists:#"Number"];
[query whereKey:#"Number" matchesRegex:searchTerm modifiers:#"i"];

For Swift use this:
query.whereKey("Number", matchesRegex: searchTerm, modifiers: "i")
reference: https://docs.parseplatform.org/ios/guide/#queries

Related

How to access fields of a pointer's value on parse.com with iOS?

I am designing a database. According to documentation, when number of relationships are greater than 100 and there is extra fields, I must design a Join Table. I designed this Join Table by having two pointer value.
This pointer value is pointing to _User. Later I need only rows for currUser.
This pointer value is the objectId of another table which is an entity. My question is, how can I write a query to return objects for this table in queryfortable.
Let's say:
Table _User
Table Entity
Table Join ---> objectId Pointer1(_User) Pointer2(Event)
This look like this:
This is what I have tried so far:
First I tried in viewDidLoad to get array of invitedUser from cloud and later in queryForTable:
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query whereKey:#"objectId" containedIn:_inviteList];
but I need to access _inviteList.objectId which is not possible!
I tried to use innerQuerry or relation query. but as I just started learning parse I am not able to implement this.
PFUser *friendPointer = [PFUser currentUser];
PFQuery *query2 = [PFQuery queryWithClassName:#"Event"];
[query2 whereKey:friendPointer containedIn:_inviteList];
return query2;
This also did not work for me.
PFQuery *innerQuery = [PFQuery queryWithClassName:#"Invite"];
[innerQuery whereKey:#"invitedUser" equalTo:[PFUser currentUser]];
query = [PFQuery queryWithClassName:#"Event"];
[query whereKey:#"user" matchesQuery:innerQuery];
return query;
I appreciate if anyone can help me to write this query or re-design my table in order to have access to this query.
Pleaser try this code and give me review
PFUser *user = [PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"Invite"];
[query whereKey:#"invitedUser" equalTo:user];
[query includeKey:#"invitedUser"];
[query includeKey:#"eventId"];
[query orderByDescending:#"updatedAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (error == nil)
{
for (PFObject *underobject in [objects reverseObjectEnumerator])
{
PFUser *user1 = underobject[#"invitedUser"];
NSLog(#"invitedUser is :%#",user1);
}
}
}];

PFQuery orSubQuery for PFQueryTableViewController not working

I had a table view working off of a compound query, and I updated Parse and now it seems to not work anymore. I'm not sure if it's the Parse update, but here's the situation:
The query is as follows (in the queryForTable method (which is called) of the PFQueryTableViewController):
self.parseClassName = #"Event";
PFQuery *attendeeQuery = [PFQuery queryWithClassName:self.parseClassName];
PFQuery *organizerQuery = [PFQuery queryWithClassName:self.parseClassName];
[attendeeQuery whereKey:#"attendees" containsAllObjectsInArray:[NSArray arrayWithObjects:[PFUser currentUser], nil]];
[organizerQuery whereKey:#"organizer" equalTo:[PFUser currentUser]];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[organizerQuery, attendeeQuery]];
[query includeKey:#"organizer"];
[query whereKey:#"isVisible" equalTo:[NSNumber numberWithBool:YES]];
[query orderByAscending:#"time"];
return query;
When I run this query, objectsDidLoad never gets called and my table view hangs in the loading state. Help please!
The solution has to do with the one line I didn't include above.
I had this:
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
and it turns out in the most current Parse SDK and iOS SDK, cachePolicy is broken, so one must use kPFCachePolicyIgnoreCache to get results when using a where ContainedIn clause.
Source:
https://developers.facebook.com/bugs/717037238403238/

IOS Parse Queries

I am trying to parse some objects from my data base. I'm using parse.com as web service.
The following code gives me back some object based on 4 queries, 2 of them being compound.
The variable "meetingAge" must be equal to indexAge OR "6"
The variable "meetingSex" must be equal to "sex" OR "2"
The variable "isAvaiable" must be equal to YES
The variable "author" must be different from user.username
Basically i am trying to implement a query with 2 OR conditions and 2 AND conditions.
The following code gives me a wrong result, no exceptions or stuff like that, just a wrong kind of object. I'm sure it's a syntax issue but i can't figure out how to properly write this..If i just use 1 compound query and then add the 2 AND queries to that i get a right result, but then i miss 1 filter..
PFQuery *queryAge1 = [PFQuery queryWithClassName:#"MeetingObject"];
[queryAge1 whereKey:#"meetingAge" equalTo:indexAge ];
PFQuery *queryAge2 = [PFQuery queryWithClassName:#"MeetingObject"];
[queryAge2 whereKey:#"meetingAge" equalTo:#"6"];
PFQuery *queryCompound = [PFQuery orQueryWithSubqueries:#[queryAge1,queryAge2]];
PFQuery *querySex1 = [PFQuery queryWithClassName:#"MeetingObject"];
[querySex1 whereKey:#"meetingSex" equalTo:user[#"sex"]];
PFQuery *querySex2 = [PFQuery queryWithClassName:#"MeetingObject"];
[querySex2 whereKey:#"meetingSex" equalTo:#"2"];
queryCompound = [PFQuery orQueryWithSubqueries:#[querySex1,querySex2]];
[queryCompound whereKey:#"isAvaiable" equalTo:[NSNumber numberWithBool:YES]];
[queryCompound whereKey:#"author" notEqualTo:user.username];
[queryCompound findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
}
In your line... queryCompound = [PFQuery orQueryWithSubqueries:#[querySex1,querySex2]]; you are creating a brand new query ignoring all the age stuff.
EDIT
You are overcomplicating this.
You can do this this way...
PFQuery *query = [PFQuery queryWithClassName:#"MeetingObject"];
[query whereKey:#"meetingAge" containedIn:#[#"6", indexAge]];
[query whereKey:#"meetingSex" containedIn:#[#"2", user[#"sex"]]];
[query whereKey:#"isAvaiable" equalTo:[NSNumber numberWithBool:YES]];
[query whereKey:#"author" notEqualTo:user.username];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
}
This will catch all the options you are looking for.
Also, just as a quick note. Choose your variable names to make them more readable. If they all begin with the word query it's hard to distinguish them. Also make them descriptive. queryAge1 and queryAge2 makes it harder to understand what they are doing.

Combining conditional queries isn't working properly?

I'm needing to use the parse.com method orQueryWithSubquerries: to create a combined conditional query.
Here is my code for it:
PFQuery *testQuery = [PFUser query];
[testQuery whereKey:#"displayName notEqualTo:#"Bob"];
PFQuery *testQuery2 = [PFUser query];
[testQuery whereKey:#"username" notEqualTo:#"frank"];
PFQuery *orQuery = [PFQuery orQueryWithSubqueries:#[testQuery, testQuery2]];
[orQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
PFUser *user = [users firstObject];
}
}];
The code runs fine, but it returns all of my user objects, including the ones that should be filtered out from the 2 queries. Am I using this method wrong, or is there a certain way that I need to use it?
Edit:
I have also attempted to use this method exactly like how this parse.com example does, but it still doesn't work properly.
PFQuery *testQuery = [PFUser query];
[testQuery whereKey:#"numberOfPhotos" greaterThan:#(3)];
PFQuery *testQuery2 = [PFUser query];
[testQuery whereKey:#"numberOfPhotos" lessThan:#(1)];
PFQuery *orQuery = [PFQuery orQueryWithSubqueries:#[testQuery, testQuery2]];
[orQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// users still contains every user in the app even though it shouldn't according to the parse example
}
}];
You don't need to do a compound query, just query regularly:
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"displayName" notEqualTo:#"Bob"];
[userQuery whereKey:#"username" notEqualTo:#"frank"];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
PFUser *user = [users firstObject];
}
}];
You're confusing the use of a compound query, as you have it currently,
TestQuery = All users whose display isn't bob. (includes users whose username is frank)
TestQuery2 = All users whose username isn't frank. (includes users whose display name is Bob)
When you combine them, you get all of the users, your compound is contradictory.
Basically, all of the users left out in query1 are included in query2. And, all the users left out in query2 are included in query1. When you combine these queries, they fill in the missing space and you get all users.
Update
If you're trying to do hasPrefix, it seems like it should work fine:
NSString * prefixToSearch = ...;
PFQuery * displayNameQuery = [PFUser query];
[displayNameQuery whereKey:#"displayName" hasPrefix:prefixToSearch];
PFQuery * usernameQuery = [PFUser query];
[usernameQuery whereKey:#"username" hasPrefix:prefixToSearch];
PFQuery * compoundQuery = [PFQuery orQueryWithSubqueries:#[displayNameQuery, usernameQuery]];
[orQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
NSLog(#"Found %i users", users.count);
}
}];
Update 2 - Let's Test
PFQuery *testQuery = [PFUser query];
[testQuery whereKey:#"numberOfPhotos" greaterThan:#(3)];
PFQuery *testQuery2 = [PFUser query];
[testQuery whereKey:#"numberOfPhotos" lessThan:#(1)];
PFQuery *orQuery = [PFQuery orQueryWithSubqueries:#[testQuery, testQuery2]];
[orQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// users still contains every user in the app even though it shouldn't according to the parse example
for (PFUser * user in users) {
int numberOfPhotos = [user[#"numberOfPhotos"] intValue];
if (1 <= numberOfPhotos && numberOfPhotos <= 3) {
NSLog(#"Query is failing");
}
}
}
}];
Update 3 - Predicates!
Via our conversation, we got it working with this:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"keyOne BEGINSWITH 'z' OR keyTwo BEGINSWITH 'a'"];
PFQuery * userQuery = [PFQuery queryWithClassName:#"_User" predicate:predicate];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"Found objects: %#", objects);
}];
After looking into this once again I ended up changed my solution to being regex based rather than predicate. For my purposes that means I don't need to create an additional lowercase display name property since Parse regex allows for case-insensitive options. Also I can more easily change the parameters of the search with higher levels of flexibility.
My code now looks like this:
// Uses regex to remove white space at the beginning and end of search text
NSString *modifiedSearchText = [Utility stringTrimmedForLeadingAndTrailingWhiteSpacesFromString:self.searchText];
PFQuery *userUsernameQuery = [PFUser query];
// the #"i" modifier is stated in the parse documentation and means case-insensitive
[userUsernameQuery whereKey:#"username" matchesRegex:modifiedSearchText modifiers:#"i"];
PFQuery *userDisplaynameQuery = [PFUser query];
[userDisplaynameQuery whereKey:#"displayName" matchesRegex:modifiedSearchText modifiers:#"i"];
PFQuery *userQuery = [PFQuery orQueryWithSubqueries:#[userUsernameQuery, userDisplaynameQuery]];
[userQuery orderByAscending:#"username"];
return userQuery;
In iOS 8.3, xcode 6.3.1
query.whereKey("name", matchesRegex:searchBar.text, modifiers:"i")
searches upper or lowercase words

parse.com: How to compare pointer-keys with objects?

I am using the backend service parse.com for a iOS app and I have a problem with querying it properly. I need help with the method whereKey:matchesKey:inQuery;
I have this code:
//NOT WORKING
PFQuery *query1 = [PFQuery queryWithClassName:#"Object"];
PFQuery *query2 = [PFQuery queryWithClassName:#"ObjectsRelations"];
[query2 whereKey:#"user" equalTo:[PFUser currentUser]];
[query1 whereKey:#"objectId" matchesKey:#"objectPointer" inQuery:query2];
[query1 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// No objects
}];
It is not working the way I want. I have tried several ways for it to compare the key "objectPointer" in class "ObjectsRelations" (which is a pointer to an instance of class Object) to the actual Object in query 1. I do not get any objects back, because the comparison does not work as I want, since the key objectId is just a string and the key objectPointer is a pointer to a Object.
When I run this code, I get the intended result, but this requires me to do two api-requests to get the actual objectId as a string!
//WORKING
PFQuery *query = [PFQuery queryWithClassName:#"Object"];
PFQuery *query2 = [PFQuery queryWithClassName:#"ObjectRelations"];
[query2 whereKey:#"user" equalTo:[PFUser currentUser]];
[query2 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
PFObject *firstObject = [((PFObject*)[objects firstObject]) objectForKey:#"objectPointer"];
[query whereKey:#"objectId" equalTo:firstObject.objectId];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
//Getting the objects correctly from the class Object!
}];
}];
How to do this with a single api-request? How to compare a instance of a Class to a pointer of a class with a query?
Something like this is what i want to do: (Pseudo Code)
[query1 where:SELF matches:#"objectPointer" inQuery:query2];
Any suggestions?
I just searched this exact same problem and there are a number of questions on the Parse forum regarding it:
Trouble with nested query using object_id
Assistance with relational query
Compare string to pointer in query with does not match key in query
The first one explains a hack to include an extra field (in this case) in your ObjectRelations class. This key/field would be a string that would be the objectId of the pointer. It would be in addition to the key/field that holds the pointer.
If you look through the questions you can see that as of now there doesn't seem to be an answer directly from Parse regarding this.
Instead of adding an additional column with type String to contain the objectID it points to, I would suggest to add a column on each object with type Pointer to point to the object itself. This would dramatically reduce the amount of the columns you have to add. The only downside is this must be done on Cloud Code.
At this question's scenario, you'll have two queries like:
//Inner query
//Library containing pointer<Deck> & pointer<User>
PFQuery * subscriptions = [PFQuery queryWithClassName:#"subscription"];
[subscriptions whereKey:#"User" equalTo:[PFUser currentUser]];
//Outer query
//Pull down a list of deckStore objects not included in the subscriptions for current user
PFQuery * decks = [PFQuery queryWithClassName:#"deckStore"];
Then instead of:
[decks whereKey:#"objectId" doesNotMatchKey:#"deckString" inQuery:subscriptions];
You can do this:
[decks whereKey:#"this" doesNotMatchKey:#"deck" inQuery:subscriptions];
Here's a sample of what the Cloud Code should be added:
Parse.Cloud.afterSave("Deck", function(request) {
var deck = request.object;
// To make sure this is the first time of "afterSave" of this object.
if (deck.createdAt.getTime() == deck.updatedAt.getTime()) {
// "this" is the column which contains the pointer of the object itself.
if (deck.get("this") == null) {
deck.set("this", deck);
deck.save();
}
}
}
I have the same problem. This is my solution:
PFQuery *query1 = [PFQuery queryWithClassName:#"tableClass"];
[query1 whereKey:#"objectId" equalTo:[(PFObject *)[object objectForKey:#"pointerField"] objectId]];
PFObject *obj1 = [query1 getObjectWithId:[(PFObject *)[object objectForKey:#"pointerField"] objectId]];
NSString *pointerName = [obj1 objectForKey:#"name"];

Resources