Search code examples
javascriptmongodbparse-platformparse-server

Create a complexe parse server or mongodb query


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 ?


Solution

  • 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);
      });
    });