0

I'm using parse server + mongodb for back-end and I need help for creating an complexe query:

First here's a schema for my db : enter image description here

The friends model represent the friendship between two users. Each user has a city

What I'm trying to achieve is to create function that take an userId as input and return list of cities Sorted by number of friends.

Here's my code until now

function getCities(userId) {

  //This query allow to count number of friends in a given city 
  var innerQuery1 = new Parse.Query(Friends);
  innerQuery1. equalTo("user1", userId);

  var innerQuery2 = new Parse.Query(Friends);
  innerQuery2.equalTo("user2", userId);

  countFriendsQueryInCity = Parse.Query.or(innerQuery1, innerQuery2);
  countFriendsQueryInCity.equalTo("city", cityId)
  countFriendsQueryInCity.count({..})

  //This to get all cities
  var query = new Parse.Query(City);
  query.find({})

}

So the probleme is I can't figure a way way in parse or mongodb to join the two queries ?

1 Answer 1

2

Parse doesn't support aggregation queries at this time. Here's an example of how you can do this using the js sdk api. In case you're curious, to make sure that this worked, in my checked out version of the parse-server repo, I created a spec file in the spec directory with all of the below in it then i focused on just the test (by putting an 'f' in front of 'describe').

/**
 * Return a sorted list of cities with count of friends in that city
 *
 * @param userId id of the user to build the list for
 * @returns an array of city, count pairs sorted descending
 */
const getCities = function getCities(userId) {
  const aggregation = {};
  const userPointer = new Parse.Object('Person').set('objectId', userId);
  return new Parse.Query('Friend')
    .equalTo('user1', userPointer)
    .include('user2.city')
    .each((friendship) => {
      const city = friendship.get('user2').get('city').get('name');
      if (aggregation[city]) {
        aggregation[city]++
      }
      else {
        aggregation[city] = 1;
      }
    })
    .then(() => {
      const sortable = [];
      for (const city in aggregation) {
        sortable.push([city, aggregation[city]]);
      }
      return sortable.sort((a, b) => b[1] - a[1]); // desc
    });
}


// the unit test for the function above....
fdescribe('play with aggregations', () => {
  it('should count friends by city and order desc', (done) => {

    // create cities
    const ny = new Parse.Object('City').set('name', 'ny');
    const sf = new Parse.Object('City').set('name', 'sf');

    // create some people to befriend
    const people = [
      new Parse.Object('Person').set('city', ny),
      new Parse.Object('Person').set('city', sf),
      new Parse.Object('Person').set('city', sf),
      new Parse.Object('Person').set('city', sf),
    ];

    // the object of these friendships
    const friendee = new Parse.Object('Person').set('city', sf);

    // make the friendships
    const friends = people.map(person =>
      new Parse.Object('Friend')
        .set('user1', friendee)
        .set('user2', person));

    // just saving the friends will save the friendee and cities too!
    Parse.Object.saveAll(friends)
      // all saved, now call our function
      .then(() => getCities(friendee.id))
      .then((result) => {
        const lastResult = result.pop();
        const firstResult = result.pop();
        expect(lastResult[0]).toBe('ny');
        expect(lastResult[1]).toBe(1);
        expect(firstResult[0]).toBe('sf');
        expect(firstResult[1]).toBe(3);
        done();
      })
      .catch(done.fail);
  });
});
Sign up to request clarification or add additional context in comments.

10 Comments

Thank you! So you're getting list of all user friends then loop the friends list and make the count of city, that it ?? Also what If the user have thousands of friends it will be too complicated because parse can't handle more then 1000 item per page ?
Parse.Query.each() doesn't have a limit. It'll run through all of them. The issue would be if there are hundreds of thousands or millions, it could timeout the connection. the alternate would be to make a 'beforeSave' hook on the 'Friend' class and store the aggregation at save instead of looking it up when needed. So you could have something like a FriendCityCount class that has userid, city, count as the columns.....
I thought on that but this add another table to handle, other functions ... It's wired why NoSql doesn't support join query
Mongo dpes support aggregations. They just aren't exposed by parse yet. One of the parse server contributors is keen to add them though.
The solution works but it show only the city where I've friends, city where I don't have friends are not listed) .
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.