Tire - sort by number of related object - ruby-on-rails

I have something like this in my model:
class User < ActiveRecord::Model
has_many :followers, :through => :as_followed_followings, :class_name => "User", :foreign_key => "follower_id", :uniq => true
...
def self.search(params)
tire.search(load: true, page: params[:page], per_page: params[:per]) do
end
end
end
I would like the return an array of users ordered by the count of followers that user has.
So what is the correct way to define mappings and indexes to search through nested object with count property ?
Thanks for reading.

I have solved this problem by creating another field on users table and index that value instead of performing nested search in runtime.
This may not be the best solution but I have deadline. Any better solution is welcome :)

Trying with ...
AsFollowedFollowing.count(:group => :follower_id, :order => "count_all DESC")
This is Ordered Hash that contains user_id => count
If you want to loop this hash, try following
# in controller
#counts = AsFollowedFollowing.count(:group => :follower_id, :order => "count_all DESC")
#users = User.where(:id => #counts.map{|k,_| k})
#in view
<% #counts.each do |k, v| %>
<% user = #users.find{|u| u.id == k} %>
<div><%= user.name %> | <%= v %></div>
<% end %>

Related

when search features works it causes error on nesting routes

I'm trying to implement a search with on an index page.
However it's affecting the nested route of a single entry when I add the code. [I tried gems, using elsif, and anything else I could think of].
For context, I'm using books belongs to authors scenario.
I'd like to implement this:
#comicbooks = Comicbook.search(params[:search])
into here:
def index
#comicbooks = #comicbooks.filter_by_name(params[:name])
if params[:person_id]
person = Person.find_by(id: params[:person_id])
#comicbooks = person.comicbooks
else
#comicbooks = Comicbook.all
end
end
Here is my model:
class Comicbook < ApplicationRecord
belongs_to :person
belongs_to :squad
validates :title, presence: true, uniqueness: { scope: :person }
def self.search(search)
if search
find(:all, :conditions => ['name Like ?', "%#{:search}%"])
else
find(:all)
end
end
def person_attributes=(args)
self.person = Person.find_or_create_by(args)
end
def squad_attributes=(args)
self.squad = Squad.find_or_create_by(args)
end
end
Here is the search form on the view
<%= form_tag comicbooks_path, :method => :get do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , :name => nil %>
<% end %>
Whats the best way to implement search without throwing off the other routes? I've tried many gems, but they've complicated things more than I can understand.
Thanks in advance.
You can use scope in your model and chain in your controller
Model:
scope :search, -> (search) do
return if search.blank?
where('name Like ?', "%#{:search}%")
end
Controller:
#comicbooks = Comicbook.filter_by_name(params[:name]).search(params[:search])

How to show a link to related records FROM ALIAS in ActiveAdmin?

I have this classes in order to show related records within user and all records on top menu.
ActiveAdmin.register Position, :as => 'Positionsfake' do
belongs_to :user
controller do
def scoped_collection
end_of_association_chain.where(category: 1)
end
end
ActiveAdmin.register Position, :as => 'Contactsfake' do
belongs_to :user
controller do
def scoped_collection
end_of_association_chain.where(category: 0)
end
end
ActiveAdmin.register Position do
controller do
def scoped_collection
end_of_association_chain.where(category: 1)
end
end
ActiveAdmin.register Position, :as => 'Contacts' do
controller do
def scoped_collection
end_of_association_chain.where(category: 0)
end
end
ActiveAdmin.register User do
controller do
def scoped_collection
User.includes(:provider).where(:providers => {:user_id => nil})
end
end
index do
column :name
column "Positions" do |a|
link_to a.positions.count, admin_user_positions_path(a)
end
column "Contatcs" do |a|
a.contacts.count
end
actions
end
Positions works fine! But Contacts can`t be called from here.
I have only one model. Contact and Position are the same except in category field. There is any way to solve this?
Thanks.
Any of this works for me.
ActiveAdmin.register User do
controller do
def scoped_collection
User.includes(:provider).where(:providers => {:user_id => nil})
end
end
index do
column :name
column :positions do |a|
link_to a.positions.where(category: 1).count, admin_user_positions_path(a)
end
column 'Contacts', :positions do |a|
a.positions.where(category: 0).count
end
actions
end
ActiveAdmin.register User do
controller do
def scoped_collection
User.includes(:provider).where(:providers => {:user_id => nil})
end
end
index do
column :name
column 'Positions' do |a|
link_to a.positions.where(category: 1).count, admin_user_positions_path(a)
end
column 'Contacts', 'Positions' do |a|
a.positions.where(category: 0).count
end
actions
end

Sunspot model assocation

I have two models, an School model and Price model. Every school has a price. I would like to return in the search result school with its prices. I am using rails and sunspot.
School-controller:
class SchoolsController < ApplicationController
def index
#query = params[:search]
#search = School.search do
fulltext params[:search]
paginate :page => params[:page], :per_page => 7
end
#results = #search.results
end
end
School-model:
class School < ActiveRecord::Base
has_many :prices
# sunspot search
searchable do
text :name, :locality
end
end
Index - view
<% for result in #results %>
<tr>
# School name, from the school-model
<td><h3><%= link_to result.name, result %></h3></td>
# School price, from the price-model
<td><h3><%= result.prices.min %> kr</h3></td>
</tr>
<% end %>
How do I return for every school its prices, with sunspot?
Maybe you can do eager loading with :include:
#search = School.search(:include => :prices) do # or :include => :price, depends on the relation
fulltext params[:search]
paginate :page => params[:page], :per_page => 7
end
Additionnal:
If a School can only have one Price, you should replace has_many :prices with has_one :price in your School model. After doing this, you could access to the price you want by doing this: result.price.min (take a look at number_to_currency, you could be interested in this Helper)

i just want to show the latest 10 tags sorted by tag.resources.count

this will show up in the sidebar, that is why i put it in application controller. im open to a better solution
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :get_tags_latest, :get_tags_popular
def get_tags_latest
#tags_latest = Tag.all.first(5)
end
def get_tags_popular
#tags_popular = Tag.by_most_resources.limit(10)
end
end
tag.rb:
class Tag < ActiveRecord::Base
self.include_root_in_json = false
has_many :resource_tags
has_many :resources, :through => :resource_tags
attr_accessible :name
validates :name, :presence => true,
:length => { :within => 2..20 },
:uniqueness => { :case_sensitive => false }
scope :by_most_resources,
joins("INNER JOIN resources ON resources.tag_id = tags.id").
group("tags.*").order("count(resources.id) DESC")
end
sidebar.html.erb
<ul class="tag-list">
<% #tags_popular.each do |t| %>
<li><%= link_to t.name, tag_path(t), :class => :tag %> (<%= t.resources.count %>)</li>
<% end %>
</ul>
I don't have much code at the moment (hopefully its in the right spot too)... really all I want to do is show the most popular 10 tags sorted by tag.resources.count, as well as the latest 5 tags sorted by date. I tried looking around for find(:order => ) but that proved unhelpful.
Is there a magical way to do this? Thanks
Starting with the SQL
SELECT tags.*
FROM tags
INNER JOIN resources ON resources.tag_id = tags.id
GROUP BY tags.*
ORDER BY count(resources.id) DESC
LIMIT 10
so, to ActiveRecordize this...
class Tag < ActiveRecord::Base
scope :by_most_resources,
joins("INNER JOIN resources ON resources.tag_id = tags.id").
group("tags.*").order("count(resources.id) DESC")
call this by:
Tag.by_most_resources.limit(10)

Is passing multiple params to thinking_sphinx search method absolutely impossible?

I have a page that when visted brings up the most recently active users. Above the users are some filtering options such as filtering by one or a combination of:
location
gender
sexual preference
age range
country
I'm using a form_tag helper for my form. My issue is passing each of these parameters to my controller:
class BrowsersController < ApplicationController
def index
#default_image = "/assets/default_avatar.jpg"
#users = Profile.search params[:search], :page => params[:page], :per_page => 26
end
end
If I was searching with one field with the param "Search" I would be fine but I have multiple fields, select menu's on my form. How am I suppose to pass that info to my controller in order to filter the search results?
I'm sure I'm not the first to use search filtering in ruby on rails
<%= form_tag browsers_path, :method => 'get' do %>
<p>
Location: <%= text_field_tag :location %><br />
Gender: <%= select_tag :gender,
options_for_select([["Select", nil],
["Male", 1],
["Female", 2]]) %>
<br />
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<br />
Kind regards
update
#users = Profile.search 'HERE IS WHERE THE POWER LIES', :page => params[:page], :per_page => 20, :conditions_all => { :gender => params[:gender], :location => params[:location]}
I use :conditions_all to get my field params and in rails server logs I can see that they are being picked up.. now I just need to some how get them all seen by thinking sphinx
Update 2
i have has gender in the define_index block and indexes location because it seems i need at least 1 field.
this working to return genders:
#users = Profile.search params[:location], :page => params[:page], :per_page => 40, :with => { :gender => [params[:gender]]}
I've tried to check for both location and gender and it seems to work but I'll double check in console because it's returning 1 female in united kingdom out of 1000 and that could be wrong but I'll double check in console and edit this update appropriately.
I'm not quite sure where you got :conditions_all from - if you're dealing with fields, then :conditions is what you're after:
#users = Profile.search 'HERE IS WHERE THE POWER LIES',
:page => params[:page],
:per_page => 20,
:conditions => {:gender => params[:gender], :location => params[:location]}
But, it sounds like you've got gender as an attribute instead of a field - and so, you want to filter on it instead:
#users = Profile.search 'HERE IS WHERE THE POWER LIES',
:page => params[:page],
:per_page => 20,
:conditions => {:location => params[:location]},
:with => {:gender => params[:gender]}
As I said here, I will try to explain a bit of Thinking Sphinx and my suggested approach to solve your problem.
Let's say you have the following Profile model:
# == Schema Information
#
# Table name: access_number_campaigns
#
# id :integer
# latitude :float
# longitude :float
# created_at :datetime
# updated_at :datetime
class Profile < ActiveRecord::Base
GENDER = {1 => "Male", 2 => "Female"}
belongs_to :country
has_and_belongs_to_many :sexual_preferences
has_and_belongs_to_many :age_ranges
end
And you may have those models:
class SexualPreference < ActiveRecord::Base
has_and_belongs_to_many :profiles
end
class AgeRange < ActiveRecord::Base
has_and_belongs_to_many :profiles
end
class Country < ActiveRecord::Base
has_many :profiles
end
Then you may define your ThinkingSphinx index in the following schema:
# within model Profile
define_index
has "RADIANS(latitude)", :as => :latitude, :type => :float
has "RADIANS(longitude)", :as => :longitude, :type => :float
has sexual_preferences(:id), :as => :sexual_preference_ids
has age_ranges(:id), :as => :age_range_ids
has country_id, :type => :integer
has gender, :type => :integer
end
And you can create a class to build the following ThinkingSphinx query to handle all needed associations and attributes:
Profile.search "your keywords", :page => 1, :per_page => 10, :with => {"#geodist" => 0.0..NUMBER_OF_METERS, :with_all=>{:sexual_preference_ids=>["1", "2", "3"], :age_range_ids=>["1", "2", "3"], :country_id => ["123"], :gender => ["1"]} }
You can see above a Thinking Sphinx search query with :with and :with_all hashes included. There is also an important Sphinx's #geodist function called. I hope you have readable example and the best reference for Thinking Sphinx gem you can find here:
Thinking Sphinx reference
Section about indexing
Section about searchin
Section about geodist
I hope you enjoy reading my example and very clear Thinking Sphinx reference.

Resources