2

我正在为位于维基媒体实验室的维基媒体数据库创建一些模型,由于技术问题,有两个表格供修订。

一个表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根据上下文中是否指定用户来动态设置。IE

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`

对于修订用户索引:

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)
4

3 回答 3

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
于 2013-06-08T19:09:39.083 回答
0

您有 2 个不同的表,“计算机”应该选择在某个时候使用哪一个。有3个地方可能发生:

  1. 您的代码:您创建了 2 个 AR,比方说,一个 IF 语句选择使用哪一个。

  2. SQL 代码:SP 或视图中的 IF 语句。但是,在 MySQL 中是不可能的(据我所知)。- 如果可能的话,这将是我的首选。

  3. SQL 语句,然后查询优化器将选择正确的表。有几种方法可以将选择潜入 SQL 语句。它可以通过JOINorUNION ALL或者,也许是其他方式来完成。但是在我看来,MySQL 引擎并不是为这些事情而设计的。它的专长是快速处理行。进行比较和添加抽象层是插件,用途有限。

我建议创建 2 个 AR 对象并选择其中一个。您可以在不指定表名的情况下创建 AR 类,然后 2 个新的 AR 对象将继承所有属性并定义表名。我对 Ruby 的了解有限,所以如果这个建议不完全正确,我深表歉意。

于 2013-06-18T08:37:30.307 回答
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)

视图上的查询不使用索引

[编辑]

已确认上述解决方法不会使用任何索引。

也许您最好的选择是让您的 DBA 创建另一个视图,直接从物理表中聚合数据。告诉他/她您已经可以访问这些数据。如果他/她需要,我很乐意编写视图定义。

于 2013-06-12T11:58:05.657 回答