I have materials which:
belong to a user: user_id:
can be masters or versions: status:
can have a parent_id if a version: parent_id: (note: if it's a master the parent_id will be nil)
I want to get all the materials that:
a) belong to a certain user
b) are not versions unless the parent_id of the version is not one of the ids of the masters
How would I do this kind of query?
I'm not sure if I understood you correctly.
This will give you the master materials or (version materials that aren't childs of a master of the user) for a given user.
master_ids = certain_user.materials.where(status: 'master').pluck(:id)
certain_user.materials.where("status = ? OR parent_id NOT IN (?)", 'master', master_ids)
If you don't have access to an instance of the user just replace certain_user.materials with Material.where(user_id: the_id_of_the_user)
Try these...
a)Material.joins(:users).merge(User.where(field: value))
b)Material.where("parent_id IS NOT NULL and parent_id not in (?)", Material.pluck(:id))
Related
I have three models: User, Category and Feed.
The first one is User and it has a One to Many relationship with the second model which is Category. Category has a userId column and a Many to One relationship with User.
Category has a One To Many relationship with the third and last model: Feed. Similarly, Feed has a column categoryId and a Many To One relationship with Category.
I want to access the Feeds of a certain categoryId (for example of categoryId = 2) but only where the userId on this category is a certain value too (for example of userId = 1).
This relation is the has_many_through for Ruby on Rails programmers...
How can I build this query using TypeORM ?
Alternatively, if you have an idea on how to write it in pure SQL I'll take it too.
I'm also thinking about creating a column userId directly through Feeds to have a One To Many relationship between User and Feed. Do you think it'll be more optimized to do so ?
Many thanks.
This is how you would achieve that through pure SQL, I'm not much help on the TypeORM front unfortunately.
SELECT f.*
FROM Feeds f
LEFT JOIN Category g
ON f.categoryId = g.categoryId
WHERE f.categoryId = 2
AND g.userId = 1
I have an Adventure model, which is a join table between a Destination and a User (and has additional attributes such as zipcode and time_limit). I want to create a query that will return me all the Destinations where an Adventure between that Destination and the User currently trying to create an Adventure does not exist.
The way the app works when a User clicks to start a new Adventure it will create that Adventure with the user_id being that User's id and then runs a method to provide a random Destination, ex:
Adventure.create(user_id: current_user.id) (it is actually doing current_user.adventures.new ) but same thing
I have tried a few things from writing raw SQL queries to using .joins. Here are a few examples:
Destination.joins(:adventures).where.not('adventures.user_id != ?'), user.id)
Destination.joins('LEFT OUTER JOIN adventure ON destination.id = adventure.destination_id').where('adventure.user_id != ?', user.id)
The below should return all destinations that user has not yet visited in any of his adventures:
destinations = Destination.where('id NOT IN (SELECT destination_id FROM adventures WHERE user_id = ?)', user.id)
To select a random one append one of:
.all.sample
# or
.pluck(:id).sample
Depending on whether you want a full record or just id.
No need for joins, this should do:
Destination.where(['id not in ?', user.adventures.pluck(:destination_id)])
In your first attempt, I see the problem to be in the usage of equality operator with where.not. In your first attempt:
Destination.joins(:adventures).where.not('adventures.user_id != ?'), user.id)
you're doing where.not('adventures.user_id != ?'), user.id). I understand this is just the opposite of what you want, isn't it? Shouldn't you be calling it as where.not('adventures.user_id = ?', user.id), i.e. with an equals =?
I think the following query would work for the requirement:
Destination.joins(:adventures).where.not(adventures: { user_id: user.id })
The only problem I see in your second method is the usage of destinations and adventures table in both join and where conditions. The table names should be plural. The query should have been:
Destination
.joins('LEFT OUTER JOIN adventures on destinations.id = adventures.destination_id')
.where('adventures.user_id != ?', user.id)
ActiveRecord doesn't do join conditions but you can use your User destinations relation (eg a has_many :destinations, through: adventures) as a sub select which results in a WHERE NOT IN (SELECT...)
The query is pretty simple to express and doesn't require using sql string shenanigans, multiple queries or pulling back temporary sets of ids:
Destination.where.not(id: user.destinations)
If you want you can also chain the above realation with additional where terms, ordering and grouping clauses.
I solved this problem with a mix of this answer and this other answer and came out with:
destination = Destination.where
.not(id: Adventure.where(user: user)
.pluck(:destination_id)
)
.sample
The .not(id: Adventure.where(user: user).pluck(:destination_id)) part excludes destinations present in previous adventures of the user.
The .sample part will pick a random destination from the results.
In my rails 4 application, a client (clients table) can have many projects (projects table). I have a column called name in each table. I am trying to write a join and then select which uses projects as the base table and clients as the lookup table. client_id is the foreign_key in the projects table:
I am writing my query as follows:
Project.joins(:client).select('projects.id,projects.name,clients.name')
I get the following response:
Project Load (0.6ms) SELECT projects.id,projects.name,clients.name FROM "projects" INNER JOIN "clients" ON "clients"."id" = "projects"."client_id"
=> #<ActiveRecord::Relation [#<Project id: 1, name: "Fantastico Client">]>
If I try to alias it like so:
Project.joins(:client).select('projects.id,projects.name,clients.name as client_name')
Then I get the following response:
Project Load (0.8ms) SELECT projects.id,projects.name,clients.name as client_name FROM "projects" INNER JOIN "clients" ON "clients"."id" = "projects"."client_id"
=> #<ActiveRecord::Relation [#<Project id: 1, name: "The Dream Project">]>
In either case, ActiveRecord looses one of the names as you can see from the above response. How should I be writing this query?
If the column in select is not one of the attributes of the model on which the select is called on then those columns are not displayed. All of these attributes are still contained in the objects within AR::Relation and are accessible as any other public instance attributes.
You could verify this by calling first.client_name:
Project.joins(:client)
.select('projects.id,projects.name,clients.name as client_name')
.first.client_name
You can use :'clients.name' as one of your symbols. For instance:
Project.select(:id, :name, :'clients.name').joins(:client)
I like it better because it seems like Rails understands it, since it quotes all parameters:
SELECT "projects"."id", "projects"."name", "clients"."name"
FROM "projects"
INNER JOIN "clients" ON "clients"."id" = "projects"."client_id"
(I'm not 100% sure that's the exact SQL query, but I'm fairly certain and I promise it will use "clients"."name")
To get both project table name and client name you can do like below query
Project.joins(:client).pluck(:name,:'clients.name')
your query don't looses any thing. Actually you have applied join on models and you have written Project.joins(:client) that why it is looking like.
means It will hold Project related data as it is and associated data hold with alias name that you have given 'client_name' in your query.
if you use
Project.joins(:client)
.select('projects.id project_id, projects.name projects_name,clients.name as client_name')
then it look like
[#, #]
but it hold all the attribute that you selected.
Try This:
sql = Project.joins(:client).select(:id, :name, :"clients.name AS client_name").to_sql
data = ActiveRecord::Base.connection.exec_query(sql)
OUTPUT
[
{"id"=>1, "name"=>"ProjectName1", "client_name"=>"ClientName1"},
{"id"=>2, "name"=>"ProjectName2", "client_name"=>"ClientName2"}
]
I have a has_many (through accounts) association between the model User and the model Player.
I would like to know what is the best way to query all the users, and for each returned record get the associated players usernames attributes (in comma separated values).
So, if for example, a User named 'John' is associated with 3 players with usernames 'john_black', 'john_white', 'john_yellow'. I would like the query to return not just the User attributes but also an attribute called, for example, player_username, that would have this value: john_black, john_white, john_yellow.
I have tried the following Arel query in the User model:
select(`users`.*).select("GROUP_CONCAT(`players`.`username` SEPARATOR ',') AS comma_username").joins(:user)
.joins(:accounts => :player )
Which gives me the following SQL:
SELECT `users`.*, GROUP_CONCAT(`players`.`username` SEPARATOR ',') AS comma_username FROM `users` INNER JOIN `accounts` ON `accounts`.`user_id` = `users`.`id` INNER JOIN `players` ON `players`.`id` = `accounts`.`player_id`
If I execute this in MySQL console it works, but if I try to fetch from the model, I don't get the comma separated values.
What am I missing?
I believe ActiveRecord maps all columns retrieved by the SQL query with all attributes you have on your model, which in most of the cases are exactly the same. Maybe if you create a virtual accessor on your model, ActiveRecord could map your virtual column to the virtual attribute.
class User
attr_accessible :player_username
...
Give it a try and see if this works.
So, I have this "advanced" query (not much, really) and I would like to translate it into Ruby Active Record's syntax.
SELECT microposts.*
FROM microposts
WHERE user_id IN
( SELECT r.followed_id as uid
FROM relationships r
WHERE follower_id = 1
UNION
SELECT u.id as uid
FROM users as u
WHERE id = 1
)
ORDER BY microposts.created_at DESC
The idea was to retrieve all microposts for user 1 AND user 1 followed users in desc creation order, but I really don't know how to translate this easily using Active Record's syntax.
Any thought ?
PS : As asked here is some rails context :
I have 3 models : Microposts, Users, Relationships.
Relationships is a join table handling all users relationships (follower/followed stuff).
Users have many followed_users/followers through relationships.
Users have many microhoops, and microhoops have one user.
Thanks.
No idea about Ruby but the SQL can be simplified to:
SELECT microposts.*
FROM microposts
WHERE user_id IN
( SELECT r.followed_id as uid
FROM relationships r
WHERE follower_id = 1
)
OR user_id = 1
ORDER BY microposts.created_at DESC
My answer will assume (since you've provided no ruby/rails-context outside of your raw SQL query) you have a User model, a Micropost model through relation :microposts, and a Relationship model through relation :following. User has many Micropost and Relationship instances related. You could do
u = User.find(1)
user.microposts + user.following.microposts
or you could move this into a method within Micropost
def self.own_and_following(user)
user.microposts + user.following.microposts
end
And call Micropost.own_and_following(User.find(1)).
This may not be what you're looking for, but in given the above mentioned likely relations you have in your Rails application, it sounds like something similar to this should work.
Your query is very specific, therefore your best bet would be to write a good portion of it using SQL, or try a gem like squeel that can help out generating very customized SQL from ActiveRecord.
Nevertheless, this should do the work with no additional gems :
user_id = ... #Get the user_id you want to test
Micropost.where("user_id IN
( SELECT r.followed_id as uid
FROM relationships r
WHERE follower_id = ? )
OR user_id = ?
", user_id, user_id).order("created_at desc")
I managed to do it using only where, seems a lot like a find_by_sql to me, and I don't know which one would be better :
Micropost.order('created_at DESC').
where('user_id in (select r.followed_id as uid from relationships as r where follower_id = ?) or user_id = ?', user.id, user.id)
Don't know how good this is, but it seem to be working.