動的フィールドの実装は、次の要因に依存します。
- 属性を動的に追加する機能
- 新しいデータ型をサポートする機能
- 追加のクエリなしで動的属性を取得する機能
- 通常の属性などの動的属性にアクセスする機能
- 動的属性に基づいてオブジェクトを照会する機能。(例:スキーの趣味を持つユーザーを見つける)
通常、ソリューションはすべての要件に対応しているわけではありません。マイクのソリューションは、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
ここでのパフォーマンスの向上は重要です。