2011-03-10 62 views
2

我希望能夠將「元」信息添加到模型,基本上是用戶定義的字段。因此,例如,讓我們來想象一個用戶模型:Rails自定義元模型?

我爲名字,姓氏,年齡,性別定義了字段。

我希望用戶能夠定義一些「元信息」,基本上可以進入他們的個人資料頁面並共享其他信息。因此,一個用戶可能想添加「興趣愛好」,「職業」和「家鄉」,另一個用戶可能想要添加「興趣愛好」和「教育」。

所以,我希望能夠有這種東西標準視圖,因此,例如,在視圖中我可能會做這樣的事情(在HAML):

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

這樣我可以確保信息始終如一顯示,而不僅僅是向用戶提供降價文本框,以便可以對所有不同的方式進行格式化。

我也希望能夠點擊元,並看到其他用戶誰給了同樣的事情,所以在上面的例子中,用戶定義「興趣愛好」,這將是很高興能夠說我想看到有興趣愛好的用戶 - 甚至更好,我希望看到用戶的興趣愛好是_ __

因此,由於我不知道用戶需要事先定義哪些字段,因此提供這種功能有哪些選擇?

是否有像這樣的模型處理自定義元信息的寶石,或者至少類似?有沒有人有過這種問題的經驗?如果是這樣,你是如何解決它的?

謝謝!

回答

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   | 

此表將被用於尋找誰有着相同的愛好/寵物/等其他用戶。

由於性能方面的原因(此表格將變大),您可能需要維護多個信息存儲位置 - 用於不同目的的不同信息來源。如果性能絕對必要,我不認爲將相同的信息存儲在多個表中是不好的。

這一切都取決於你需要什麼功能。也許最終會讓每個用戶都有他們的鍵/值對序列化到用戶表中的字符串列(Rails爲這種類型的序列化提供很好的支持),所以當你顯示某個特定用戶的信息時,你不需要甚至需要觸摸巨大的桌子。或者,也許你會最終有另一個表看起來像這樣:

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

,如果你需要找到一個有自己的愛好(運行像對鍵列SQL)的所有用戶,或所有用戶該表將是有用的與狗有什麼關係(在值欄中運行LIKE sql)。

這是我可以給你的要求給出的最佳答案。也許有第三方解決方案可用,但我很懷疑。這不是真正的「彈出一個寶石」類型的問題。

+0

這聽起來像是一個非常好的解決方案。謝謝! – Andrew 2011-03-17 21:25:13

2

在這種情況下,我至少會考慮像mongo或沙發這樣的documentdb,它可以比rdms更容易處理這種類型的場景。

如果情況並非如此,那麼我最終可能會按照Mike A.所描述的方式做一些事情。

7

動態現場實施取決於以下幾個因素:

  1. 能夠動態地添加屬性
  2. 能夠支持新的數據類型
  3. 能力來獲取動態屬性,無需額外的查詢
  4. 能力訪問像常規屬性那樣的動態屬性
  5. 能夠根據動態屬性查詢對象。 (例如:找到具有 滑雪愛好的用戶)

通常情況下,解決方案不能滿足所有要求。 Mike的解決方案優雅地解決了1和5。你應該使用他的解決方案,如果你對你很重要。

這是一個長期的解決方案,可以滿足1,2,3,4和5

更新users

添加text場叫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]}" 

迭代元字段

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

爲了解決您需要使用Solr的還是獅身人面像的全文搜索引擎,第5的要求。它們比依賴於LIKE查詢的DB更有效率。

如果您通過太陽黑子寶石使用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 

的性能增益這裏是顯著。

+0

這是一個非常好的方法,我非常感謝你分享它! – Andrew 2011-03-21 13:49:21