如何实现has_many:通过与Mongoid和mongodb的关系?

使用Rails指南中的这个修改过的示例,如何使用mongoidbuild模关系“has_many:through”关联?

挑战是,mongoid不支持has_many:通过ActiveRecord。

# doctor checking out patient class Physician < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointments has_many :meeting_notes, :through => :appointments end # notes taken during the appointment class MeetingNote < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointments has_many :physicians, :through => :appointments end # the patient class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, :through => :appointments has_many :meeting_notes, :through => :appointments end # the appointment class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patient belongs_to :meeting_note # has timestamp attribute end 

Mongoid没有has_many:通过或相当的function。 这对于MongoDB来说并不是那么有用,因为它不支持连接查询,所以即使你可以通过另外一个引用相关集合,它仍然需要多个查询。

https://github.com/mongoid/mongoid/issues/544

通常情况下,如果你在RDBMS中有许多关系,你可以在MongoDB中使用包含“foreign”键数组的字段对其进行build模。 例如:

 class Physician include Mongoid::Document has_and_belongs_to_many :patients end class Patient include Mongoid::Document has_and_belongs_to_many :physicians end 

换句话说,你会消除连接表,它将有一个类似的影响has_many:通过访问“对方”。 但在你的情况下,这可能不合适,因为你的连接表是一个约会类,它携带一些额外的信息,而不仅仅是关联。

你如何build模这在一定程度上取决于你需要运行的查询,但好像你需要添加Appointment模型并定义与Patient和Physician的关联,如下所示:

 class Physician include Mongoid::Document has_many :appointments end class Appointment include Mongoid::Document belongs_to :physician belongs_to :patient end class Patient include Mongoid::Document has_many :appointments end 

通过MongoDB中的关系,您必须在embedded或关联的文档之间进行select。 在你的模型中,我猜想MeetingNotes是embedded式关系的一个很好的候选者。

 class Appointment include Mongoid::Document embeds_many :meeting_notes end class MeetingNote include Mongoid::Document embedded_in :appointment end 

这意味着您可以一起检索笔记和约会,而如果这是一个关联,则需要多个查询。 如果您有大量的会议logging,您只需要记住单个文档的16MB大小限制。

为了扩展这个,下面的模型扩展了与has_many非常相似的方法:通过从ActiveRecord通过返回一个查询代理而不是一个logging数组:

 class Physician include Mongoid::Document has_many :appointments def patients Patient.in(id: appointments.pluck(:patient_id)) end end class Appointment include Mongoid::Document belongs_to :physician belongs_to :patient end class Patient include Mongoid::Document has_many :appointments def physicians Physician.in(id: appointments.pluck(:physician_id)) end end 

史蒂文Soroka解决scheme真的很棒! 我没有评论一个答案的声望(这就是为什么我要添加一个新的答案:P),但我认为使用地图的关系是昂贵的(特别是如果你的has_many关系有h |数千logging),因为它得到来自数据库的数据,构build每个logging,生成原始数组,然后迭代原始数组,以使用给定块中的值构build新数据。

使用采摘更快,也许是最快的select。

 class Physician include Mongoid::Document has_many :appointments def patients Patient.in(id: appointments.pluck(:patient_id)) end end class Appointment include Mongoid::Document belongs_to :physician belongs_to :patient end class Patient include Mongoid::Document has_many :appointments def physicians Physician.in(id: appointments.pluck(:physician_id)) end end 

这里有Benchmark.measure的一些数据:

 > Benchmark.measure { physician.appointments.map(&:patient_id) } => #<Benchmark::Tms:0xb671654 @label="", @real=0.114643818, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=0.06999999999999984, @total=0.07999999999999985> > Benchmark.measure { physician.appointments.pluck(:patient_id) } => #<Benchmark::Tms:0xb6f4054 @label="", @real=0.033517774, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.0, @total=0.0> 

我只用了250个约会。 不要忘记在约会文档中添加索引:patient_id和:physician_id!

我希望它有帮助,感谢阅读!