Stamplay / stamplay-js-sdk

:rocket: JavaScript SDK of Stamplay cloud platform
https://stamplay.com
MIT License
88 stars 18 forks source link

Making the "or" method of the Query entity accepting an array as argument #11

Closed chiefGui closed 9 years ago

chiefGui commented 9 years ago

Hey guys!

The purpose of this PR is giving to the .or() method of the Query entity the capability to accept an array as the first argument.

But, why?

Let's suppose you have a custom object called posts and want to retrieve their owner's data—how do you do that?

Given the following scenario:

let posts = new Stamplay.Cobject('posts').Collection

posts
  .fetch()
  .then(() => {
    // posts are filled here.
  })

You have two options:

  1. Instead of using the Stamplay's User Model, you can create a custom one; then, through fetch(), just { populate: true }.
  2. Iterate over every post and get its owner's/author's/user's data. Something like this:
let posts = new Stamplay.Cobject('posts').Collection

posts
  .fetch()
  .then(() => {
    posts.each((post) => {
      let user = new Stamplay.User().Model

      user
        .fetch(post.get('id'))
        .then(() => {
          post.set('owner', user)
        })
    })
  })

But, what is the problem of this solution? Basically, iterating over each post will result in a new request to getting its author's data. Having 10 posts, 10 pointless requests will be made just to retrieve a few amount of data.

image

See? 3.5 seconds at localhost to retrieve 6 authors. Can you imagine how this could become a riot just by retrieving more results? Also, it can be expensive for the company and for your clients, since the plans are mainly based in requests number.

Finally, the solution I'm proposing here is enabling the injection of an array as an .or() method argument. Actually, its very first—and unique—argument.

Talking to Claudio, he reached this syntax:

var a = new Stamplay.Query('user').equalTo('_id','55b14125c757585053cee8c4');
var b = new Stamplay.Query('user').equalTo('_id','55b14131c757585053cee8c5');
var query = new Stamplay.Query('user').or(a, b);
query.exec().then(function(response){
  console.log(response);
}) 

But this way we were passing explicitly the ids of the users—in the end, it won't be useful enough because the data I want to retrieve is dynamic; recursive. By then, him and me were mining a little more a polished solution and we get almost there:

let posts = new Stamplay.Cobject('posts').Collection

posts
  .fetch()
  .then(() => {
    let query
    posts.each((post) => {
      query = new Stamplay.Query('user').equalTo('id', post.get('owner'))
    })
  })

Now the challenge was: "How to pass recursively the query as different properties to the .or() method?"

Basically, the answer is injecting recursively the representation of the objects—their variable names—as properties, something like this. Yes, it would work, but the complexity doesn't worth.

So, that's why I created this PR: using this 1.2.2 version I'm proposing, this became possible:

let posts = new Stamplay.Cobject('posts').Collection

posts
  .fetch()
  .then(() => {
    let queries = []

    posts.each((post) => {
      queries.push(new Stamplay.Query('user').equalTo('id', post.get('owner')))
    })

    let query = new Stamplay.Query('user').or(queries)
    query.exec().then((users) => {
      posts.each((post) => {
        post.set('owner', _.findWhere(users, { '_id': post.get('owner') }))
      }

      console.log(posts.at(0).get('displayName')) // will work!
    }
  })

Also, instead of having one request for each post, you'll have just one more request. After receiving the response, all you need is to set the way you want the users accordingly to their posts. In my case, I used underscore.

This is the result:

image

Two requests only:

  1. Requesting the posts (in screenshot's case, testimonals);
  2. Requesting their authors.

Programatically speaking, this is not a technical workaround. Actually, just an "hipster" way to solve a problem. While the { populate: true } for the User object doesn't come, I think this is a good way to go—logical and not hard to implement.

If you wanna test before merge and without hassle, just link this file to a <script> tag instead of using the conventional version of the Stamplay SDK.

Finally, I want to highlight that this is totally backward compatible. I didn't touch the current code—as you can see at https://github.com/Stamplay/stamplay-js-sdk/commit/9b2b18733c3961077741fffe0a16285f58d5c81d —, and that's why this update is launching like as a patch instead of a major or minor version: no need for this.

chiefGui commented 9 years ago

Guys, seems like Travis is having some difficulty to set up the SDK in their environment. I really don't want to touch this because I am not sure if there is an appropriate approach to make it work since this is a commercial tool.

May I ask you to take a look whether my PR get accepted?