2

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

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

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

所以,我希望能够对这类东西有一个标准的视图,所以例如在视图中我可能会做类似(在 HAML 中)的事情:

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

通过这种方式,我可以确保信息始终如一地显示,而不是仅仅为用户提供一个 markdown 文本框,他们可以以各种不同的方式进行格式化。

我也希望能够点击 meta 并查看其他用户提供了相同的内容,因此在上面的示例中,两个用户都定义了“爱好”,如果能够说我想查看有共同的爱好——甚至更好的是,我希望看到爱好是_ __的用户。

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

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

谢谢!

4

3 回答 3

8

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

  1. 能够动态添加属性
  2. 支持新数据类型的能力
  3. 无需额外查询即可检索动态属性
  4. 能够访问像常规属性一样的动态属性
  5. 能力根据动态属性查询对象。(例如:找到有滑雪爱好的用户)

通常,解决方案不能满足所有要求。Mike 的解决方案优雅地解决了 1 和 5。如果 1 和 5 对您很重要,您应该使用他的解决方案。

这是一个解决 1、2、3、4 和 5 的长解决方案

更新users表格

添加一个text名为metausers 表的字段。

更新您的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]}"

迭代元字段

u.meta.each do |k, v|
  puts "#{k}=#{v}"
end

要解决第 5 个要求,您需要使用 Solr 或 Sphinx 全文搜索引擎。它们比依赖数据库进行LIKE查询更有效。

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

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

查找相似用户

u = User.first
u.similar_users # matching any one of the meta fields
u.similar_users :hobbies # with matching hobbies
u.similar_users :hobbies, :city # with matching hobbies or the same city

这里的性能提升是显着的。

于 2011-03-20T02:38:45.720 回答
4

如果允许每个用户定义自己的属性,一个选项可能是拥有一个包含三列的表: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           |

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

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

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

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

如果您需要查找所有有爱好的用户(对 keys 列运行 LIKE sql)或所有与狗有任何关系的用户(对 values 列运行 LIKE sql),此表将很有用。

这是我可以根据您给出的要求给出的最佳答案。也许有可用的第三方解决方案,但我持怀疑态度。这并不是真正的“宝石流行”类型的问题。

于 2011-03-17T04:45:19.720 回答
2

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

如果不是这样,我可能最终会按照 Mike A. 所描述的方式做一些事情。

于 2011-03-17T04:50:16.770 回答