ActiveRecord通过需要所有属性的关系查找记录

时间:2015-02-27 03:06:21

标签: ruby-on-rails ruby postgresql activerecord

好的,所以我是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号。

如何确保我只查找具有所有名称的记录,不多也不少。

是否有更好的查询或我应该重新考虑我的数据库结构?

3 个答案:

答案 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中过滤结果的选项:

  1. 选择所有包含所需人员的FamilyPerson个对象
  2. 选择包含所需人员之外的所有FamilyPerson个对象
  3. 在第一个查询结果中选择包含ID的所有Family个对象而不是第二个 - 也就是那些拥有所有必需人员且不再包含其他内容的家庭
  4. 实施

    应用程序/模型/ family.rb

    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
    

    应用程序/模型/ person.rb

    class Person < ActiveRecord::Base
      has_many :family_people
      has_many :families, through: :family_people
    end  
    

    应用程序/模型/ family_person.rb

    class FamilyPerson < ActiveRecord::Base
      belongs_to :family
      belongs_to :person
    end
    

    摘自db / schema.rb

    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
    

    测试

    规格/模型/ family_spec.rb

    使用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
    

    分贝/ seeds.rb

    种子数据

    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!