rails mongoid criteria find by association - ruby-on-rails

I'm trying to find a record by associated username which is included in a belongs_to relation, but it's not working.
Articles belong to Users
Users have many articles
Article.where(user_id: someid) works fine, but I'd like to use the username as reference which is stored in the Users table.
Article.includes(:user).where(:username => "erebus")
Article.includes(:user).where("user.username" => "erebus")
I also have identity_map_enabled: true
Article.includes(:user).inclusions returns the relation details
Doesn't work, what am I not understanding?

You have to keep in mind that there are no joins in mongodb. In relational dbs, includes forms a join query and you can use columns from both the tables in query. However due to absence of joins in mongodb, same is not possible.
In mongoid, includes just saves a bunch of db calls. It fetches and stores the associated records in identity map for fast retrieval, but still while querying, one query can only deal with one collection.
If you need articles based on user names, I would suggest following work around:
user_ids = User.where(username: 'erebus').only(:_id).map(&:_id)
articles = Article.where(:user_id.in => user_ids)

You can make it little shorter from what rubish suggested:
user_ids = User.where(username: 'erebus').pluck(:id)
articles = Article.where(:user_id.in => user_ids)
Or one liner:
articles = Article.where(:user_id.in => User.where(username: 'erebus').pluck(:id))

Related

Rails - Filter the results if they match the association

I have ManytoMany association setup between Model User and Tag, suppose if I do
users = User.all
This will give me all the users, I want to filter these records if they are in ManyToMany association with lets say tag_id 55
For a single object I know I can do users.first.tags.exists?(55) and this will give true or false bot how do I perform this on a users which contains 100's of record?
My questions are
Is using loop the only way to achieve this?
How to remove to records from users where the relationship does not exist?
I have hundreds of records in users so I need to do this in a way it does not effect the performance.
I will really appreciate any feedback on this.
You can access the tag id 55 of user in this way
#user = User.joins(:tags).where("tags.id = ?" ,55)
OR
#user = User.includes(:tags).where(tags: {id: 55})
You can also filter for multiple ids of tag in this way
tags_id = [55,56,57,58,59]
#user = User.joins(:tags).where("tags.id IN = (?)" ,tags_id)
You can find whether associations is exists or not with this query
User.includes(:tags).where( :tags => { :id => nil } ).
It will give all users who dont have any tags.

Building an ActiveRecord query from a distant association

If a Diary has many Pages,
and each Page has many LoveLetters,
and the LoveLetters have many Recipients through Relationships,
then using ActiveRecord, how might I select all Pages who have LoveLetters that have Recipients (through Relationships) of name == "Bob"?
I tried using where, but it makes me specify a foreign key after a certain amount of nested associations, and I can't query the model any more:
diary = Diary.take
diary.pages.where(love_letters: { recipient_id #...
# I can't put 'recipient' here and keep nesting hashes, apparently.
# I think I have to specify the column name.
Not sure if this will work but off the top of my head:
Page.joins(:love_letters).merge(LoveLetter.joins(:recipients)).merge(Recipient.where(:name => "Bob"))
If that doesn't work, try it with Relationships merged also.
Page.joins(:love_letters).merge(LoveLetter.joins(:recipients)).merge(Relationship.merge(:recipients)).merge(Recipient.where(:name => "Bob"))

Datamapper: Sorting results through association

I'm working on a Rails 3.2 app that uses Datamapper as its ORM. I'm looking for a way to sort a result set by an attribute of the associated model. Specifically I have the following models:
class Vehicle
include DataMapper::Resource
belongs_to :user
end
class User
include DataMapper::Resource
has n, :vehicles
end
Now I want to be able to query the vehicles and sort them by the name of the driver. I tried the following but neither seems to work with Datamapper:
> Vehicle.all( :order => 'users.name' )
ArgumentError: +options[:order]+ entry "users.name" does not map to a property in Vehicle
> Vehicle.all( :order => { :users => 'name' } )
ArgumentError: +options[:order]+ entry [:users, "name"] of an unsupported object Array
Right now I'm using Ruby to sort the result set post-query but obviously that's not helping performance any, also it stops me from further chaining on other scopes.
I spent some more time digging around and finally turned up an old blog which has a solution to this problem. It involves manually building the ordering query in DataMapper.
From: http://rhnh.net/2010/12/01/ordering-by-a-field-in-a-join-model-with-datamapper
def self.ordered_by_vehicle_name direction = :asc
order = DataMapper::Query::Direction.new(vehicle.name, direction)
query = all.query
query.instance_variable_set("#order", [order])
query.instance_variable_set("#links", [relationships['vehicle'].inverse])
all(query)
end
This will let you order by association and still chain on other scopes, e.g.:
User.ordered_by_vehicle_name(:desc).all( :name => 'foo' )
It's a bit hacky but it does what I wanted it to do at least ;)
Note: I'm not familiar with DataMapper and my answer might not be within the standards and recommendations of using DataMapper, but it should hopefully give you the result you're looking for.
I've been looking through various Google searches and the DataMapper documentation and I haven't found a way to "order by assocation attribute". The only solution I have thought of is "raw" SQL.
The query would look like this.
SELECT vehicles.* FROM vehicles
LEFT JOIN users ON vehicles.user_id = users.id
ORDER BY users.name
Unfortunately, from my understanding, when you directly query the database you won't get the Vehicle object, but the data from the database.
From the documentation: http://datamapper.org/docs/find.html. It's near the bottom titled "Talking directly to your data-store"
Note that this will not return Zoo objects, rather the raw data straight from the database
Vehicle.joins(:user).order('users.name').all
or in Rails 2.3,
Vehicle.all(:joins => "inner join users on vehicles.user_id = user.id", :order => 'users.name')

Postgres ORDER BY values in IN list using Rails Active Record

I receive a list of UserIds(about 1000 at a time) sorted by 'Income'. I have User records in "my system's database" but the 'Income' column is not there. I want to retrieve the Users from "my system's database"
in the Sorted Order as received in the list. I tried doing the following using Active Record expecting that the records would be retrieved in the same order as in the Sorted List but it does not work.
//PSEUDO CODE
User.all(:conditions => {:id => [SORTED LIST]})
I found an answer to a similar question at the link below, but am not sure how to implement the suggested solution using Active Record.
ORDER BY the IN value list
Is there any other way to do it?
Please guide.
Shardul.
Your linked to answer provides exactly what you need, you just need to code it in Ruby in a flexible manner.
Something like this:
class User
def self.find_as_sorted(ids)
values = []
ids.each_with_index do |id, index|
values << "(#{id}, #{index + 1})"
end
relation = self.joins("JOIN (VALUES #{values.join(",")}) as x (id, ordering) ON #{table_name}.id = x.id")
relation = relation.order('x.ordering')
relation
end
end
In fact you could easily put that in a module and mixin it into any ActiveRecord classes that need it, since it uses table_name and self its not implemented with any specific class names.
MySQL users can do this via the FIELD function but Postgres lacks it. However this questions has work arounds: Simulating MySQL's ORDER BY FIELD() in Postgresql

How to join multiple tables with one to one relationships in rails

Ruby on Rails is very new to me. I am trying to retrieve set of columns from 3 different tables. I thought I could use SQL view to retrieve my results but could not find a way to use views in Rails. Here are my tables.
1) User table --> user name, password and email
2) UserDetails table --> foreign key: user_id, name, address1, city etc.
3) UserWorkDetails --> foreign key: user_id, work address1, work type, etc
These 3 tables have one to one relationships. So table 2 belongs to table 1 and table 3 also belongs to table 1. Table 1 has one userdetails and one userworkdetails.
I want to get user email, name, address1, city, work address1, work type using joins.
What is the best way to handle this?
The data is (are) in the models. Everything else is just an optimization. So address1 is at user.user_detail.address1, for instance.
if you have
class User
has_one :user_detail
has_one :user_work_detail
end
class UserDetail
belongs_to :user
end
class UserWorkDetail
belongs_to :user
end
With user_id columns in tables named user_details and user_work_details then everything else is done for you.
If you later need to optimize you can :include the owned models, but it's not necessary for everything to work.
To get what you want done quickly use the :include option to include both the other tables when you query the primary table, so:
some_user_details = User.find(some_id, :include => [:user_details, :user_work_details])
This will just load all of the fields from the tables at once so there's only one query executed, then you can do what you need with the objects as they will contain all of the user data.
I find that this is simple enough and sufficient, and with this you're not optimising too early before you know where the bottlenecks are.
However if you really want to just load the required fields use the :select option as well on the ActiveRecord::Base find method:
some_user_details = User.find(some_id, :include => [:user_details, :user_work_details], :select => "id, email, name, address1, city, work_address1")
Although my SQL is a bit rusty at the moment.
User.find(:first, :joins => [:user_work_details, :user_details], :conditions => {:id => id})
I'd say that trying to just select the fields you want is a premature optimization at this point, but you could do it in a complicated :select hash.
:include will do 3 selects, 1 on user, one on user_details, and one on user_work_details. It's great for selecting a collection of objects from multiple tables in minimum queries.
http://apidock.com/rails/ActiveRecord/Base/find/class

Resources