Rails自定义元模型?

时间:2011-03-10 03:32:59

标签: ruby-on-rails ruby-on-rails-3 metamodel

我希望能够将“元”信息添加到模型中,基本上是用户定义的字段。因此,举例来说,让我们想象一下用户模型:

我定义名字,姓氏,年龄,性别的字段。

我希望用户能够定义一些“元信息”,基本上可以进入他们的个人资料页面并分享其他信息。因此,一个用户可能想要添加“爱好”,“职业”和“家乡”,而另一个用户可能想要添加“爱好”和“教育”。

所以,我希望能够为这种东西提供标准视图,所以例如在视图中我可以做一些像(在HAML中)的东西:

- for item in @meta
  %li
    %strong= item.key + ":"
    = item.value

通过这种方式,我可以确保始终如一地显示信息,而不仅仅是向用户提供可以以不同方式格式化的降价文本框。

我也希望能够点击meta并查看已经给出相同内容的其他用户,因此在上面的示例中,两个用户都定义了“爱好”,能够说我想要看到有共享兴趣爱好的用户 - 或者甚至更好,我希望看到其兴趣爱好 _ __ 的用户。

所以,既然我不知道用户想要提前定义哪些字段,那么提供这种功能有哪些选择呢?

是否有一个gem可以在这样的模型上处理自定义元信息,或者至少有类似的东西?有没有人遇到过这种问题?如果是这样,你是如何解决的?

谢谢!

3 个答案:

答案 0 :(得分:7)

动态字段实现取决于以下因素:

  1. 能够动态添加属性
  2. 支持新数据类型的能力
  3. 无需其他查询即可检索动态属性
  4. 能够访问常规属性等动态属性
  5. 能够根据动态属性查询对象。 (例如:找到用户 滑雪爱好)
  6. 通常,解决方案无法满足所有要求。迈克的解决方案优雅地解决了1和5问题。你应该使用他的解决方案1& 5对你很重要。

    这是一个解决1,2,3,4和5

    的长解决方案

    更新users表格

    将名为text的{​​{1}}字段添加到users表。

    更新您的meta型号

    User

    添加新的元字段

    class User < ActiveRecord::Base  
      serialize :meta, Hash  
    
      def after_initialize
        self.meta ||= {} if new_record?
      end
    
    end
    

    访问元字段

    u = User.first
    u.meta[:hobbies] = "skiing"
    u.save
    

    迭代元字段

    puts "hobbies=#{u.meta[:hobbies]}"
    

    要满足第5项要求,您需要使用Solr Or Sphinx全文搜索引擎。它们比依赖数据库进行u.meta.each do |k, v| puts "#{k}=#{v}" end 查询更有效。

    如果您通过Sunspot gem使用Solr,这是一种方法。

    LIKE

    查找类似用户

    class User    
      searchable do
        integer(:user_id, :using => :id)
        meta.each do |key, value|
          t = solr_type(value)
          send(t, key.to_sym) {value} if t
        end
      end
    
      def solr_type(value)
        return nil      if value.nil?
        return :integer if value.is_a?(Fixnum)
        return :float   if value.is_a?(Float)
        return :string  if value.is_a?(String)
        return :date    if value.is_a?(Date)
        return :time    if value.is_a?(Time)
      end    
    
      def similar_users(*args)
        keys = args.empty? ? meta.keys : [args].flatten.compact
        User.search do
          without(:user_id, id)
          any_of do
            keys.each do |key|
              value = meta[key]
              with(key, value) if value
            end
          and
        end
      end
    end
    

    此处的性能提升非常重要。

答案 1 :(得分:3)

如果允许每个用户定义自己的属性,则一个选项可能是具有三列的表:user_id,attribute_name,attribute_value。它可能看起来像:

| user_id | attribute_name | attribute_value |
| 2       | hobbies        | skiing          |
| 2       | hobbies        | running         |
| 2       | pets           | dog             |
| 3       | hobbies        | skiing          |
| 3       | colours        | green           |

此表将用于查找具有相同爱好/宠物/等的其他用户。

出于性能原因(此表会变得很大),您可能希望维护信息存储的多个位置 - 用于不同目的的不同信息源。如果性能绝对必要,我不认为将相同的信息存储在多个表中是不好的。

这完全取决于您需要什么功能。也许最终会让每个用户将其键/值对序列化到users表的字符串列中(Rails为这种类型的序列化提供了很好的支持),所以当你为特定用户显示信息时你不会甚至需要触摸巨大的桌子。或许你最终会得到另一张看起来像这样的表:

| user_id | keys             | values               |
| 2       | hobbies, pets    | skiing, running, dog |
| 3       | hobbies, colours | skiing, green        |

如果您需要查找所有具有爱好的用户(针对keys列运行LIKE sql),或者与狗有任何关系的所有用户(对值列运行LIKE sql),此表将非常有用。< / p>

这是我能给出的最佳答案。也许有第三方解决方案可用,但我持怀疑态度。它并不是真正的“弹出宝石”类型的问题。

答案 2 :(得分:2)

在这种情况下,我至少会考虑像mongo或couch这样的documentdb,它可以比rdms更容易处理这种情况。

如果情况并非如此,我可能最终会按照Mike A.描述的方式做一些事情。