Hey I'm trying to figure something out..
I want to get all Carts that have a cart_stage.stage equal to '35' (this is a separate table and a cart has many stages)
the cart_stage table is a bit like
id ----- cart_id ----- stage
1 ------- 123 ---------- 20
2 ------- 123 ---------- 35
3 ------- 102 ---------- 35
I am trying this at the moment:
# Cart model
has_one :top_stage, :foreign_key => 'cart_id', :class_name => "CartStage", :order => 'stage'
# Cart controller
#carts = Cart.find :all, :order => 'created_at DESC', :include => :top_stage, :conditions => ["top_stage.stage = ?", 35]
This gives me :
SQLite3::SQLException: no such column: top_stage.stage: SELECT DISTINCT "carts".id FROM "carts" WHERE (top_stage.stage = 35) ORDER BY created_at DESC LIMIT 40 OFFSET 0
Hope it all makes sense and any help would be greatly appreciated,
Alex
That should probably be:
#carts = Cart.find(:all, :order => 'carts.created_at DESC', :include => :top_stage, :conditions => { "cart_stages.stage" => 35 })
Remember that you use the name of the table in the conditions, not the name of the association, however you do use the name of the association in the include option.
Whenever possible, you should probably use the hash method for expressing conditions to keep your declarations simple. It's when you need complicated OR or > type logic that the array-style proves necessary.
A better way of expressing it for Rails 3 is:
#carts = Cart.order('carts.created_at DESC').include(:top_stage).where('top_stages.stage' => 35)
It's not clear why your cart_stages table isn't being added with a JOIN.
Anywhere that you include literal SQL, you need to use the real table and column names, not the Rails association names. So you need to have: :conditions => ["cart_stage.stage = ?", 35] instead of top_stage
I managed to work it out my self actually.. What I wanted was -
#carts = Cart.find :all, :order => 'created_at DESC', :joins => :top_stage, :conditions => { :cart_stages => { :stage => 35 } }
Thanks for all the help guys! :)
Related
Ok, I got the following simple model class:
class Baby < ActiveRecord::Base
attr_accessible :name, :born_at
...
end
And I need to collect the youngest 20 babies with unique names
Baby.all(:order => "born_at desc", :limit => 20)
But I don't know what to add to the request so the names of the babies to be unique.
Disclaimer: I'm pretty new to SQL databases, so don't judge me for my lame question.
Baby.group(:name).order('born_at desc').limit(20)
Baby.all(:order => 'born_at desc', :limit => 20, :group => :name)
Or if you need only names
Baby.select(:name).limit(20).order('born_at desc')
I'd like to do a complex search with thinking sphinx:
Search for users which:
-> live in a city (city_id attribute)
-> or has hability to move to a city (mobile_cities association)
-> or live at a maximum distance from a lat/long point, the maximum distance is different for each user and set in a mobility_distance attribute.
For now I did that with 3 differents search, I volontary set a big per_page number, then i merge the 3 results on a single array, an then paginate this array :
#users living in the #city
search_set_living = search_set.merge({:city_id => #city.id })
users_living = User.search :with => search_set_living.dup,
:page => 1, :per_page => 1000
#users declaring hability to move to the #city
search_set_mobile = search_set.merge({:mobile_cities_ids => #city.id })
users_mobile = User.search :with => search_set_mobile.dup, :page => 1, :per_page => 1000
#users living at a maximum distance from the origin point(custom distance for each user, max 30km)
search_set_around = search_set.merge({"#geodist" => 0.0..30_000.0})
users_around = User.search :geo => [#search_latitude * Math::PI / 180 , #search_longitude * Math::PI / 180],
:with => search_set_around.dup,
:page => 1, :per_page => 1000
users_around_filtered = users_around.dup.delete_if{|user| (user.mobility_distance * 1000 )< user.sphinx_attributes['#geodist'] }
#merge the 3 results in a array
all_users = (users_mobile.flatten + users_around_filtered.flatten).uniq
#look for facets and paginate the array
#facets = User.facets :with => {:user_id => all_users.map(&:id)}
#users_to_display = all_users.paginate(:page => params[:page], :per_page => 10)
This is working fine but i'm not satisfied:
-performance are not so good,
-I want the ability to sort on multiple attributes like this :order => "created_at DESC, #relevance DESC"
I want to do the exact same search but in a single sphinx's search.
I know that I should use the "OR Logic with Attribute Filters" from the docs but I don't know how to mix it with a geo_search call...
I really have no idea how to do that,
can you guys help me ?
Many thanks,
The :sphinx_select option is definitely your friend here, as you've guessed. Let's piece it together bit by bit:
logic = [
"city_id = #{#city.id}",
"IN(mobile_cities_ids, #{#city.id}",
"GEODIST(lat, lng, #{lat}, #{lng}) < (mobility_distance * 1000)"
]
User.search :sphinx_select => "*, #{logic.join(" OR ")}) AS valid",
:with => {:valid => true}
Add pagination as you like, tweak the attribute names if needed (maybe your lat/lng attributes are named something else). I don't think you need the IF call around that custom attribute like in the docs, but if things aren't working when they should be, maybe give it a shot. Should be good in a facets call too.
Great ! Thank you so much. I just needed to correct a little your syntax (some parenthesis missing) in order to get it work.
I had to add per_page and page arguments too, don't know really why.
logic = ["city_id = #{#city.id}",
"IN(mobile_cities_ids, #{#city.id})",
"GEODIST(latitude, longitude, #{#search_latitude * Math::PI / 180}, #{#search_longitude * Math::PI / 180}) < (mobility_distance * 1000)"]
search_set_logic = search_set.merge({:valid => true})
#users_to_display = User.search :sphinx_select => "*, (#{logic.join(" OR ")}) AS valid",
:with => search_set_logic.dup,
:sort_mode => :extended,
:order => "visibility DESC, last_login_at DESC",
:page => params[:page], :per_page => 10
I am finishing up my first RoR project, and am working on a leaderboard system that shows the number of points users have accrued for correctly answering quiz questions.
I am getting all of the users that have answered at least one question correct, grouping them by user_id, and displaying them in descending order by most correct using this:
#users = Point.find(:all,
:group => 'user_id',
:order => 'correct DESC', :conditions => { :correct => "yes"})
In my view, I am using this to iterate through the results:
<% #users.each_with_index do |user, index| %>
However, I am not able to get the number of correct answers per user. I tried:
user.count
but that doesn't work. How do I get the number of items per group?
You're on the right track. Seems like you would be better off using the all command with the count condition within it as opposed to the count command. Something like this:
Point.all(:select => 'user_id, count(id) as point_count', :group => :user_id, :conditions => { :correct => 'yes' }, :order => 'point_count desc', :limit => 10)
This will return 10 limited Point objects with a user_id attribute (so you can still access the user relationship), and a point_count attribute with the number of correct points said user has obtained.
Note: you could change the limit to be however many users you wanted to display in your leaderboard. This example would return 10.
It might make more sense to have your code look like this:
#points = Point.all(:select => 'user_id, count(id) as point_count', :group => :user_id, :conditions => { :correct => 'yes' }, :order => 'point_count desc', :limit => 10)
And as I said in a comment below, you could iterate through them by doing something like this (this would assume that your User model has a name attribute):
<table>
<% #points.each do |point| %>
<tr>
<td><%= point.user.name %></td>
<td><%= point.point_count %></td>
</tr>
<% end %>
</table>
I think the problem may be that you think you're getting an Array back, but you actually get a Hash back.
Try doing:
p #users
(which is equivalent to puts #users.inspect). You'll probably see it's more so something like:
{ "1" => [UserObject, UserObject], "2" => `[UserObject] }
You can even do p #users.class and you'll see it's not an array.
When you loop with a .each_with_index on a Hash, you need to do:
#users.each_with_index do |(key, value), index|
Then you can do #users[key].count or value.count.
Figured out how to get the correct count:
#users = Point.count(:group => :user_id, :conditions => { :correct => "yes"})
The most simple way should be:
#user.points.where(:correct => "yes").count
Though this will only work if have defined your associations in the user and point model like
class User < ActiveRecord::Base
has_many :points
class Point < ActiveRecord::Base
belongs_to :user
(personally I would have used a bool flag (smallint) instead of string for the "correct" column.
on my site, I'm trying to display the hiking trails with the latest posted pictures (Scroll to the "Picture section: http://www.trailheadfinder.com/trail_search/latest_trails). However, the current query I use, is showing the trails in order of the "first" picture posted. So when a new picture is added at a later date, the trail does not show at the top. I have a trail table and a trailpicture table that are linked. Here is the current query I use:
#trails_pictures = Trailpicture.find(:all,
:limit => 20,
:include => [:trail],
:select => 'trailpictures.trail_id, trails.name, trails.short_description, trails.city, trails.state, trails.country',
:group => 'trailpictures.trail_id',
:conditions => ["trailpictures.parent_id is NULL"],
:order => 'trailpictures.id DESC')
Any help would be greatly appreciated!
Thank You,
Nick,
You need to order by created_at. :)
I finally figured it out. This is how it needs to be written:
#trails_pictures = Trail.find(:all,
:joins => 'INNER JOIN trailpictures ON trails.id = trailpictures.trail_id',
:limit => 20,
:conditions => ["trailpictures.parent_id is NULL"],
:select => 'trails.id, trails.name, trails.short_description, trails.city, trails.state, trails.country, max(trailpictures.id)',
:group => 'trails.id',
:order => 'max(trailpictures.id) DESC')
I've a post model with act-as-taggable-on gem. Both tables have timestamps.
I started with
def tags
#posts = current_user.posts.find_tagged_with(params[:tag], :order => "#posts.tags.updated_at DESC"])
end
And when that didn't work, I tried changing things and ended up with this mess.
def tags
#posts = current_user.posts.find_tagged_with(params[:tag])
#tags = #posts.tags.all
#posts = #tags(params[:tag, :order => "#posts.tags.updated_at DESC"])
end
I basically want to sort by when the tags was last updated.
Bonus: Sort by tag.updated_at or post.updated_at, but in this particular app, I'll be updating tags the most, so just first one will be fine.
Any help is appreciated.
You have to join the tags table in your find statement:
def tags
#posts = Post.find_tagged_with(
params[:tag],
:conditions => {:user_id => current_user.id},
:joins => :tags,
:order => 'tags.updated_at DESC',
:group => 'posts.id'
)
end
Note: Find the right conditions to select only posts from the current user. This example could work, though.