好的,所以我是ruby和rails的新手,它可能会显示我的问题。
我正在写一个有趣的应用程序,并且在查找HABTM关系中的记录方面有一个特别奇怪的问题。底层数据库是Postgresql
我的模型如下所示
class Family < ActiveRecord::Base
has_and_belongs_to_many :persons
end
class Person < ActiveRecord::Base
has_and_belongs_to_many :families
end
让我们说我有4个人&#39; Joe&#39;&#39; Jane&#39;&#39; Mary&#39; Ben&#39; Ben&#39;他们都属于多个家庭。
Family 1 << 'Joe', 'Jane', 'Ben'
Family 2 << 'Jane', 'Ben'
Family 3 << 'Joe', 'Jane', 'Ben', 'Mary'
我希望能够通过搜索他们的姓名找到家人
这是我的查询目前的样子
Family.joins(:persons).where(persons: {name:['Joe','Jane','Ben']})
这非常适合查找所有包含Joe或Jane或Ben(所有系列)的记录,但不能返回仅包含Joe,Jane和Ben的记录。
在我的例子中,我希望只找到1号家庭,而不是其他2号。
如何确保我只查找具有所有名称的记录,不多也不少。
是否有更好的查询或我应该重新考虑我的数据库结构?
答案 0 :(得分:0)
我会以这种方式设置模型:
class Family < ActiveRecord::Base
has_many :persons
end
class Person < ActiveRecord::Base
belongs_to :family
end
然后将family_id添加到家庭表。
现在您可以致电family.first.persons
或者
Family.where(id: 1).first.persons
此外,如果您认为此方法不合适,我建议您使用has_many :through
代替has_many_and_belongs_to
以下是更多信息: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
答案 1 :(得分:0)
因此,经过对#rubyonrails irc的一些挖掘和一些帮助,我想出了这个解决方案。
class Family < ActiveRecord::Base
def self.find_by_person_names(persons = [])
family = nil
families = Family.joins(:persons).where(persons: {name:persons})
families.each do |f|
if f.persons.names.sort == persons.sort
family = f
end
end
return family
end
end
答案 2 :(得分:0)
我还认为has_many通过关系更合适:has_and_belongs_to_many vs has_many through
这是一个不需要在ruby中过滤结果的选项:
FamilyPerson
个对象FamilyPerson
个对象Family
个对象而不是第二个 - 也就是那些拥有所有必需人员且不再包含其他内容的家庭class Family < ActiveRecord::Base
has_many :family_people
has_many :people, through: :family_people
def self.all_with_members members
member_ids = members.collect {|m| m.id }
# FamilyPersons that have each of the members in it
enough = FamilyPerson.where(person_id: member_ids).group("family_id").having("count(person_id) >= ?", member_ids.count).collect { |fp| fp.family_id }
# FamilyPersons that have no additional members
too_many = FamilyPerson.where("person_id NOT IN(?)", member_ids).group("family_id").having("count(person_id) > 0").collect { |fp| fp.family_id }
Family.find enough - too_many
end
end
class Person < ActiveRecord::Base
has_many :family_people
has_many :families, through: :family_people
end
class FamilyPerson < ActiveRecord::Base
belongs_to :family
belongs_to :person
end
create_table "families", force: true do |t|
t.string "name"
end
create_table "family_people", force: true do |t|
t.integer "person_id"
t.integer "family_id"
end
create_table "people", force: true do |t|
t.string "name"
end
使用RSpec进行测试
require 'rails_helper'
describe Family do
before :each do
Rails.application.load_seed
end
describe "self.all_with_members" do
it "returns all families that have each person in it" do
people = [Person.find_by_name("Joe"), Person.find_by_name("Jane"), Person.find_by_name("Ben")]
expect(Family.all_with_members(people).to_a).to eq Family.where(name: "Family 1").to_a
end
end
end
种子数据
Family.destroy_all
Person.destroy_all
FamilyPerson.destroy_all
joe = Person.create name: "Joe"
jane = Person.create name: "Jane"
ben = Person.create name: "Ben"
mary = Person.create name: "Mary"
family_one = Family.create name: "Family 1"
family_two = Family.create name: "Family 2"
family_three = Family.create name: "Family 3"
family_one.people << joe
family_one.people << jane
family_one.people << ben
family_one.save!
family_two.people << jane
family_two.people << ben
family_two.people << mary
family_two.save!
family_three.people << joe
family_three.people << jane
family_three.people << ben
family_three.people << mary
family_three.save!