ruby-on-railsrubyruby-on-rails-6geocoder

sort_by multiple attributes in hash


I want to be able to sort my posts based on

So far I've been able to sort by distance, but I'm having trouble figuring out how to construct my map and still be able to keep distance visible to my users in the array (even though they have chosen to sort by vote_count for instance)

User calls update_order from view

def update_order
  @order = params[:order]
  @posts = Post.all
  get_distance_posts
end

The method that determines the distance:

def get_distance_posts
    posts = {}
    @posts.map{ |post| posts[post.id] = (check_coordinates_present?(post, current_user) ? get_users_distance(post,current_user) : 0 ) }
    @sorted_posts = Hash[*posts.sort_by {|_key, value| value}.flatten]
end

My view.html.erb

<% @sorted_posts.map do |u_id, dist|  %>
    <% post = @posts.find(u_id) %>
    <p> <%= post.created_at %> </p>
    <p> <%= post.vote_count %> </p>
    <p><%= dist %></p>
<% end%>

Any help would be greatly appreciated!


Solution

  • You should not be doing ordering in Ruby in the first place. You will be pulling all of the records out of the database and your server will start to have serious performance problem and eventually start crashing when your app scales as it will exhaust the available memory.

    Instead you want to use .order which adds an order clause to the query to sort the results.

    Post.near([48.848903, 2.354533])
        .order(
           created_at: :asc, 
           vote_count: :desc
        )
    

    This allows you to apply paging and limits to the query to prevent the above mentioned memory issues.

    .near is a special scope provided by the Geocoder gem which selects two special calculated geospatial columns - distance and bearing. It also orders by distance.

    Here I'm using a static set of coordinates but you can pass a set of coordinates from your model or even an address that will be geocoded:

    # to_coordinates is provided by Geocoder
    Post.near(user.to_coordinates)
        .order(
           created_at: :asc, 
           vote_count: :desc
        )
    
    Post.near("Beijing")
        .order(
           created_at: :asc, 
           vote_count: :desc
        )
    

    If you want to select the distance but not use it for ordering use .reorder which replaces the order clause of the query:

    Post.near(user.to_coordinates)
        .reorder(
           created_at: :asc, 
           vote_count: :desc
        )