Assume, we have two tables: "Items" and "Types". The relationships are:
item belongs_to type
type has_many items
Also, the Item table have a column, let's call it "mark". What would be a query (in a Rails secure way if it's possible) to extract all the types from them Types table, which have connected items in Items table with a "mark"?
This:
Type.find :all, :include => items, :conditions => ['items.mark = ?', somevalue]
should work.
Note: you shouldn't use Type as class name, nor :type as attribute, as this name can lead to conflicts.
Related
I've got indexes on a few different models, and sometimes the user might search for a value which exists in multiple models. Now, if the user is really only interested in data from one of the models I'd like the user to be able to pre/postfix the query with something to limit the scope.
For instance, if I only want to find a match in my Municipality model, I've set up an index in that model so that the user now can query "xyz municipality" (in quotes):
define_index do
indexes :name, :sortable => true
indexes "name || ' municipality' name", :as => :extended_name, :type => :string
end
This works just fine. Now I also have a Person model, with a relation to Municipality. I'd like, when searching only on the Person model, to have the same functionality available, so that I can say Person.search("xyz municipality") and get all people connected to that municipality. This is my current definition in the Person model:
has_many :municipalities, :through => :people_municipalities
define_index do
indexes [lastname, firstname], :as => :name, :sortable => true
indexes municipalities.name, :as => :municipality_name, :sortable => true
end
But is there any way I can create an index on this model, referencing municipalities, like the one I have on the Municipality model itself?
If you look at the generated SQL in the sql_query setting of config/development.sphinx.conf for source person_core_0, you'll see how municipalities.name is being concatenated together (I'd post an example, but it depends on your database - MySQL and PostgreSQL handle this completely differently).
I would recommend duplicating the field, and insert something like this (SQL is pseudo-code):
indexes "GROUP_CONCAT(' municipality ' + municipalities.name)",
:as => :extended_municipality_names
Also: there's not much point adding :sortable true to either this nor the original field from the association - are you going to sort by all of the municipality names concat'd together? I'm guessing not :)
I have a model called websites that has_many :likes, and of course another model called likes with belongs_to :website. I want to get an array of all the websites but order them by the number of likes a website has.
helpful info:
I have it set up so #website.likes will return the likes from #website. (#website.likes.count is the number of likes a website has)
I want to get an array of all the websites but order them by the number of likes a website has.
As others have posted, you can do a join onto likes and then order by the count. Performance may be a bit iffy depending on indexing etc. You'll have slightly different syntax depending on if you're running Rails 2 or 3.
An alternative would be to maintain a denormalised likes_count column on websites which is updated when a Like model object is saved.
Then you just need to query on Website and specify an order likes_count descending (and is easily indexed).
To do this, create a likes_count integer column on the websites and specify the :counter_cache option on the belongs_to declaration in the Likes model. e.g:
class Likes
belongs_to :website, :counter_cache => true
end
Check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html for more info
This query should give you what you need:
all(:select => 'websites.*, count(*) as count',
:joins => :likes,
:group => :websites,
:order => 'count DESC')
Something along the lines of:
SELECT Websites.*, COUNT(Like.ID) AS Liked
FROM websites
LEFT OUTER JOIN Like ON websites.ID = Like.website_id
GROUP BY Like.website_id ORDER BY Liked DESC
I have several similar models ContactEmail, ContactLetter, etcetera.
Each one belongs_to a Contact
Each contact belongs_to a Company
So, what I did was create a virtual attribute for ContactEmail:
def company_name
contact = Contact.find_by_id(self.contact_id)
return contact.company_name
end
Question: How can I get an easy list of all company_name (without duplicates) if I have a set of ContactEmails objects (from a find(:all) method, for example)?
When I try to do a search on ContactEmail.company_name using the statistics gem, for example, I get an error saying that company_name is not a column for ContactEmail.
Assuming your ContactEmail set is in #contact_emails (untested):
#contact_emails.collect { |contact_email| contact_email.company_name }.uniq
You don't need the virtual attribute for this purpose though. ActiveRecord sets up the relationship automatically based on the foreign key, so you could take the company_name method out of the ContactEmail model and do:
#contact_emails.collect { |contact_email| contact_email.contact.company_name }.uniq
Performance could be a consideration for large sets, so you might need to use a more sophisticated SQL query if that's an issue.
EDIT to answer your 2nd question
If company_name is a column, you can do:
ContactEmail.count(:all, :joins => :contact, :group => 'contact.company_name')
On a virtual attribute I think you'd have to retrieve the whole set and use Ruby (untested):
ContactEmail.find(:all, :joins => :contact, :select => 'contacts.company_name').group_by(&:company_name).inject({}) {|hash,result_set| hash.merge(result_set.first=>result_set.last.count)}
but that's not very kind to the next person assigned to maintain your system -- so you're better off working out the query syntax for the .count version and referring to the column itself.
When fetching content from a database using activerecord, I would like to fetch a custom resultset with specified columns across two tables.
SELECT users.name, users.username, users.age, users.city_id, cities.name as city_name FROM users INNER JOIN cities ON users.city_id = cities.id
Which would be in AR as
Users.find(:all,
:joins => :cities,
:select => "users.name, users.username, users.age, users.city_id,
cities.name as city_name")
But this only returns the user table results and not the city results. I am 100% sure that the inner join statement is going through (that both tables are being joined).
It seems as if the return object only has the columns associated with the model. So UserModel would only have the columns that the users table has and won't allow to fetch the columns of the cities table even though they're specified in the select.
Should I be using :joins or :include? Any idea what's going on?
If you alias the joined column name then returned object should have an attribute by the alias name, i.e.
u = User.first( :joins => :cities,
:select => "users.*, cities.name AS city_name")
u.city_name # returns the city_name.
In your case, :joins is appropriate than :include.
I checked this in my setup and it works for me ( I am on Rails 2.3.8)
In your returned instances, if the column's name is city_name, you should be using user.city_name. Alternatively, if you use :include, you would be telling ActiveRecord to load the associated city models, which you would then reference as user.city.name.
To summarize:
users = User.find(:all, :joins => :cities, :select => "users.name, users.username, users.age, users.city_id, cities.name as city_name")
users.map(&:city_name)
users = User.find(:all, :include => :cities)
users.map(&:city).map(&:name)
you can use specific column name in user table in place of "users.*" if you dont need all column. I think its good programming practice.
u = User.first( :joins => :cities,
:select => "users.name, users.username, users.age, users.city_id, cities.name AS city_name")
u.city_name # returns the city_name.
I have a database with two tables: tags and items.
Each Item has a score, the highest scoring items being the most popular. There is a many-to-many relationship between tags and items.
Getting all items belonging to a tag is easy. (= tag.items) But how do I retrieve the 10 most popular items belonging to this tag?
So in fact I need the ruby equivalent of
SELECT * from items INNER JOIN item_tags ON items.id = item_tags.item WHERE item_tags.tag = :tagid ORDER BY items.score DESC LIMIT 10
Since a tag might have a lot of items, I prefer to let the database do this work instead of retrieving all items and then filtering them manually. (and if there is a faster way to perform this operation, it is certainly welcome!)
Assuming the following setup:
class Item < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :items
end
You should be able to do:
#tag = Tag.find(PARAMS)
#tag.items.find(:all, :order => "items.score DESC", :limit => 10)
If you want to make it even slicker, add this line to your Item class:
named_scope :popular, :order => "items.score DESC", :limit => 10
You can then call
#tag.items.popular