I'm having trouble querying a has_many association. The context is stores.
class Store < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to: store
end
Stores table:
id name
1 Macys
2 Target
3 Dillards
Items table:
id store_id name
1 1 pants
2 1 shirt
3 2 pants
4 2 shirt
5 3 shirt
I'm trying to query for stores that only sell shirts. So I need a query that returns the store record with id of 3.
When I tried to do
Store.includes(:items).where(
items: { name: %w(shirts)} ).references(:items)
it returns store_ids 1, 2, and 3 (all stores) because they all have shirts.
I ended up using:
Store.joins(:items).group('items.store_id').having("max(items.name) =
min(items.name) and min(items.name) = 'shirt'")
Store.includes(:items)
.where(items: { name: 'shirt' })
.where.not(id:
Item.where.not(name: 'shirt').select(:store_id)
)
Hopefully there's a better way... (if anyone)
In your Item model, you need to set the counter_cache:
belongs_to :store, counter_cache: true
then your query will be:
Store.joins(:items).where('items_count = ? AND items.name = ?', 1, 'shirt')
One way of doing this as mentioned in the post that stores only having items as shirts would be:
Store.joins(:item).where("items.name = ?", 'shirt').where.not(item_name: Item.where("items.name != ?", "shirt"))
Hope it helps!!
Related
I need a help with nestjs typeorm query. Scenario is as follows
Suppose i have 3 tables say Teachers, Students and Parents
Teachers = One to many relation w.r.t Students , i.e one teacher can
teach multiple students.
Students = Many to one relation w.r.t
Teachers.
Students = One to Many w.r.t Parents , assuming 1 child
of Parents
Parents = Many to One w.r.t Students
const records = await this.createQueryBuilder('teachers').leftJoinAndSelect('teachers.students', 'students').leftJoinAndSelect('students.parents', 'parents').orderBy('teachers.id', 'DESC').limit(1);
let res = await records.getMany();
output something like this
{
teacher_id: '4',
teacher_name: 'abc',
students: [
{
'id': 1,
'name': 'pqr',
status: 'failed',
parents: [
{
id: 1,
name: 'mom',
relationship: 'mom'
},
{
id: 2,
name: 'dad',
relationship: 'dad'
}
]
}
]
}
Let us say there are 100 teachers. Each teacher has 20 students and each student has 2 parents.
Total records which the query would return = 100 * 20 * 2 = 4000
But while showing the results in a paginated way we need to group records by teacher.id.
Total count using group by teacher.id = 100
Example code -
async getTeacher(): Promise<any[]> {
return await this.repo
.createQueryBuilder('teachers')
.leftJoinAndSelect('teachers.students', 'students')
.leftJoinAndSelect('students.parents', 'parents')
.groupBy('teachers.id')
.orderBy('teachers.id','DESC')
.limit(1)
.getMany();
}
I have 2 tables named user and comments.
User Comments
id| name id| description| user_id
--------- ------------------------
1 Sam 1 Awesome dude 1
2 Dean 2 Cool 1
3 Ghost busters 2
how can i join tables to get result like the following.
{
id:1,
name: Sam
comments:[
{
id:1,
description:Awesome dude
},
{
id:2,
description:Cool
}
]
}
Try laravel ORM
Write this relationship inside your User model
protected $with = [ 'comments'];
function comments ()
{
return $this->hasMany(Comments::class, 'user_id', 'id');
}
My tables are set up such that Child has a 1:1 relationship with Parent.
They share a primary key (id):
Parent has id and type and name
Child has id and health
The Child is a polymorphic inheritance of the Parent. My goal is that Child.find(1) should return a Child object which responds to both name and health. The SQL statement would hypothetically look something like this:
SELECT parents.id, parents.name, childs.health FROM parents
LEFT JOIN childs ON childs.id = Parents.id
WHERE parents.id = 1 AND parents.type = 'Child' LIMIT 1
Thus I've attempted to use Polymorphic Inheritance in ActiveRecord:
class Parent < ApplicationRecord
class Child < Parent
When I attempt to execute Child.find(1), I see:
SELECT `parents`.* FROM `parents` WHERE `parents`.`type` IN ('Child') AND `parents`.`ID` = 1 LIMIT 1
=> #<Child id: 1, type: "Child", name: "Hello">
Notably, there's no JOIN on the child table, yet I receive a Child object back. This leads to the unexpected behavior that the Child object does not respond to health. Curiously, if I add an explicit table association to the Child class (self.table_name = "childs"), then the query pattern changes to:
> c = Child.find(1)
Obtainable Load (0.3ms) SELECT `childs`.* FROM `childs` WHERE `childs`.`ID` = 2 LIMIT 1
Now I can access the health, but not the type or name.
How can I correctly create this JOIN association such that an attempt to load a Child object successfully JOINs the data from the parent?
Edit: these tables were created outside of an ActiveRecord migration (they are also accessed by other, pre-existing, non-Ruby applications) so I have no ability to change their schema. I can think of some fancy metaprogramming approaches, like responding to method_missing, that might let me lazy-load the missing attributes through a join... but I'm afraid I'll end up having to re-implement a bunch of ActiveRecord, like delete, create, etc. (which will lead to bugs). So I'm looking for some native/clean(ish) way to accomplish this.
This is not a typical Rails polymorphic association as described here: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
So, in this case, when tables were created earlier by some other app, I suggest that you do something like this:
class Child < ApplicationRecord
self.table_name = "childs"
belongs_to :parent, foreign_key: :id, dependent: :destroy
delegate :name, :type, to: :parent
delegate :name=, to: :parent, allow_nil: true
after_initialize do
self.build_parent(type: "Child") if parent.nil?
end
end
class Parent < ApplicationRecord
self.inheritance_column = 'foo' # otherwise, type column will be used for STI
has_one :child, foreign_key: :id
delegate :health, to: :child
end
and now you can access the health, type and name:
> c = Child.joins(:parent).find(1)
Child Load (0.2ms) SELECT "childs".* FROM "childs" INNER JOIN "parents" ON "parents"."id" = "childs"."id" WHERE "childs"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Child id: 1, health: "Good", created_at: "2016-10-27 21:42:55", updated_at: "2016-10-27 21:44:08">
irb(main):002:0> c.health
=> "Good"
irb(main):003:0> c.type
Parent Load (0.1ms) SELECT "parents".* FROM "parents" WHERE "parents"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "Child"
irb(main):004:0> c.name
=> "Hello"
and similar is for the parent:
p = Parent.joins(:child).find(1)
Parent Load (0.1ms) SELECT "parents".* FROM "parents" INNER JOIN "childs" ON "childs"."id" = "parents"."id" WHERE "parents"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Parent id: 1, type: nil, name: "Hello", created_at: "2016-10-27 21:40:35", updated_at: "2016-10-27 21:40:35">
irb(main):003:0> p.name
=> "Hello"
irb(main):004:0> p.health
Child Load (0.1ms) SELECT "childs".* FROM "childs" WHERE "childs"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "Good"
If you prefer, you can specify LEFT JOIN like this:
irb(main):003:0> p = Parent.joins("LEFT JOIN childs ON (childs.id = parents.id)").select("parents.id, parents.name, childs.health").find(1)
Parent Load (0.2ms) SELECT parents.id, parents.name, childs.health FROM "parents" LEFT JOIN childs ON (childs.id = parents.id) WHERE "parents"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Parent id: 1, name: "Hello">
irb(main):004:0> p.name
=> "Hello"
irb(main):005:0> p.health
Child Load (0.1ms) SELECT "childs".* FROM "childs" WHERE "childs"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "Good"
# engine.rb
has_many :pistons
#piston.rb
belongs_to :engine
Piston has a column, piston_count and, of course, engine_id
My database has the following 7 records
Engine.all
#=> [#<Engine id: 1>, #<Engine id: 2>, #<Engine id: 3>]
Piston.all
#=> [#<Piston id: 1, engine_id: 1, piston_count: 1>, #<Piston id: 2, engine_id: 1, piston_count: 2>, #<Piston id: 2, engine_id: 2, piston_count: 1>, #<Piston id: 2, engine_id: 3, piston_count: 2>]
I want to write a query that says, return the Engine containing Pistons with a piston_count of 1 and also contains a piston_count of 2
I've tried...
engines = Engine.joins(:pistons).merge(Piston.where(piston_count: 1))
#=> [#, #]
engines.joins(:pistons).merge(Piston.where(piston_count:2))
#=> []
It returns an empty array because active record turns that into one AND clause. However, if I do an OR statement, it will return too many records. Any thoughts?
Figured it out. This takes the intersect of both Active Record Queries.
engine_ids = Engine.joins(:pistons).merge(Piston.where(piston_count: 1)).pluck(:id) & Engine.joins(:pistons).merge(Piston.where(piston_count: 2)).pluck(:id)
Then go back and retrieve all the intersects.
Engine.where(id: engine_ids)
I have two models, User and Profile.
A User has_one Profile and a Profile belongs_to User.
Correspondingly, the Profile model has a user_id attribute.
The association works:
p = Profile.first
=> #<Profile id: 1, name: "Jack", ... , user_id: 1>
u = User.first
=> #<User id: 1, email: "jack#example.com", ... >
u.profile.id
=> 1
p.user.id
=> 1
p.user == u
=> true
u.profile == p
=> true
I can set the user_id field on a Profile directly:
p.user_id = 2
=> 2
p.save!
=> true
p.user_id
=> 2
But why can I not set the user_id like this:
u.profile.user_id = 2
=> 2
u.profile.save!
=> 2
u.profile.user_id
=> 1
You must refresh u.profile object. Try this:
u.profile.user_id = 2
=> 2
u.profile.save!
=> 2
u.profile.reload.user_id
=> 2
This is because original profile object is still loaded on memory in u.
Hope this help :)