2013-06-08 4 views
2

Я работаю над созданием некоторых моделей для баз данных wikimedia, расположенных в викимедийных лабораториях, и из-за технических проблем есть две таблицы для ревизий.Обработка двух почти идентичных таблиц как одна модель

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

Другая таблица revision_userindex имеет индекс, но вместо этого ей не хватает исправлений, в которых затрагивается гашение.

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

В настоящее время у меня есть следующий код:

class Revision < ActiveRecord::Base 
    self.table_name = :revision_userindex # or :revision 
    self.primary_key = :rev_id 

    has_many :externallinks, :class_name => 'Externallink', :foreign_key => :el_from  
    has_many :iwlinks, :class_name => 'Iwlink', :foreign_key => :iwl_from  
    has_many :langlinks, :class_name => 'Langlink', :foreign_key => :ll_from  
    has_many :pagelinks, :class_name => 'Pagelink', :foreign_key => :pl_from  
    has_many :recentchanges, :class_name => 'Recentchange', :foreign_key => :rc_this_oldid  
    belongs_to :page, :class_name => 'Page', :foreign_key => :rev_page  
    belongs_to :user, :class_name => 'User', :foreign_key => :rev_user  
    has_many :templatelinks, :class_name => 'Templatelink', :foreign_key => :tl_from  
    has_many :texts, :class_name => 'Text', :foreign_key => :old_id  
end 

Но я не знаю ни одного способа динамически установить self.table_name в зависимости от когда пользователь задается в контексте или нет. т.е.

Revision.find_by_page("page") 
Revision.find(nnn) 
Revision.where(...) # where the query doesn't link to the user table 
@page.revisions 
# etc... 

следует использовать revision таблицу и

Revision.find_by_user("user") 
@user.revisions 

следует использовать revision_userindex таблицу

Если это, где это возможно осуществить без реализовав половину AR, я был бы рад узнать.

Первичная_запись одинакова для обеих таблиц.

Для справки, вот определение, если таблицы:

Сначала для пересмотра:

CREATE ALGORITHM=UNDEFINED DEFINER=`viewmaster`@`%` SQL SECURITY DEFINER VIEW `revision` AS 
SELECT `enwiki`.`revision`.`rev_id` AS `rev_id`, 
     `enwiki`.`revision`.`rev_page` AS `rev_page`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_text_id`) AS `rev_text_id`, 
     if((`enwiki`.`revision`.`rev_deleted` & 2),NULL,`enwiki`.`revision`.`rev_comment`) AS `rev_comment`, 
     if((`enwiki`.`revision`.`rev_deleted` & 4),NULL,`enwiki`.`revision`.`rev_user`) AS `rev_user`, 
     if((`enwiki`.`revision`.`rev_deleted` & 4),NULL,`enwiki`.`revision`.`rev_user_text`) AS `rev_user_text`, 
     `enwiki`.`revision`.`rev_timestamp` AS `rev_timestamp`, 
     `enwiki`.`revision`.`rev_minor_edit` AS `rev_minor_edit`, 
     `enwiki`.`revision`.`rev_deleted` AS `rev_deleted`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_len`) AS `rev_len`, 
     `enwiki`.`revision`.`rev_parent_id` AS `rev_parent_id`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_sha1`) AS `rev_sha1` 
FROM `enwiki`.`revision` 

И revision_userindex:

CREATE ALGORITHM=UNDEFINED DEFINER=`viewmaster`@`%` SQL SECURITY DEFINER VIEW `revision_userindex` AS 
SELECT `enwiki`.`revision`.`rev_id` AS `rev_id`, 
     `enwiki`.`revision`.`rev_page` AS `rev_page`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_text_id`) AS `rev_text_id`, 
     if((`enwiki`.`revision`.`rev_deleted` & 2),NULL,`enwiki`.`revision`.`rev_comment`) AS `rev_comment`, 
     `enwiki`.`revision`.`rev_user` AS `rev_user`, 
     `enwiki`.`revision`.`rev_user_text` AS `rev_user_text`, 
     `enwiki`.`revision`.`rev_timestamp` AS `rev_timestamp`, 
     `enwiki`.`revision`.`rev_minor_edit` AS `rev_minor_edit`, 
     `enwiki`.`revision`.`rev_deleted` AS `rev_deleted`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_len`) AS `rev_len`, 
     `enwiki`.`revision`.`rev_parent_id` AS `rev_parent_id`, 
     if((`enwiki`.`revision`.`rev_deleted` & 1),NULL,`enwiki`.`revision`.`rev_sha1`) AS `rev_sha1` 
FROM `enwiki`.`revision` 
WHERE ((`enwiki`.`revision`.`rev_deleted` & 4) = 0) 

ответ

0

Просто поставить общую логику/конфигурацию в Mixin а затем использовать две разные модели, которые включают его. Что-то вроде этого (не проверялось, но вы получите идею):

module RevisionCommon 
extend ActiveSupport::Concern 

included do 

    has_many :externallinks, :class_name => 'Externallink', :foreign_key => :el_from  
    has_many :iwlinks, :class_name => 'Iwlink', :foreign_key => :iwl_from  
    has_many :langlinks, :class_name => 'Langlink', :foreign_key => :ll_from  
    has_many :pagelinks, :class_name => 'Pagelink', :foreign_key => :pl_from  
    has_many :recentchanges, :class_name => 'Recentchange', :foreign_key => :rc_this_oldid  
    belongs_to :page, :class_name => 'Page', :foreign_key => :rev_page  
    belongs_to :user, :class_name => 'User', :foreign_key => :rev_user  
    has_many :templatelinks, :class_name => 'Templatelink', :foreign_key => :tl_from  
    has_many :texts, :class_name => 'Text', :foreign_key => :old_id 
end 

module ClassMethods 
    # if any 
end 
end 

class RevisionForPage < ActiveRecord::Base 
    self.table_name = :revision_userindex # or :revision 
    self.primary_key = :rev_id 

    include RevisionCommon 
end 

class RevisionForUser < ActiveRecord::Base 
    self.table_name = :revision_userindex # or :revision 
    self.primary_key = :rev_user_id # or whatever 
    include RevisionCommon 
end 
+0

Разве это не требует от контроллера выбора какой модели использовать в зависимости от того, когда пользователь находится в контексте или нет? Кроме того, primary_key всегда будет одинаковым, это только имя таблицы, которое отличается. – azatoth

0

revision и revision_userindex являются видами, а не таблицы. Просто привяжите свою модель к таблице enwiki.revision.

прямой доступ не авторизован

[править]

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

CREATE ALGORITHM=MERGE VIEW revision_superview AS 
SELECT 
    r.rev_id, r.rev_page, 
    COALESCE(r.rev_user, ru.rev_user) AS rev_user, 
    -- and so on 
FROM revision AS r JOIN revision_user AS ru USING (rev_id) 

запросы на представлении не используют индексы

[править]

Было подтверждено, что no index will be used с выше обходным путем.

Возможно, ваш лучший вариант - попросить вашего администратора базы данных создать другое представление, которое агрегирует данные непосредственно из физической таблицы. Скажите ему, что у вас уже есть доступ к этим данным. Я с удовольствием напишу определение представления, если он/она понадобится.

+0

У меня нет доступа к таблице напрямую, только через представления (из-за секретных данных, которые нужно замаскировать). – azatoth

+0

admin чувак говорит мне индекс не будет работать – azatoth

+0

@azatoth Да, я так думал ... Пожалуйста, см. Мое редактирование. – RandomSeed

0

У вас есть 2 разных стола, и «компьютер» должен выбрать, какой из них использовать в какой-то момент. Есть 3 места, где это может произойти:

  1. Ваш код: вы создаете 2 АСКР, скажем, в заявлении IF выбирает, какой из них использовать.

  2. Код SQL: оператор IF в SP или view. Однако это невозможно сделать в MySQL (насколько я знаю). - это был бы мой предпочтительный вариант, если бы это было возможно.

  3. SQL-запрос, тогда оптимизатор запросов будет выбирать правильную таблицу. Существует несколько способов подбора выбора в SQL-выражении. Это можно сделать через JOIN или UNION ALL или, может быть, каким-то другим способом. Однако, на мой взгляд, MySQL-движок не предназначен для таких вещей. Специальность - быстро работать со строками. Выполнение сравнений и добавление уровней абстракции - это аддоны, которые имеют ограниченное использование.

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

Смежные вопросы