Parse.com : how to join 2 tables? - ios

I'm a new user of Parse.com. I have a question about joining classes. Easy with SQL, but I don't find the solution with Parse. Thanks for your help !
I have 2 classes : ProductDatabase and MachineDatabase.
ProductDatabase contains 4 columns : serialNumber, name, description, price.
MachineDatabase contains 3 columns : serialNumber, date, idMachine.
I want to display the following datas : name, description, price, date.
- (PFQuery *)queryForTable
{
PFQuery *productQuery = [PFQuery queryWithClassName:#"ProductDatabase"];
PFQuery *query = [PFQuery queryWithClassName:#"MachineDatabase"];
[query whereKey:#"serialNumber" matchesKey:#"serialNumber" inQuery:productQuery];
[query includeKey:#"name"];
[query includeKey:#"description"];
[query includeKey:#"price"];
[query orderByDescending:#"date"];
return query;
}
// In my (UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
cell.nameLabel.text = [object objectForKey:#"name"];
cell.textLabel.text = [object objectForKey:#"description"];
cell.priceLabel.text = [object objectForKey:#"price"];
NSDate *productDate = [object objectForKey:#"date"];
NSString * dateString = [self timeSincePublished:productDate];
cell.dateLabel.text = dateString;
This code displays the date, but not the name, description, price.
Do you know how I can join these two classes ?
Thanks !

You are thinking it in an RDBS way and it is not ideal for a tool like Parse. You might need a little restructuring of your schema. Ideally, you should try using the Pointers in Parse (assuming your's is a one to many or one to one relation).
So your class structure will be like:
ProductDatabase: objectId, serialNumber, name, description, price, machine
MachineDatabase: objectId, serialNumber, date, idMachine
If serialNumber is not specific to Product and Machine, you can keep it in any one class. The field 'machine' in ProductDatabase should be an object pointer to the particular row in MachineDatabase.
PFQuery *query = [PFQuery queryWithClassName:#"ProductDatabase"];
[query includeKey:#"machine"];
includeKey keyword will expand the relational field machine and add the fields for the respective row in MachineDatabase into the result.
[query findObjectsInBackgroundWithBlock:^(NSArray *products, NSError *error) {
for (PFObject *product in products) {
// Machine row will be an object inside the retrieved product row.
PFObject *machine = product[#"machine"];
NSLog(#"retrieved related machine: %#", machine);
}
}];

Related

PFQuery with multiple constraints on one key

i have parse.com database. i have column in a table named "text".
i have to find text with multiple keys matching with AND condition.
i tried it all ways. i tried to use:
PFQuery *query=[PFQuery queryWithClassName:#"post"];
[query whereKey:#"text" containedIn:Array];
or
PFQuery *query=[PFQuery queryWithClassName:#"post"];
[query whereKey:#"text" containsAllObjectsInArray:Array];
or
PFQuery *query=[PFQuery queryWithClassName:#"post"];
for (NSString *str in filtersArray) {
[query whereKey:#"text" containsString:str];
}
but no one works. please guide me if parse sdks supports this or not totally? if yes how can i achieve the results.
many thanks in advance :)
EIDT:
for example i have three entries in database as text:
"i have a data entry"
"i can not find something in data"
"how can i do this"
if pass "i" and "this" it should return entry (3)
if pass "i" and "data" it should return entry (1,2)
if pass "i" and "else" it should return nothing
The reason your query doesn't work is because Parse does not support having the same constraint (in this case 'containsString:') more than once on the same key.
So, what I would suggest is to query for a regular expression which will match all of your filter strings using - (instancetype)whereKey:(NSString *)key matchesRegex:(NSString *)regex.
NSArray *qryStrings = #[str1, str2, str3, ....]; //strings you are trying to match
//string which will hold our regular expression
NSString *regexString = #"^"; //start off the regex
for (NSString *str in qryStrings) { //build the regex one filter at a time
regexString = [NSString stringWithFormat:#"%#(?=.*%#)", regexString, str];
}
regexString = [NSString stringWithFormat:#"%#.*$", regexString]; //finish off the regex
PFQuery *query = [PFQuery queryWithClassName:#"post"];
[query whereKey:#"text" matchesRegex:regexString];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
//objects array contains matching rows with matching strings
}
else {
NSLog(#"%# %#", error, [error userInfo]);
}
}];
Depending on the speed requirements of your query and the number of objects being queried, I would try separating the results using componentsSeparatedByString: after enumerating through the results array using something like: for(NSString *string in results). After separating each string into an individual array, use an if(...) statement that determines if that collection of words contains each word searched.
Code example:
NSMutableArray *searchResults = [[NSMutableArray alloc] init];
PFQuery *query=[PFQuery queryWithClassName:#"post"];
// consider setting a limit, depending on the number of posts
// consider sorting the query as needed
NSArray *posts = [query findObjects];
for(NSString *text in posts)
{
NSArray *words = [text componentsSeparatedByString:#" "]; // use SeperatedByCharactersInSet: for . , or anything else you'd need
switch ([filterArray count])
{
case 1:
if([words containsObject:filterArray[0]])
{
[searchResults addObject:text];
}
break;
case 2:
if([words containsObject:filterArray[0]] &&
[words containsObject:filterArray[1]])
{
[searchResults addObject:text];
}
break;
// case n:
// if([words containsObject:filterArray[0]] &&
// [words containsObject:filterArray[1]] &&
// [words containsObject:filterArray[n]])
// {
// [searchResults addObject:text];
// }
//
// break;
default:
break;
}
_filteredResults = [NSArray arrayWithArray:searchResults]; // local global instance of results - use for maintaining order (be sure to clear it between searches)
The _filteredResults array should be what you're wanting. Be sure to consider speed and the character separators used in the search though.
You simply just need to use whereKey:containsString:
And if you want to use additional parameters, you just simply add a subQuery
Your solution need to modify a bit.
Like this.
PFQuery *query=[PFQuery queryWithClassName:#"post"];
[query whereKey:#"text" containedIn:Array];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
//what your logic you want to write there might comes some records that are counts two times so you can remove those using NSSet
}];

Parse.com querying all users in a tableview

How would I go about displaying a list of all users in the parse.com database in a tableview and then when they click on each table, display that particular user's information.
All I know is that in order to query the users I must use:
PFQuery *query = [PFUser query];
Thank you in advance and any help is much appreciated.
You're really asking a specific question about Parse queries, but it seems like you don't understand queries in general, so just start with a general query against Parse:
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// Something went wrong
} else {
// objects is an array of PFObject containing your results
}
}];
As you figured out, you can also do a user query by making that first line:
PFQuery *query = [PFUser query];
The rest is the same.
Lyndsey Scott is right, this is basic stuff in the docs. I'm posting this here because of the one "gotcha" which is the class name (_User instead of User) if you use the first method.
What you generally will do is call [myTableView reloadData] inside the success block since you now have an array of users. In your didSelectCellAtIndexPath: perform a seque with a new viewcontroller, and in your prepareForSegue method, pass the user object to your pushed view controller so it knows what user to show.
I assume you know how to use table view. So I'll implement it this way:
1.create a property
#property (nonatomic) NSArray *users;
2.In viewDidAppear execute the query:
PFQuery *query = [PFUser query];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.users = objects;
[self.tableView reloadData];
}
}];
3.You need to implement table view code, the most important is cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"userCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
PFUser *user = self.users[indexPath.row];
cell.textLabel.text = user.username;
return cell;
}
Beside that you also need all of the required table view's data source code to tell it the number of cell. If you'll need some help with that just tell.

Set PFQuery order

I want to make my PFQuery come in a random order, so the next time I'm creating the same PFQuery with limit it won't return the same objects as the first one.
PFQuery *query = [PFUser query];
[query orderBy...]; //Is there a randomOrder method?
//Or a workaround to get random order?
[query setLimit:10];
I need this to be in a random order every time, or else the PFQuery will contain the same 10 objects everytime
You can't change the ordering of data returned in the query, but you can use paging to change the first object that is returned - so you could do something like this (it is based on the ToDo sample code from Parse but it will work for any object) -
PFQuery *query =[PFQuery queryWithClassName:#"Todo"];
NSInteger count=[query countObjects];
NSInteger skip = arc4random_uniform(count-10);
query.skip=skip;
query.limit=10;
NSArray *results=[query findObjects];
NSLog(#"object count=%d",results.count);
for (PFObject *object in results) {
NSLog(#"text=%#",object[#"text"]);
}
You can now retrieve your 10 objects. for any given skip count they will be in the same order, but you could randomise the order after you retrieved the 10 items. Simply put them into an NSMutableArray and use technique in this answer - Re-arrange NSArray/MSMutableArray in random order
Note that this code isn't optimal as it doesn't perform the fetch tasks on the background thread. To use background threads you would use something like the following -
PFQuery *query =[PFQuery queryWithClassName:#"Todo"];
[query countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
query.skip=arc4random_uniform(number-10);;
query.limit=10;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"An error occurred - %#",error.localizedDescription);
}
else {
NSLog(#"object count=%d",objects.count);
for (PFObject *object in objects) {
NSLog(#"text=%#",object[#"text"]);
}
}
}];
}];
PFQuery does not support random order but you can workaround this by creating an increasing index field to each object
Then given that you know the maxIndexin the table you can generate random indices as the following:
- (NSArray *)generateRandomIndices:(int)maxIndex limit:(int)limit {
NSMutableArray *indices = [[NSMutableArray alloc] initWithCapacity:limit];
for (int i=0; i<limit; i++) {
int randomIndex = arc4random() % maxIndex;
[indices addObject:[NSNumber numberWithInt:randomIndex]];
}
return indices;
}
Now you can query your class by using INpredicate
NSArray *randomIndices = [self generateRandomIndices:maxIndex limit:10];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"index IN %#", randomIndices];
PFQuery *query = [PFQuery queryWithClassName:#"className" predicate:predicate];
PFQuery don't give random objects. You can get all objects then randomize to get any 10 objects from it and show it.

Query Custom User Class Parse

I'm trying to query three columns that I created in my user class with parse. I successfully saved them to the user class. But I'm having difficulty query these three columns onto a label. I really don't know where to start with the query, I want it to be user specific of course, so user A gets his name, bio, gender, ect. And name is the user's full name, not their username.
This is my code to save the objects:
- (IBAction)save:(id)sender {
PFUser *profile = [PFUser currentUser];
[profile setObject:_name.text forKey:#"Name"];
[profile setObject:_bio.text forKey:#"Bio"];
NSString *var = [[NSUserDefaults standardUserDefaults] objectForKey:#"MyKey"];
[profile setObject:var forKey:#"Gender"];
[profile saveInBackground];
}
For the query I really don't know, this is where I need help. So if you have any suggestions I would appreciate it. Thank you!
Try this one
PFQuery * query = [PFQuery queryWithClassName:#"ClassName"];
[query whereKey:#"Name" equalTo:#"XYZ"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// objects : PFObjects
// you can filter out the data you want and print into lable
}];

Remove an item from a column type of array in Parse (iOS SDK)

I have table name "Events". In that table I have a column of type array of string. I'm struggling with how I can delete only one element from that column. Consider the image below, I want to delete all the occurrences of "iYYeR2a2rU" from the "usersIncluded" column, without deleting the rows.
I've used the removeObject:(id) forKey:(NSString *) and it didn't work.
This is how I'm trying to achieve it:
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
NSArray *eventObjects = [query findObjects];
[query whereKey:#"usersIncluded" equalTo:[self.uniqeFriendList objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
for (int i = 0; i<objects.count; i++) {
PFObject *event = [eventObjects objectAtIndex:i];
[event removeObject:[self.uniqeFriendList objectAtIndex:indexPath.row] forKey:#"usersIncluded"];
}
}];
}
The self.uniqeFriendList is a mutable array containing the ids that I want to delete from the 'usersIncluded' column.
Thanks in Advance
I think you're using the right method (removeObject:forKey: should do exactly what you want) but I think you're working with objects from the wrong array. You're performing your query twice, and within the findObjectsInBackgroundWithBlock: you're working with the array from the first time you called it... Try this:
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
[query whereKey:#"usersIncluded" equalTo:[self.uniqeFriendList objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
for (int i = 0; i <objects.count; i++) {
PFObject *event = [objects objectAtIndex:i]; // note using 'objects', not 'eventObjects'
[event removeObject:[self.uniqeFriendList objectAtIndex:indexPath.row] forKey:#"usersIncluded"];
}
[PFObject saveAll:objects];
}];
}

Resources