Using combined models with pagy, getting undefined method 'offset' error - ruby-on-rails

I am using pagy. I combined two models into one, and I used pagy on that combined model. I am getting this error:
undefined method `offset' for #<Array:0x00007f886f88b3b0>
With the last line of the code below highlighted.
My code:
#problems = Problem.me_and_friends(current_user)
#activities = Activity.me_and_friends(current_user)
#combine = (#problems + #activities).sort{|a,b| a.created_at <=> b.created_at }
#pagy, #combined = pagy_countless(#combine, items:100, link_extra: 'class="" style="color:black; margin:3px;"')
It worked fine with using pagination on #problems alone.
I'd appreciate any help.

As soon as you call the (#problems + #activities), you transform the ActiveRecord::Relation into an array (which is also not good because you are loading all the database rows into memory, sorting and then paginating them). Pagy expects an ActiveRecord::Relation to work, hence the error.
You can consider multiple solutions,
Change your UI to show problems and activities in separate UIs, then you can paginate them separately
Update your models to store both problems and activities in the same table (maybe just a reference table which points to either a Problem or an Activity)
If either of these is not feasible, you can consider rolling out a custom solution for the pagination, but it will be tricky.
Update: June 21, 2021
If you are using Rails 6, it introduces the concept of Delegated Types which fits well into this scenario. The example given in the link mentions the issue of pagination across different tables.

Related

Performance difference when calling class methods in model vs controller in rails?

In my rails app I am fetching a batch of data from the DB with around a million records. I am simply calling the following query combined with some pagination logic, and right now it is working very well. The code is defined in my model, like so:
def find_records(current_page, max_records, start_value, end_value)
where(value_range: start_value..end_value)
.offset((current_page - 1) * max_records).limit(max_records)
end
However, in my previous attempt, I had the following code defined in my model:
def find_records(max_records, start_value, end_value)
where(value_range: start_value..end_value)
end
And I called .offset and .limit inside the controller like so:
def index
current_page = params[:page]
max_records = 3
start_value = 4
end_value = 8
Model.find_records(start_value, end_value).offset((current_page - 1) * max_records).limit(max_records)
end
When I did this, my memory completely gave up on the 3rd or 4th page and my app just crashed. I don't know why calling .limit and .offset in the model solved the issue.
So my question is, how does calling class methods in your model rather than the controller improve code execution performance? I mean this query is obviously data-related so it makes sense to call it inside the model anyways, but I would still like to know the wonders behind the magic.
Thank you!
how does calling class methods in your model rather than the controller improve code execution performance?
It should not. Both your queries return a ActiveRecord::Relation. Both offset and limit are used to build the query, so in both scenarios you should see the same query in your logs. Please check your development.log when in doubt.
Having the code query code in your model makes sense. The controller shouldn't know all those details.
About the pagination, there are a few solutions in the rails world - Kaminari, will_paginate

undefined method `paginate' for #<PG::Result:0x007f8f096a3390 (Converting arel result)

I have the following method:
#vulnerabilities = Vulnerability.details(filter_version_param,
filter: filter,
sort: order)
.paginate(page: page_param, per_page: 10)
The internals of this method uses arel to find the relevant information to display on a page. However when I run into the .page method from the will_paginate gem, I receive the following error:
`undefined method `paginate' for #<PG::Result:0x007f8f096a3390>`
This is due of course to the fact that the object returned is not a class from ActiveRecord. My question is whether there is a way to convert the PG class into an ActiveRecord model? A previous implementation on the application from another developer creates a Struct.new() class and feeds the values into it. I was wondering though if there was an easier way to do this.
You have two options:
for Kaminary gem as an example : convert the query result into an
array and use Kaminary.paginate_array(array_of_result).page(#pagenumber).per(#elemnts per page), but this way could have problems with large data tables since you need to load the hole table for example, then slice the page!
you can build an object that has the sufficient methods needed for the paginator to do its job.
for more explanation and information about the above two points, please chick this link.

Avoiding lazy loading in Datamapper in loop

I am new to Ruby on Rails and Datamapper. I have written models using Datamapper, one of my model name is Student. In one view haml file I have written the following code:
-students = Student.all
-students.each |student|
%tr
%td= student.roll_no
%td= student.type if student.type
%td= student.department.name
Here I have used newrelic -rpm for profiling my code. Here I found that in each iteration of the above block, one query of the form select prop1, prop2,... from students where id ="some value" is being generated. This is very undesired as it is taking time on each iteration of the block. I think it's due to lazy loading. I have spent nearly a week on that but found nothing to avoid this. If anyone have any idea regarding this please help me. Thank you.
It could be helpful if you could show us the schema of the Students table in app/db/schema.rb.
I suspect your problem is not that each iteration of student takes so long because of lazy evaluation of each row in students, but that it has to load the department in each step. The Student.all is, since Rails 4, lazily evaluated, but it loads the whole set at once.
In order to solve your issue, you have to write Student.includes(:department) in your first line.
You can force DataMapper to preload data before embarking on the loop.
After loading the Collection into students, add a line such as:
prefetch = students.collect { |s| s.department }
You don't have to do anything with the prefetch object; DataMapper will load all the data and associate it with the students object. You should then find that you can iterate through the loop as expected without a separate query been generated for each. (I've just tested this on a project I'm working on and it succeeds.)

Rails named scopes

I was trying to refactoring and optimizing me code. In particular, I wanted to reduce the amount of queries going to the database. In my users controller it worked very well but in an other controller, where I tried the same, it didn't. I've searched for some time now for the answer why it didn't work but I can't really answer it.
I've got users, which can subscribe to courses through enrolments. They are connected through has_many :through etc. relationships. The following works:
#users_courses = current_user.courses
#courses = #users_courses.a_named_scope
But in my courses controller the following wont work:
#all_courses = Course.all
#specific_course = #all_courses.specific_course_scope
The scopes are defined in the respective models and work properly. They are not complicated, just "where ... true/false" definitions. Does someone know the problem here? Thanks!
I'm using rails version 3.2 and ruby version 2.
Until Rails 4 you should use scoped method if you want to have ActiveRecord::Relation instance (on which you can call other scopes) returned instead of Array:
#all_courses = Course.scoped
#specific_course = #all_courses.specific_course_scoped
This should work.
If you want to use includes(:courses), you just do it, for example with:
#specific_course = #all_courses.specific_course_scoped.includes(:courses)

break down a complex search query in Rails 3

I have a controller which has a lot of options being sent to it via a form and I'm wondering how best to separate them out as they are not all being used simultaneously. Ie sometimes no, tags, sometimes no price specified. For prices I have a default price set so I can work around with it always being there, but the tags either need to be there, or not. etc.
#locations = Location.find(params[:id])
#location = #locations.places.active.where("cache_price BETWEEN ? AND ?",price_low,price_high).tagged_with([params[:tags]).order(params[:sort]).paginate :page => params[:page]
I haven't seen any good examples of this, but I'm sure it must happen often... any suggestions? Also, even will_paginate which gets tacked on last should be optional as the results either go to a list or to a google map, and the map needs no pagination.
the first thing to do when refactoring a complex search action is to use an anonymous scope.
Ie :
fruits = Fruit.scoped
fruits = fruits.where(:colour => 'red') if options[:red_only]
fruits = fruits.where(:size => 'big') if options[:big_only]
fruits = fruits.limit(10) if options[:only_first]
...
If the action controller still remains too big, you may use a class to handle the search. Moreover, by using a class with Rails 3 and ActiveModel you'll also be able to use validations if you want...
Take a look at one of my plugins : http://github.com/novagile/basic_active_model that allows you to easily create classes that may be used in forms.
Also take a look at http://github.com/novagile/scoped-search another plugin more specialized in creating search objects by using the scopes of a model.

Resources