Paginate from record - ruby-on-rails

I use the gem will-paginate.
Lets suppose i have a model records that is sort by created_at and the client has the records until a specific record with the id 77. Now would it be possible to define for example:
Records.paginate(:page => params[:record_id], :per_page => 30)
So that the pagination doesnt`t start at a specific page but at a record
Thanks!

The unique alternative I can imagine is to add a where condition and sorting the results by id
Records.where("id >= ?", params[:record_id]).order(id: :asc).paginate(:page => params[:page], :per_page => 30)
This way you ensure that the record_id received is the first element in the pagination and all the results are after that one

Related

Rails Union Table and sort by common key

I have two models in rails application Match and Round as follow
Match(id, team1, team2, series_id, event_time, event_lock_time, match_type, created_at, updated_at, match_no, match_stage)
Round(id, series_id, name, description, event_time, event_lock_time, team_ids, matches_count, created_at, updated_at)
I would like to add two table and sort records based on created_at something like
events = (Match + Round).order('created_at desc').paginate(:page => params[:page], :per_page => params[:per_page])
and also apply pagination. I am using will paginations gem.
Can anyone help me how to do this in active record.
As long as I know, models doesn't have + operator. So you can't do that:
(Match + Round)
But you can add Active Record Relations:
matches = Match.all
rounds = Round.all
(matches + rounds).sort_by(&:created_at)
You have to use sort_by or a similar way because (matches + rounds) is Array and not an Active Record Relation (so order is not defined).
UPDATE:
I guess you are using will_paginate to paginate the results. It still works with array but you have to tell that and you can tell it in two ways:
Add this to the top of the controller: require 'will_paginate/array'
Create an initializer config/initializers/will_paginate_with_array.rb and require it inside this file.
Then, you can do:
(matches + rounds).sort_by(&:created_at).paginate(:page => params[:page], , :per_page => params[:per_page])

How to merge multiple and different ActiveRecord query in rails and paginate over the records?

I have two similar tables in rails named as messages and message_histories. Periodically we remove the old data from messages table and drop it in message_histories .
Now , I want to generate a report on the count of messages grouped by app_id which is present in message and message_history table .
Is there a way to Query [Message & MessageHistory ] Model and paginate on the records .
Currently I use the following Step , It looks weird but suggest a better way to do this :
#messages = Message.select("SUM(devices_count) as count ,CAST(run_at AS DATE) AS date").where(:app_id => #app.id).where("last_run_date is not ?", nil).group("CAST(run_at AS DATE)").order("date desc")
#messages << MessageHistory.select("SUM(devices_count) as count ,CAST(run_at AS DATE) AS date").where(:app_id => #app.id).where("last_run_date is not ?", nil).group("CAST(run_at AS DATE)").order("date desc")
#messages = #messages.flatten.paginate(:page => params[:page] || 1, :per_page => 100)
It sounds like what you want is to UNION the two tables, then paginate the results.
See ActiveRecord Query Union for some examples, and look at the active_record_union gem (which makes it easier to do UNIONs with ActiveRecord).
It will take some experimentation to figure out how to apply the where clause filters and sums/groups properly!

How use order by condition

Is it possible to sort the data on a condition?
For example, figuratively, .order( "expiration_date <?", Time.current, :desc )
The ORDER BY keyword is used to sort the result-set by one or more columns.
The ORDER BY keyword sorts the records in ascending order by default. To sort the records in a descending order, you can use the DESC keyword.
you can sort by using something like :-
sorted = #records.sort_by &:created_at
or try where by first getting the resultset and then sorting it:-
#records.where( "expiration_date <?", Time.current).order( :created_at, :desc )
Moreover you can paginate easily;-
#records.paginate(:page => params[:page], :per_page => 15)#.order(" created_at ASC")

will_paginate reporting too many entries and pages

I'm using will_paginate to display data returned from a query that includes both a joins and a select statement. When I paginate the data the number of entries is equal to the number of entries before executing the select statement, even though paginate is being called after the query, and the query contains fewer elements than paginate reports.
#sales = Sale.joins(:line_items).where(company_id: company_id, status: ['Complete', 'Voided'], time: (midnight_1..midnight_2)).order('id DESC')
puts #sales.length
14
#sales = #sales.select('distinct sales.*')
puts #sales.length
4
#sales.paginate(:per_page => 4, :page => params[page])
puts #sales.total_entries
14
This leads to displaying links to empty pages.
It's always going to be slightly harder to paginate and join in has_many or has_and_belongs_to_many associations with will_paginate, or indeed any pagination solution.
If you don't need to query on the joined in association you can remove it. You lose the benefit of getting the associated line items in one query but you don't lose that much.
If you need to query on it, and presumably you want sales that only have line items, you'll need to pass in a :count option to the paginate call which specifies additional options that are used for the call to count how many items there are. In your case:
#sales.paginate(:per_page => 4,
:page => params[page],
:count => {:group => 'sales.id' })
Assuming that your Sale model has_many :line_items, by joining you're going to get a 'sales' entry for every related 'line_item'.

How do I Order on common attribute of two models in the DB?

If i have two tables Books, CDs with corresponding models.
I want to display to the user a list of books and CDs. I also want to be able to sort this list on common attributes (release date, genre, price, etc.). I also have basic filtering on the common attributes.
The list will be large so I will be using pagination in manage the load.
items = []
items << CD.all(:limit => 20, :page => params[:page], :order => "genre ASC")
items << Book.all(:limit => 20, :page => params[:page], :order => "genre ASC")
re_sort(items,"genre ASC")
Right now I am doing two queries concatenating them and then sorting them. This is very inefficient. Also this breaks down when I use paging and filtering. If I am on page 2 of how do I know what page of each table individual table I am really on? There is no way to determine this information without getting all items from each table.
I have though that if I create a new Class called items that has a one to one relationship with either a Book or CD and do something like
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "genre ASC")
However this gives back an ambiguous error. So can only be refined as
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "books.genre ASC")
And does not interleave the books and CDs in a way that I want.
Any suggestions.
The Item model idea will work, but you are going to have to pull out all the common attributes and store them in Item. Then update all you forms to store those specific values in the new table. This way, adding a different media type later would be easier.
Update after comment:
What about a union? Do find_by_sql and hand-craft the SQL. It won't be simple, but your DB scheme isn't simple. So do something like this:
class Item < ActiveModel::Base
attr_reader :title, :genre, :media_type, ...
def self.search(options = {})
# parse options for search criteria, sorting, page, etc.
# will leave that for you :)
sql = <<-SQL
(SELECT id, title, genre, 'cd' AS media_type
FROM cds
WHERE ???
ORDER BY ???
LIMIT ???
) UNION
(SELECT id, title, genre, 'book' AS media_type
FROM books
WHERE ???
ORDER BY ???
LIMIT ???
)
SQL
items = find_by_sql(sql)
end
end
untested
Or something to that effect. Basically build the item rows on the fly (you will need a blank items table). The media_type column is so you know how to create the links when displaying the results.
Or...
Depending on how often the data changes you could, gasp, duplicate the needed data into the items table with a rake task on a schedule.
You say you can't change how books and CDs are stored in the database, but could you add an items view? Do you have any control over the database at all?
CREATE VIEW items
(id, type, title, genre, created_at, updated_at)
AS
SELECT b.id, 'Book', b.title, b.genre, b.created_at, b.updated_at
FROM books b
UNION
SELECT c.id, 'CD', c.title, c.genre, c.created_at, c.updated_at
FROM cds c;
You can paginate on a results array, so leave pagination out of the invidual model queries, and add it to your results array:
re_sort(items,"genre ASC").paginate(:page => params[:page], :per_page => items_per_page)

Resources