title: Backbone.js model relations: speeding up performance
body: <p>I have three associated models: <em>Segments</em> each have two <em>GeoPoints</em>, with associations stored as <em>GeoPointOnSegments</em>. On the server side this is straightforward Rails code, with a has_many :through relationship. On the client side, the following is the Backbone code I have written. (A note: I allow for client-side creation of data and then batch upload; that's why there is some logic to deal with both local and server ids.)</p>
  
  <p>The problem I am finding is that this code is very slow when I have hundreds of models. Specifically it's the following line in geo_point.coffee:</p>
  
  <pre><code>masterRouter.geo_point_on_segments.select ...
  </code></pre>
  
  <p>That's doing a filter on every single GeoPointOnEntry model in order to find which ones are connected to a given GeoPoint. (Note that there can be more than one Segment connected to a GeoPoint.) Any suggestions for how to improve performance?</p>
  
  <p>Ideas I have had:</p>
  
  <ul>
  <li>Make use of functionality provided by <a href="https://github.com/PaulUithol/Backbone-relational" rel="nofollow">https://github.com/PaulUithol/Backbone-relational</a> assuming that has something to offer</li>
  <li>On the server-side, produce nested JSON, perhaps with some redundancy.</li>
  <li>Create a client-side index of sorts with a JavaScript hash.</li>
  </ul>
  
  <p>Have any other, better ideas?</p>
  
  <p><strong>geo_point.coffee</strong></p>
  
  <pre><code>class App.Models.GeoPoint extends Backbone.Model
    name: 'geo_point'
    getGeoPointOnSegments: -&gt;
      masterRouter.geo_point_on_segments.select (gpos) =&gt;
        if @isNew()
          return gpos.get('geo_point_cid') == @cid
        else 
          return gpos.get('geo_point_id') == @id
      , this
    getSegments: -&gt;
      _.compact _.map @getGeoPointOnSegments(), (gpos) =&gt;
        gpos.getSegment() unless gpos.get('markedForDelete')
    getConnectedGeoPoints: -&gt;
      _.compact _.flatten _.map @getSegments(), (s) =&gt;
        s.getGeoPoints() unless s.get('markedForDelete')
  </code></pre>
  
  <p><strong>geo_point_on_segment.coffee</strong></p>
  
  <pre><code>class App.Models.GeoPointOnSegment extends Backbone.Model
    name: 'geo_point_on_segment'
    getGeoPoint: -&gt;
      if local = masterRouter.geo_points.getByCid(@get 'geo_point_cid')
        return local
      else if server = masterRouter.geo_points.get(@get 'geo_point_id')
        return server
    getSegment: -&gt;
      if local = masterRouter.segments.getByCid(@get 'segment_cid')
        return local
      else if server = masterRouter.segments.get(@get 'segment_id')
        return server
  </code></pre>
  
  <p><strong>segment.coffee</strong></p>
  
  <pre><code>class App.Models.Segment extends Backbone.Model
    name: 'segment'
    getGeoPointOnSegments: -&gt;
      _.compact masterRouter.geo_point_on_segments.select (gpos) =&gt;
        if gpos.isNew()
          return gpos.get('segment_cid') == @cid
        else if gpos.get('markedForDelete')
          return null
        else
          return gpos.get('segment_id') == @id
      , this
    getGeoPoints: -&gt;
      _.map @getGeoPointOnSegments(), (gpos) =&gt;
        gpos.getGeoPoint() unless gpos.get('markedForDelete')
  </code></pre>