Ruby on Rails查询未归档关联的ActiveRecord scope编写问题
问题排查与修复
1. 测试用例的核心错误
你定义的scope属于User模型,返回结果是User对象的集合,但你的断言里尝试将PotentialMatch类实例(pot_match1、pot_match2、pot_match3)和User集合做匹配,两类对象类型完全不匹配,这是测试不通过的最直接原因。
正确的测试断言应该判断返回的User集合中包含关联了未归档匹配的user1、user2,不包含仅关联归档匹配的user3、user4:
expect(pot_matches).to include(user1, user2) expect(pot_matches).not_to include(user3, user4)
2. Scope本身的逻辑问题
2.1 LEFT JOIN的使用冗余
你使用了LEFT JOIN关联表,但后续加了potential_matches.archived = false的过滤条件,会直接过滤掉所有没有匹配到未归档potential_match的用户,实际效果和INNER JOIN完全一致,直接改用INNER JOIN语义更清晰。
2.2 缺少去重逻辑
如果一个用户同时作为mentor和mentee关联了多个未归档的potential_match,查询结果会返回多条同一个用户的重复记录,需要加distinct去重。
2.3 手写SQL条件兼容性差
直接手写SQL字符串判断布尔值,在不同数据库下可能存在兼容问题,改用Rails哈希形式的where条件更稳妥。
修正后的代码
修正后的Scope
scope :potential_matches, -> { joins('INNER JOIN potential_matches ON potential_matches.mentor_id = users.id OR potential_matches.mentee_id = users.id') .where(potential_matches: { archived: false }) .where(users: { active: true }) .distinct }
修正后的测试用例
describe '.potential_matches' do it 'returns only active users associated with unarchived potential matches' do # 主动指定用户活跃状态,避免工厂默认值干扰测试 user1 = FactoryBot.create(:user, active: true) user2 = FactoryBot.create(:user, active: true) user3 = FactoryBot.create(:user, active: true) user4 = FactoryBot.create(:user, active: true) # 补充非活跃用户测试场景 inactive_user = FactoryBot.create(:user, active: false) pot_match1 = FactoryBot.create(:potential_match, mentor_id: user1.id, mentee_id: user2.id) pot_match2 = FactoryBot.create(:potential_match, mentor_id: user2.id, mentee_id: user1.id) pot_match3 = FactoryBot.create(:potential_match, mentor_id: user3.id, mentee_id: user4.id, archived: true) pot_match4 = FactoryBot.create(:potential_match, mentor_id: inactive_user.id, mentee_id: user1.id) pot_matches = User.potential_matches # 用contain_exactly更严谨的校验返回结果正好符合预期,无多余/缺失数据 expect(pot_matches).to contain_exactly(user1, user2) end end
内容的提问来源于stack exchange,提问作者Ilovebathroomlights
相关产品推荐
相关产品推荐

