2

モデルに「メタ」情報、基本的にはユーザー定義フィールドを追加できるようにしたいと思います。たとえば、ユーザーモデルを想像してみましょう。

名、姓、年齢、性別のフィールドを定義します。

基本的にはプロフィールページにアクセスして他の情報を共有するために、ユーザーがいくつかの「メタ情報」を定義できるようにしたいと思います。したがって、あるユーザーは「趣味」、「職業」、「故郷」を追加し、別のユーザーは「趣味」、「教育」を追加したいと思うかもしれません。

だから、私はこの種のものの標準ビューを持ちたいので、例えばビューで私は(HAMLで)次のようなことをするかもしれません:

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

このようにして、すべての異なる方法でフォーマットできるマークダウンテキストボックスをユーザーに提供するだけでなく、情報が一貫して表示されるようにすることができます。

また、メタをクリックして、同じことをした他のユーザーを表示できるようにしたいので、上記の例では、両方のユーザーが「趣味」を定義しているので、趣味を共有している-またはさらに良いのは、趣味が___であるユーザーを見たいです

では、ユーザーがどのフィールドを事前に定義したいかわからないので、そのような機能を提供するためにどのようなオプションがありますか?

このようなモデル、または少なくとも同様のモデルでカスタムメタ情報を処理するgemはありますか?誰かがこの種の問題を経験したことがありますか?もしそうなら、どのようにそれを解決しましたか?

ありがとう!

4

3 に答える 3

8

動的フィールドの実装は、次の要因に依存します。

  1. 属性を動的に追加する機能
  2. 新しいデータ型をサポートする機能
  3. 追加のクエリなしで動的属性を取得する機能
  4. 通常の属性などの動的属性にアクセスする機能
  5. 動的属性に基づいてオブジェクトを照会する機能。(例:スキーの趣味を持つユーザーを見つける)

通常、ソリューションはすべての要件に対応しているわけではありません。マイクのソリューションは、1と5をエレガントに扱います。1と5が重要な場合は、彼のソリューションを使用する必要があります。

これは、1、2、3、4、および5に対処する長いソリューションです。

usersテーブルを更新します

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

5番目の要件に対処するには、SolrまたはSphinxの全文検索エンジンを使用する必要があります。LIKEクエリをDBに依存するよりも効率的です。

Sunspotgemを介してSolrを使用する場合の1つのアプローチを次に示します。

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

各ユーザーが独自の属性を定義できる場合、1つのオプションは、user_id、attribute_name、attribute_valueの3つの列を持つテーブルを持つことです。次のようになります。

| 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よりもはるかに簡単にこのタイプのシナリオを処理できます。

そうでない場合、私はおそらくマイクA.が説明したことに沿って何かをすることになるでしょう。

于 2011-03-17T04:50:16.770 に答える