我可以有单向的HABTM关系吗?

时间:2014-01-09 16:45:46

标签: ruby-on-rails activerecord

假设我有一个具有一个Foo和多个Bars的模型Item。

Foo和Bar可以在搜索项目时用作参数,因此可以像这样搜索项目:

www.example.com/search?foo=foovalue&bar[]=barvalue1&bar[]=barvalue2

我需要生成一个能够保存这些搜索参数的Query对象。我需要以下关系:

  • 查询需要访问一个Foo和多个Bars。
  • 许多不同的查询都可以访问One Foo。
  • 可以通过许多不同的查询访问一个栏。
  • Bar和Foo都不需要了解有关Query的任何信息。

我现在建立了这种关系:

class Query < ActiveRecord::Base
  belongs_to :foo
  has_and_belongs_to_many :bars
  ...
end

Query还有一个方法,它返回一个这样的哈希:{ foo: 'foovalue', bars: [ 'barvalue1', 'barvalue2' },它可以很容易地将这些值传递给url帮助器并生成搜索查询。

一切正常。

我的问题是,这是否是建立这种关系的最佳方式。我还没有看到任何其他单向HABTM关系的例子,所以我想我可能在这里做错了。

这是HABTM的可接受用途吗?

1 个答案:

答案 0 :(得分:3)

功能上是,但语义上没有。以“片面”方式使用HABTM将实现您想要的。不幸的是,HABTM的名称暗示了一种并非总是如此的互惠关系。同样地,belongs_to :foo在这里没有什么直观意义。

不要陷入HABTM和其他关联的语义,而只是考虑您的ID需要坐在哪里才能正确有效地查询数据。请记住,效率考虑首先应考虑到您的工作效率。

我将冒昧地创造一个比你的泡沫和酒吧更具体的例子...说我们有一个引擎,可以让我们查询某个池塘中是否存在某些鸭子,我们想跟踪这些问题。

<强>可能性

在查询记录中存储鸭子有三种选择:

  1. 加入表格
  2. 本地鸭子数组
  3. 鸭子ids的序列化数组
  4. 您自己已经回答了联接表用例,如果“[Duck]或[Pond]都不需要了解有关Query的任何内容”,那么使用单向关联应该会导致您没有问题。您需要做的就是创建一个ducks_queries表,ActiveRecord将提供其余的表。如果您需要做任何花哨的事情,您甚至可以选择使用has_many :through关系。

    有时数组比使用连接表更方便。您可以将数据存储为serialized整数数组,并添加处理程序以访问类似于以下内容的数据:

    class Query
     serialize :duck_ids
     def ducks
       transaction do 
         Duck.where(id: duck_ids)
       end
     end
    end
    

    如果您的数据库中有本机阵列支持,则可以在数据库中执行相同操作。类似。

    使用Postgres的本机阵列支持,您可以进行如下查询:

    SELECT * FROM ducks WHERE id=ANY(
      (SELECT duck_ids FROM queries WHERE id=1 LIMIT 1)::int[]
    )
    

    您可以在SQL Fiddle

    上播放上述示例

    权衡

    1. 加入表格:
      • 优点:约定配置;您可以获得开箱即用的所有Rails礼物(例如query.barsquery.bars=query.bars.where()
      • 缺点:您为数据层添加了复杂性(即另一个表,更密集的查询);没什么直觉感觉
    2. 原生数组:
      • 优点:语义上很好;你可以获得所有与数据库相关的数组相关的好东西;可能性能更高
      • 缺点:您必须使用自己的Ruby / SQL或使用ActiveRecord扩展名,例如postgres_ext;不是DB不可知论者;再见Rails好东西
    3. 序列化数组:
      • 优点:语义上很好;数据库不可知
      • 缺点:你必须推出自己的Ruby;你将无法直接通过你的数据库进行某些查询;序列化是icky;再见Rails好东西
    4. 在一天结束时,您的用例会有所不同。除此之外,我会说你应该坚持你的“片面”HABTM实施:否则你会丢失很多Rails赠送的礼物。