0
votes

after struggling many days I managed to retrieve the results I want from a Parse Cloud Query.

My problem is that I have two queries combouned to one, so there is need to use diffurent column to display.

I am using Ionic3/Angular 4 with Parse Server.

Here is my Parse Cloud Code

    Parse.Cloud.define('FriendsQuery', function (req, res) {
  const fromUserQ = new Parse.Query("Friends")
    .equalTo("toUser", req.user)
    .include('fromUser')
    .find();

  const toUserQ = new Parse.Query("Friends")
    .equalTo("fromUser", req.user)
    .include('toUser')
    .find();

  Parse.Promise.when(toUserQ, fromUserQ)
    .then((toUsers, fromUsers) =>
      res.success({
        toUsers: toUsers.map(e => e.get('toUser')),
        fromUsers: fromUsers.map(e => e.get('fromUser')),
      }))
    .catch(res.error);
});

meets.ts

import { Data } from './../../services/data';
import { localData } from './../../services/local';
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Parse } from 'parse';
import 'rxjs/add/operator/map';
import {User} from '../../models/User';
import 'rxjs/add/operator/debounceTime';
import {FormControl} from '@angular/forms';

@Component({
  selector: 'page-meet',
  templateUrl: 'meet.html'
})
export class MeetPage {

  currentUser = Parse.User.current();
  query = new Parse.Query(Parse.User);
  tmp =[];
  tmp2 =[];
  friends: any=[];

  initializeItems() {
  this.friends = this.localdata.tmpfriends;
  }

  retrieveFriends(){
   if (this.currentUser= Parse.User.current()) {
     console.log(this.currentUser.id)
     Parse.Cloud.run('FriendsQuery',{currentuser: this.currentUser.id}).then(
     res => {       
       console.log(res);
       this.tmp2 = res;
       this.localdata.setFriends(this.tmp2);
       this.friends = this.localdata.tmpfriends;
       console.log(this.friends);
       });            
   }
   }

  constructor(public navCtrl: NavController, private localdata: localData, private dataService: Data) {
    this.searchControl = new FormControl;
}

showFriends: any = false;
searchControl: FormControl;
searchTerm: string = '';
searching: any = false;

filterfriends(searchTerm){ 
       return this.friends.filter((friend) => {
           return friend.fromUser.username.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
       });    
    }

ionViewDidLoad() {
  this.retrieveFriends();
  this.setFilteredItems();
  this.searchControl.valueChanges.debounceTime(700).subscribe(search => {
    this.searching = false;
    this.setFilteredItems();
     });
}

onSearchInput(){
  this.searching = true;
  }

setFilteredItems() {
  this.initializeItems();
  this.friends = this.filterfriends(this.searchTerm);
}  

onCancel(ev: any) {
  this.initializeItems();
}
}

meet.html

  <ion-header>
  <ion-navbar>
    <ion-searchbar [(ngModel)]="searchTerm" [formControl]="searchControl" (ionCancel)="onCancel($event)" (ionInput)="onSearchInput()">
  </ion-searchbar>
  </ion-navbar>
</ion-header>

<ion-content padding>
    <div *ngIf="searching" class="spinner-container">
        <ion-spinner></ion-spinner>
    </div>
       <ion-item class="item-avatar item-avatar-left item-button-right common-list" >
            <ion-avatar *ngIf="friend.fromUser.objectId == currentUser.id" item-left><img style="left:0px !important;margin-top:0px !important;" [src]="friend.toUser.avatar  || '/assets/img/no-avatar.png'"/></ion-avatar>
            <ion-label *ngIf="friend.fromUser.objectId == currentUser.id">{{friend.toUser.username}}</ion-label>
            <ion-avatar *ngIf="friend.toUser.objectId == currentUser.id" item-left><img style="left:0px !important;margin-top:0px !important;" [src]="friend.fromUser.avatar  || '/assets/img/no-avatar.png'"/></ion-avatar>
            <ion-label *ngIf="friend.toUser.objectId == currentUser.id">{{friend.fromUser.username}}</ion-label>
            </ion-item>
</ion-content>

local.ts

import { Component } from '@angular/core';

export class localData {

    setFriends (friends){
        window.localStorage.friends_data = JSON.stringify(friends);
    }
    getFriends(){
       return JSON.parse(window.localStorage.friends_data || '[]');
    }

    tmpfriends = this.getFriends();
   }    

In my table "Friends" I have two columns, "fromUser" and "toUser" and I want to display the friends of a current logged in user. So in Angular code i need both friend.toUser.username and friend.fromUser.username depending the logged in user.

When I use the first one I get only the results for the users that sent a friend request to the logged in user and the opposite.

I attach an image link to be more clear. I don`t want the results in the screen but the ones that I point with the arrows.

With the hmtl code I managed to get this results but it is not convinienent to filter them with this one:

filterfriends(searchTerm){ 
return this.friends.filter((friend) => {
return friend.fromUser.username.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
});    
 }

Because I need to filter both friend.toUser and friend.fromUser

screenshot

Thank you

2
uh-oh: if (this.currentUser= Parse.User.current()) is almost certainly not doing what you think it is. Try this instead: if(this.currentUser.id === Parse.User.current().id) - Arthur Cinader
OOOOH, i think I understand what you want!!! will update my answer, let's see.... - Arthur Cinader
Hi Arthur, yes there is no wrong with the one you mention above, but how I will display the results. The best option for me would be to to check in Cloud Code if the user is the fromUser or toUser in order to retrieve a list that finally could be used in hmtl as friend.username.I am waiting for your response!thank you again! - giorgionasis

2 Answers

2
votes

You don't need the whole { "__type": "Pointer", "className": "_User", "objectId": req.params.currentuser } if toUser and fromUser are already user pointers, you can just do .equalTo("toUser", request.params.currentUser);. Additionally, if you're making this request from a client that is logged in, you can just do .equalTo("toUser", request.user);

There is also a query method, .include(<key>); that will include the pointed to object. So, you could do friendQuery.include("toUser").include("fromUser"); and have that information fetched. And, this works with dot notation, if you have some sort of additional pointer on your users, say, a ContactInfo class. You could do query.include("toUser.contactInfo");, and that object will be included as well.

Beyond this I'm not entirely sure what the issue you're having is. What isn't working that you want to work? It sounds like you're getting the information you wanted.

1
votes

So, if I understand the question, what you want is:

  1. to get back a single array of friends because you don't really care which is a fromUser and which is a toUser, you just want all the friends.

  2. you want this list 'de-duped' since you may have friended and have been friended by the same user, and you don't want that in the list twice.

  3. you 'may' want to filter the friends too.

It makes a lot of sense to do this in a single cloud function.

The string matching is a bit tricky. We could use a regular expression and the matches query function, but since we're matching on the user table and not on the friends table, it's tricky. For simplicity, I just filter the results in javascript instead.

For the de-dupe, I use the lodash lib.

const fp = require('lodash/fp');

Parse.Cloud.define('FriendsQuery', function (req, res) {
  const fromUserQ = new Parse.Query('Friends')
    .equalTo('toUser', req.user)
    .include('fromUser')
    .find();

  const toUserQ = new Parse.Query('Friends')
    .equalTo('fromUser', req.user)
    .include('toUser')
    .find();

  return Parse.Promise.when(toUserQ, fromUserQ)
    .then((toUsers, fromUsers) => {
     const friendsWithDupes = toUsers.map(e => e.get('toUser'))
        .concat(fromUsers.map(e => e.get('fromUser')));
      // fp.uniquWith(comparator)(arrayToDeDupe);
      const friends = fp.uniqWith((x, y) => x.id === y.id)(friendsWithDupes);

      if (req.params.searchTerm) {
        const lowerSearchTerm = req.params.searchTerm.toLowerCase();
        const filteredFriends = friends.filter(
          friend => friend.get('username').toLowerCase().indexOf(lowerSearchTerm) > -1
        );
        return res.success(filteredFriends);
      }

      return res.success(friends);
    })
    .catch(e => res.error(JSON.stringify(e)));
});

You can call this function with or with out a search term:

Parse.Cloud.run('FriendsQuery', { searchTerm: 'bob' })

or

Parse.Cloud.run('FriendsQuery')