Rails Query limit - ruby-on-rails

I have a query of pictures like this
pictures = Picture.near([latitude, longitude], 6.8).where("created_at >= :time",{:time => time })
and I took out the tags which is associated with this model (every picture has_many :tags) like so
#tags = Tag.find(:all, :conditions => ["picture_id in (?)",pictures.collect(&:id)])
I used a query to pull out an attribute of each tag
#tags.map(&:tagcontent)
What I need to do is limit the results of the tags that come out. so I replaced
#tags = Tag.find(:all, :conditions => ["picture_id in (?)",pictures.collect(&:id)])
with this
numoftags = 6
tags = Tag.limit(numoftags).find(:all, :conditions => ["picture_id in (?)",pictures.collect(&:id)])
but this only yields 4 tags, when I know there is least 6 that can be pulled out. How should I modify this function.

In Rails 3.X you can do something like this (using Arel)
numoftags = 6
#tags = Tag.where(["picture_id in (?)",pictures.collect(&:id)]).limit(nooftags)

Related

limit collect in ruby

I currently have the following:
users = User.all
comments = users.collect(&:comments)
However, if there are thousands of comments, i'd like to only collect 10 from each user to limit the number of database queries that are made. Is there a way to do this?
users = User.all
comments = users.collect { |user| user.comments.limit(10) }
Or with another association in you model :
has_many :first_comments, :class_name => "Comment", :limit => 10
Then this will result in only two database queries:
users = User.includes(:first_comments)
comments = users.collect(&:first_comments)
The easiest in terms of query looks a little convoluted:
Comment.where(["user_id IN (?)", users.collect(&id) ]).limit(10)
I presume your sort order is set by a default scope somewhere.
Rails 2:
Comment.all(:conditions => ["user_id IN (?)", users.collect(&id) ], :limit => 10)
Try this
comments = Comment.where(:user_id=>users).limit(10)
or
comments = Comment.all(:conditions => {:user_id=>users}, :limit => 10)
You can use any one that suits you
users = User.all
comments = Comment.order("DESC created_at").limit(10).all
Or, if you only need users for these 10 recent comments, you can try
comments = Comment.includes(:users).order("DESC created_at").limit(10).all
users = comments.map(&:user)

Rails 2.3.5 Problem Building Conditions Array dynamically when using in (?)

Rails 2.3.5
I've looked at a number of other questions relating to building conditions dynamically for an ActiveRecord find.
I'm aware there are some great gems out there like search logic and that this is better in Rails3. However, I'm using geokit for geospacial search and I'm trying to build just a standard conditions set that will allow me to combine a slew of different filters.
I have 12 different filters that I'm trying to combine dynamically for an advanced search. I need to be able to mix equality, greater than, less than, in (?) and IS NULLs conditions.
Here's an example of what I'm trying to get working:
conditions = []
conditions << ["sites.site_type in (?)", params[:site_categories]] if params[:site_categories]
conditions << [<< ["sites.operational_status = ?", 'operational'] if params[:oponly] == 1
condition_set = [conditions.map{|c| c[0] }.join(" AND "), *conditions.map{|c| c[1..-1] }.flatten]
#sites = Site.find :all,
:origin => [lat,lng],
:units => distance_unit,
:limit => limit,
:within => range,
:include => [:chargers, :site_reports, :networks],
:conditions => condition_set,
:order => 'distance asc'
I seem to be able to get this working fine when there are only single variables for the conditions expression but when I have something that is a (?) and has an array of values I'm getting an error for the wrong number of bind conditions. The way I'm joining and flattening the conditions (based on the answer from Combine arrays of conditions in Rails) seems not to handle an array properly and I don't understand the flattening logic enough to track down the issue.
So let's say I have 3 values in params[:site_categories] I'll the above code leaves me with the following:
Conditions is
[["sites.operational_status = ?", "operational"], ["sites.site_type in (?)", ["shopping", "food", "lodging"]]]
The flattened attempt is:
["sites.operational_status = ? AND sites.site_type in (?)", ["operational"], [["shopping", "food", "lodging"]]]
Which gives me:
wrong number of bind variables (4 for 2)
I'm going to step back and work on converting all of this to named scopes but I'd really like to understand how to get this working this way.
Rails 4
users = User.all
users = User.where(id: params[id]) if params[id].present?
users = User.where(state: states) if states.present?
users.each do |u|
puts u.name
end
Old answer
Monkey patch the Array class. Create a file called monkey_patch.rb in config/initializers directory.
class Array
def where(*args)
sql = args.first
unless (sql.is_a?(String) and sql.present?)
return self
end
self[0] = self.first.present? ? " #{self.first} AND #{sql} " : sql
self.concat(args[1..-1])
end
end
Now you can do this:
cond = []
cond.where("id = ?", params[id]) if params[id].present?
cond.where("state IN (?)", states) unless states.empty?
User.all(:conditions => cond)

Rails Newbie: Appending conditions in activerecord?

I have a form which is used to filter queries. Some of the form attributes are optional; I'm just wondering how to append them as activerecord conditions if (and only if) they have a set value?
There's a fair few of them, so I'd rather not make a separate query for each pattern of potential values. Any suggestions?
To give a specific example:
people = People.paginate(
:all,
:include => [:people_postcodes, :people_specialties, :people_states],
:conditions => ["people_specialties.people_type_id = %s AND (people_postcodes.postcode_id = %s OR people_states.state_id = %s)" % [self.people_type_id, postcodeid.id, stateid]],
:page => page,
:per_page => 16
)
How would I best go about creating an extra condition (say 'nationality') only if the optional 'nationality' attribute is populated?
First off, your conditions are a little insecure. You're doing basic ruby text substitution, which will let site users inject whatever malicious sql they want. Instead, format it like this:
people = People.paginate(
:all,
:include => [:people_postcodes, :people_specialties, :people_states],
:conditions => ["people_specialties.people_type_id = ? AND (people_postcodes.postcode_id = ? OR people_states.state_id = ?)", self.people_type_id, postcodeid.id, stateid],
:page => page,
:per_page => 16
)
To answer your question, there's no natural way to tack on another condition in Rails 2.x. I would do this:
conditions = ["people_specialties.people_type_id = ? AND (people_postcodes.postcode_id = ? OR people_states.state_id = ?)", self.people_type_id, postcodeid.id, stateid]
if params[:nationality]
conditions.first += " and nationality = ?"
conditions.push params[:nationality]
end
people = People.paginate(
:all,
:include => [:people_postcodes, :people_specialties, :people_states],
:conditions => conditions,
:page => page,
:per_page => 16
)
In the example above, I'm assuming nationality is passed in as a parameter, but adjust as needed. I create the original conditions array, then append the first element (the actual condition string) and add one more element to the end of the array: the nationality value.
I hope this helps!

Using rails gem geokit sort by distance and pagination?

I come across a small issue in my app. I'm currently using geokit to find objects near a given location, and I use the sort_by_distance_from on the found set.
See below:
#find = Item.find(:all, :origin =>[self.geocode.lat.to_f,self.geocode.lng.to_f], :within=>50, :include=>[:programs], :conditions=>["programs.name = ?", self.name])
#find.sort_by_distance_from([self.geocode.lat.to_f,self.geocode.lng.to_f]
Is there any way with geokit to paginate form the DB when sorting by distance?
AKA, not calling the full found set?
The distance column isn't working anymore:
"In the current version of geokit-rails, it is not possible to add a where clause using the distance column. I've tried many different ways to do this and didn't get it working."
It behaves the same way for where and order clauses.
One would expect to build a query like this :
scoped = Location.geo_scope(:origin => #somewhere)
scoped = scoped.where('distance <= 5')
results = scoped.all
This is not possible right now, it must be done in a single step like this:
scoped = Location.within(5, :origin => #somewhere)
results = scoped.all
github.com/geokit/geokit-rails
My approach to solve this, would use the :offset and :limit parameters for the find()
also, there is a distance field for geokit models, :order=>'distance asc'
eg.
page = 0 unless params[:page]
items_per_page = 20
offset = page * items_per_page
#find = Item.find(:all, :origin =>[self.geocode.lat.to_f,self.geocode.lng.to_f], :within=>50, :include=>[:programs], :conditions=>["programs.name = ?", self.name], :order => 'distance asc', :limit => items_per_page, :offset => page)

How do I find the items immediately before and after a specific one in an ordered array, using Ruby on Rails?

I have an array of 'questions' ordered according to their number of votes, and want to show the question immediately before and immediately following the currently-selected question.
Let's say the currently-selected question is stored in the variable #question, and I'm showing a list of other questions associated with the same user. This code orders that list according to number of votes:
questions = Question.find(:all, :conditions => {:user => #question.user}).sort { |q1,q2| q2.votes.length <=> q1.votes.length}
Now how do I pick out just the question before and the question after #question in that list?
Updated my answer. I figured you guys would get the gist from the first post but here is a more elaborated example.
Can't you do something simple like:
#questions = user.questions.sort_by{ |q| -q.votes.length }
current_question_index = #questions.index(#question)
#prev_question = #questions[current_question_index-1]
#next_question = #questions[current_question_index+1]
It's more lines but just uses simple array manipulation
I would refer to time stamps.
prev = Question.first(:conditions => ["created_at < ?", #question.created_at], :order => "created_at DESC")
prev = Question.first(:conditions => ["created_at > ?", #question.created_at], :order => "created_at ASC")

Resources