jsonapi-utils icon indicating copy to clipboard operation
jsonapi-utils copied to clipboard

Non-ActiveRecord paginator support

Open gaorlov opened this issue 7 years ago • 3 comments

First and foremost: thanks so much for putting this gem out here!

Problem Statement

I have a service that uses Elasticsearch instead of ActiveRecord and I would like to return the result set counts. Because ActiveRecord::Relation is referenced directly in the counting method, the app explodes. Under the current implementation, my options are to:

  • monkeypatch Pagination.count_records in every Elasticsearch-backed service I write
  • do the counting in the controller and pass it into jsonapi_render via options[:count]

Option one is really a strawman, and it felt odd to sprinkle options: { count: MyEsModel.count( params ) } in every single controller action across multiple controllers in several projects.

Proposed Contribution

I extracted the underlying counting interface in to a RecordCounter module that responds to count( records, params = {}, options ={} ) and loops through dedicated Counter classes that know how to count a specific class type. So like, ActiveRecord::Relation objects go to the ActiveRecordCounter and Arrays go to the ArrayCounter class.

The #{Type}Counter classes register themselves with RecordCounter on declaration with counts #{Type.to_s.underscore} making adding new counters as easy as just declaring them in your project.

What this does

I will admit that this is a somewhat involved change, but there are several benefits to this added complexity. It:

  • eliminates the hard dependency on ActiveRecord::Relation in the counting mechanism
  • allows for easy extensions to counting, both in adding counting for other backend technologies, as well as modifying existing counting functionality without having to modify JSONAPI::Utils

Example

To use my use case, an Elasticsearch-backed system would implement a counter approximately like so:

class ElasticsearchCounter < JSONAPI::Utils::Support::Pagination::RecordCounter::BaseCounter
  counts "elasticsearch/persistence/repository/response/results"

  # the one thing we have to define
  def count
    # not the actual implementation 
    model_class = @records.first.class

    query = model_class.query_from_params @params

    model_class.count query
  end
end

gaorlov avatar Jun 04 '18 16:06 gaorlov

@tiagopog, I would love some feedback on this.

gaorlov avatar Jun 27 '18 20:06 gaorlov

@tiagopog bump?

gaorlov avatar Aug 01 '18 03:08 gaorlov

@tiagopog have you had a chance to look at this at all?

gaorlov avatar May 01 '19 20:05 gaorlov