Kill fewer hasMany requests and owned by Lookups

I have the following three models:

ConversationApp.Post = DS.Model.extend(
  body: DS.attr()
  replies: DS.hasMany('reply', async: true)
  author: DS.belongsTo('user', async: true)
  likedBy: DS.hasMany('user', async: true)
)

ConversationApp.Reply = DS.Model.extend(
  body: DS.attr()
  post: DS.belongsTo('post')
  author: DS.belongsTo('user', async: true)
  likedBy: DS.hasMany('user', async: true)
)

ConversationApp.User = DS.Model.extend(
  firstName: DS.attr()
  lastName: DS.attr()
)

And my pointer route makes this call:

ConversationApp.IndexRoute = Em.Route.extend(
  model: (args) ->
    @store.find('post', page: 1) # => /api/v1/posts?page=1
)

After this call, Ember begins to receive all the users necessary for the first page - a total of 17 (!) Different requests for users on the first page (with 10 messages). Here are 3 examples of requests that Ember makes to the server:

  • / Api / v1 / users / 11375
  • / Api / v1 / users / 4383
  • / API / v1 / users, identifiers [] = 34588 &? Identifiers [] = 7442 & Identifiers [] = 10294

I would like Ember to only make one request that asks all the necessary users for the first page:

  • /API/v1/, [] = 34588 &? [] = 7442 & [] = 10294 & [] = 11375 & [] = 4383

:

{{#each}}
  {{author.firstName}}
  {{#each likedBy}}
    [... removed for brevity ...]
  {{/each}}

  {{#each replies}}
    {{author.firstName}}
    {{#each likedBy}}
      [... removed for brevity ...]
    {{/each}}
  {{/each}}
{{/each}}

?

+4
3

, , DS.RESTAdapter.

. , , coalesceFindRequests true, :

App.ApplicationAdapter = DS.RESTAdapter.extend({
    coalesceFindRequests: true
});

Ember CLI

// app/adapters/application.js
import DS from 'ember-data';

export default DS.RESTAdapter.extend({
    coalesceFindRequests: true
});

DS.RESTAdapter api docs

!:)

+9

, . , , Ember-Data ​​ . , , . . , , , . , (50 ) . , . , , , , .

, , .

App.UserAdapter = DS.RESTAdapter.extend({
    _findMany: null,

    find: function(store, type, id) {
        return this.findMany(store, type, [id]);
    },

    findMany: function(store, type, ids) {
        this._findMany = this._super;

        // Create a promise, but keep the resolve function so we can call it later
        var resolve;
        var promise = new Ember.RSVP.Promise(function(r) {
            resolve = r;
        });

        // Let our debouncer know about this request
        this.concatenateRequest(store, ids, resolve);

        // Return a promise as usual
        return promise;
    },

    /**
     * The number of milliseconds after a request to wait before sending it.
     * Tweak this as necessary for performance.
     */
    debounceTimeout: 50,

    concatenateRequest: (function() {
        // All of the IDs currently requested in the pool
        var allIds = new Em.Set();
        // The pool of promises that is currently awaiting fulfillment
        var allPromises = [];

        // The ID of the last `setTimeout` call
        var timeout = null;

        // Takes the specified users out of the payload
        // We do this to break the giant payload into the small ones that were requested
        var extractUsers = function(payload, ids) {
            // Filter out the users that weren't requested
            // Note: assuming payload = { users: [], linked: {}, meta: {} }
            var users = payload.users.filter(function(user) {
                return (ids.indexOf(user.id.toString()) >= 0);
            });

            // Return payload in form that store is expecting
            return { users: users };
        };

        return function(store, ids, resolve) {
            // clear the timeout (if it already cleared, no effect)
            clearTimeout(timeout);

            // Add the current promise to the list of promises to resolve
            allIds.addObjects(ids);
            allPromises.push({ ids: ids, resolve: resolve });

            // Set our timeout function up in case another request doesn't come in time
            timeout = setTimeout(function() {
                // Get the IDs and promises store so far so we can resolve them
                var ids = allIds.toArray();
                var promises = allPromises;

                // Clear these so the next request doesn't resolve the same promises twice
                allIds = new Em.Set();
                allPromises = [];

                // Send off for the users we know need
                this._findMany(store, ConversationApp.User, ids).then(function(payload) {                   
                    // Resolve each promise individually
                    promises.forEach(function(promise) {
                        // extract the correct users from the payload
                        var users = extractUsers(payload, promise.ids);
                        // resolve the promise with the users it requested
                        promise.resolve(users);
                    });
                });
            }.bind(this), this.get('debounceTimeout'));
        };
    })()
});

EDIT: JSBin unit test , , . , , .

+1

Ember Data, , Ember. JSON API, .

Ember Data JSON API json. .

( ), /api/v1/posts, , json :

{
  "posts": [{
    "id": "1",
    "body": "post body 1",
    "links": {
      "replies": ["1", "2"]
    }
  }, {
    "id": "2",
    "body": "post body 2",
    "links": {
      "replies": ["3"]
    }
  }],
  "linked": {
    "replies": [{
      "id": "1",
      "body": "reply body 1"
    }, {
      "id": "2",
      "body": "reply body 2"
    }, {
      "id": "3",
      "body": "reply body 3"
    }]
  }
}

, . Ember Data "" .

Rails ActiveModelSerializer, - , . Ember Data .

JSON API Ember Data . , , Ember Data JSON API. , Ember Data . .

0

Source: https://habr.com/ru/post/1527598/


All Articles