Пользовательская метамодель Rails? - PullRequest
2 голосов
/ 10 марта 2011

Я хотел бы иметь возможность добавлять метаинформацию в модель, в основном определяемые пользователем поля.Так, например, давайте представим модель пользователя:

Я определяю поля для имени, фамилии, возраста, пола.

Я бы хотел, чтобы пользователи могли определять некоторую "метаинформацию"", в основном, чтобы перейти на страницу своего профиля и поделиться другой информацией.Поэтому один пользователь может захотеть добавить «хобби», «род занятий» и «родной город», а другой может захотеть добавить «хобби» и «образование».

Итак, я бы хотел иметь возможностьчтобы иметь стандартное представление для такого рода вещей, так, например, в представлении я мог бы сделать что-то вроде (в HAML):

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

Таким образом, я могу гарантировать, что информация отображается последовательно, а непросто предоставив пользователю текстовое поле для разметки, которое он может отформатировать различными способами.

Я также хотел бы иметь возможность щелкнуть мета и увидеть других пользователей, которые дали то же самое, так что в примере вышеоба пользователя определили «хобби», было бы неплохо иметь возможность сказать, что я хочу видеть пользователей, у которых есть общие хобби, или, что еще лучше, я хочу видеть пользователей, у которых хобби _ __ .

Итак, поскольку я не знаю, какие поля пользователи захотят определить заранее, какие существуют варианты для предоставления такой функциональности?

Есть ли драгоценный каменькоторый обрабатывает пользовательские мета яИнформация о модели, подобной этой, или, по крайней мере, в некотором роде?Кто-нибудь имел опыт работы с такой проблемой?Если да, то как ты решил это?

Спасибо!

Ответы [ 3 ]

7 голосов
/ 20 марта 2011

Реализация динамического поля зависит от следующих факторов:

  1. Возможность динамического добавления атрибутов
  2. Возможность поддержки новых типов данных
  3. Возможность извлечения динамических атрибутов без дополнительного запроса
  4. Возможность доступа к динамическим атрибутам, таким как обычные атрибуты
  5. Возможность запроса объектов на основе динамических атрибутов. (например: найти пользователей с лыжные увлечения)

Как правило, решение не удовлетворяет всем требованиям. Решение Майка обращается к 1 и 5 элегантно. Вы должны использовать его решение, если 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

Для выполнения 5-го требования вам необходимо использовать полнотекстовые поисковые системы Solr или Sphinx. Они эффективнее, чем полагаться на БД для LIKE запросов.

Вот один из подходов, если вы используете Solr через Gem Sunspot.

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

Прирост производительности здесь значительный.

3 голосов
/ 17 марта 2011

Если каждому пользователю разрешено определять свои собственные атрибуты, одним из вариантов может быть наличие таблицы с тремя столбцами: 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        |

Эта таблица будет полезна, если вам нужно найти всех пользователей, у которых есть хобби (запустите LIKE sql для столбца ключей), или всепользователи, которые имеют какое-либо отношение к собаке (запустите LIKE sql для столбца значений).

Это лучший ответ, который я могу дать с вашими требованиями.Может быть, есть стороннее решение, но я настроен скептически.На самом деле это не проблема типа «поп в жемчужине».

2 голосов
/ 17 марта 2011

В этом случае я бы, по крайней мере, рассмотрел бы документную базу данных типа монго или кушетки, которая может справиться с этим типом сценария намного проще, чем с rdms.

Если это не так, я бы, вероятно,в конечном итоге сделать что-то в соответствии с тем, что описал Майк А.

...