重新思考 MongoDB 的多对多关系

发布于 2024-09-10 06:34:56 字数 792 浏览 9 评论 0原文

我刚刚开始一个使用 Mongoid ORM for MongoDB 的新 Rails 3 项目。只有一件事我无法理解,那就是如何有效地建立多对多关系。现在我很可能错误地处理了这个问题,但据我所知,我的项目中至少有两个容器需要多对多关系。我更愿意将这两个模型视为“一流”模型,并为每个模型分配自己的容器。

这是我能想到的构建多对多关系的最简单方法:

// Javascript pseudo modeling
// -------------------- Apps
{ 
  app: {
    _id: "app1",
    name: "A",
    event_ids: ["event1","event2"]
  }
}

{ 
  app: {
    _id: "app2",
    name: "B",
    event_ids: ["event1"]
  }
}

// -------------------- Events

{
  event: {
    _id: "event1",
    name: "Event 1",
  }
}

{
  event: {
    _id: "event2",
    name: "Event 2",
  }
}

据我所知,这是推断多对多关系所需的最小信息量。我的假设是,我可能必须有一个地图缩减程序来确定哪些应用程序属于某个事件。如果将应用程序添加到事件模型或从事件模型中删除,我还必须在 Event 上编写提交/保存后挂钩来更新 App.event_ids 。

我走在正确的轨道上吗?如果有人有任何可以运行多对多关系的 Mongoid 或 Mongomapper 代码示例,请分享一下。

I am just starting a new Rails 3 project using Mongoid ORM for MongoDB. There is just one thing I can not get my head around, and that is how to effectively have a many-to-many relationship. Now there is a good chance that I may be approaching this problem wrong, but as far as I know, there is at least two containers in my project that need a many-to-many relationship. I would prefer to treat both models as "first class" models and allocate each with its own container.

This is the simplest way I can think to structure my many-to-many relationship:

// Javascript pseudo modeling
// -------------------- Apps
{ 
  app: {
    _id: "app1",
    name: "A",
    event_ids: ["event1","event2"]
  }
}

{ 
  app: {
    _id: "app2",
    name: "B",
    event_ids: ["event1"]
  }
}

// -------------------- Events

{
  event: {
    _id: "event1",
    name: "Event 1",
  }
}

{
  event: {
    _id: "event2",
    name: "Event 2",
  }
}

As far as I can tell this is the minimum amount of information need to infer a many-to-many relationship. My assumption is that I might have to have a map reduce procedure to determine what apps belong to an event. I would also have to write post commit/save hooks on Event to update App.event_ids if an app is added to or removed from an event model.

Am I on the right track here? If someone has any Mongoid or Mongomapper code examples of a many-to-many relationship working, could you please share.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

放肆 2024-09-17 06:34:56

您的结构可以正常工作,并且不需要 MapReduce 函数来确定哪些应用程序属于事件。您可以根据 eventid 查询应用程序集合。您可以索引字段collection.event_ids。

如果您不想根据事件 ID 而是根据事件名称搜索应用程序,则需要将该事件名称添加到应用程序集合中(非规范化)。这意味着当事件名称更改时,您还必须更新应用程序集合。不知道这样的情况是不是经常发生?

使用 MongoDB 时,您经常必须进行非规范化,因此您不会存储最少量的信息,而是将某些内容存储“两次”。

Your structure can work and you don't need a mapreduce function to determine what apps belong to an event. You can query the app collection on an eventid. You can index field collection.event_ids.

If you don't want to search apps on an eventid but on a event name, you will need to add that event name to the app collection (denormalization). That means that you also have to update the app collection when the name of an event changes. I don't know if that happens very often?

You often have to denormalize when you use MongoDB, so you don't store the minimal amount of information but you store some things "twice".

沧笙踏歌 2024-09-17 06:34:56

我能够使用 Mongoid 来实现这个设计。我编写了广泛的测试,并且能够让我的解决方案发挥作用;然而,我对我的实施并不满意。我相信我的实施将很难维护。

我在这里发布我的不优雅的解决方案。希望这将帮助某人开始更好的实施。

class App
  include Mongoid::Document
  field :name

  references_one :account
  references_many :events, :stored_as => :array, :inverse_of => :apps

  validates_presence_of :name
end

class Event
  include Mongoid::Document
  field :name, :type => String

  references_one :account

  validates_presence_of :name, :account

  before_destroy :remove_app_associations

  def apps
    App.where(:event_ids => self.id).to_a
  end

  def apps= app_array
    unless app_array.kind_of?(Array)
      app_array = [app_array]
    end
    # disassociate existing apps that are not included in app_array
    disassociate_apps App.where(:event_ids => self.id).excludes(:id => app_array.map(&:id)).to_a
    # find existing app relationship ids
    existing_relationship_ids = App.where(:event_ids => self.id, :only => [:id]).map(&:id)
    # filter out existing relationship ids before making the new relationship to app
    push_apps app_array.reject { |app| existing_relationship_ids.include?(app.id) }
  end

  def push_app app
    unless app.event_ids.include?(self.id)
      app.event_ids << self.id
      app.save!
    end
  end

  def disassociate_app app
    if app.event_ids.include?(self.id)
      app.event_ids -= [self.id]
      app.save!
    end
  end

  def push_apps app_array
    app_array.each { |app| push_app(app) }
  end

  def disassociate_apps app_array
    app_array.each { |app| disassociate_app(app) }
  end

  def remove_app_associations
    disassociate_apps apps
  end

end

I was able to implement this design using Mongoid. I wrote extensive tests and I was able to get my solution working; however, I am not satisfied with my implementation. I believe that my implementation would be a difficult to maintain.

I'm posting my non-elegant solution here. Hopefully, this will help someone with the start of a better implementation.

class App
  include Mongoid::Document
  field :name

  references_one :account
  references_many :events, :stored_as => :array, :inverse_of => :apps

  validates_presence_of :name
end

class Event
  include Mongoid::Document
  field :name, :type => String

  references_one :account

  validates_presence_of :name, :account

  before_destroy :remove_app_associations

  def apps
    App.where(:event_ids => self.id).to_a
  end

  def apps= app_array
    unless app_array.kind_of?(Array)
      app_array = [app_array]
    end
    # disassociate existing apps that are not included in app_array
    disassociate_apps App.where(:event_ids => self.id).excludes(:id => app_array.map(&:id)).to_a
    # find existing app relationship ids
    existing_relationship_ids = App.where(:event_ids => self.id, :only => [:id]).map(&:id)
    # filter out existing relationship ids before making the new relationship to app
    push_apps app_array.reject { |app| existing_relationship_ids.include?(app.id) }
  end

  def push_app app
    unless app.event_ids.include?(self.id)
      app.event_ids << self.id
      app.save!
    end
  end

  def disassociate_app app
    if app.event_ids.include?(self.id)
      app.event_ids -= [self.id]
      app.save!
    end
  end

  def push_apps app_array
    app_array.each { |app| push_app(app) }
  end

  def disassociate_apps app_array
    app_array.each { |app| disassociate_app(app) }
  end

  def remove_app_associations
    disassociate_apps apps
  end

end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文