* Denormalized post versions. Testing has shown it reduces the size

of the table 66%.
This commit is contained in:
albert
2010-11-04 18:17:03 -04:00
parent d0d3487fc8
commit 551c25909c
9 changed files with 157 additions and 114 deletions

View File

@@ -34,7 +34,7 @@ class DateTag
end end
def next_week def next_week
DateTag.new_from_range(1.week.since(start_date)), 1.week.since(end_date) DateTag.new_from_range(1.week.since(start_date), 1.week.since(end_date))
end end
def previous_month def previous_month
@@ -42,7 +42,7 @@ class DateTag
end end
def next_month def next_month
DateTag.new_from_range(1.month.since(start_date)), 1.month.since(end_date) DateTag.new_from_range(1.month.since(start_date), 1.month.since(end_date))
end end
def date def date

View File

@@ -1,7 +1,7 @@
class Post < ActiveRecord::Base class Post < ActiveRecord::Base
attr_accessor :old_tag_string, :old_parent_id attr_accessor :old_tag_string, :old_parent_id
after_destroy :delete_files after_destroy :delete_files
after_save :create_version after_save :update_history
after_save :update_parent_on_save after_save :update_parent_on_save
before_save :merge_old_tags before_save :merge_old_tags
before_save :normalize_tags before_save :normalize_tags
@@ -15,7 +15,7 @@ class Post < ActiveRecord::Base
has_one :unapproval, :dependent => :destroy has_one :unapproval, :dependent => :destroy
has_one :upload, :dependent => :destroy has_one :upload, :dependent => :destroy
has_one :moderation_detail, :class_name => "PostModerationDetail", :dependent => :destroy has_one :moderation_detail, :class_name => "PostModerationDetail", :dependent => :destroy
has_many :versions, :class_name => "PostVersion", :dependent => :destroy has_one :history, :class_name => "PostHistory"
has_many :votes, :class_name => "PostVote", :dependent => :destroy has_many :votes, :class_name => "PostVote", :dependent => :destroy
has_many :notes, :dependent => :destroy has_many :notes, :dependent => :destroy
has_many :comments has_many :comments
@@ -233,24 +233,21 @@ class Post < ActiveRecord::Base
end end
end end
module VersionMethods module HistoryMethods
def create_version def revisions
version = versions.create( if history.nil?
:source => source, update_history
:rating => rating, end
:tag_string => tag_string,
:updater_id => CurrentUser.user.id, history.revisions
:updater_ip_addr => CurrentUser.ip_addr
)
raise PostVersion::Error.new(version.errors.full_messages.join("; ")) if version.errors.any?
end end
def revert_to!(version) def update_history
self.source = version.source if history.nil?
self.rating = version.rating create_history
self.tag_string = version.tag_string end
save!
history << self
end end
end end
@@ -788,7 +785,7 @@ class Post < ActiveRecord::Base
include ImageMethods include ImageMethods
include ApprovalMethods include ApprovalMethods
include PresenterMethods include PresenterMethods
include VersionMethods include HistoryMethods
include TagMethods include TagMethods
include FavoriteMethods include FavoriteMethods
include UploaderMethods include UploaderMethods

View File

@@ -0,0 +1,40 @@
class PostHistory < ActiveRecord::Base
class Error < Exception ; end
before_validation :initialize_revisions, :on => :create
belongs_to :post
def self.build_revision_for_post(post)
hash = {
:source => post.source,
:rating => post.rating,
:tag_string => post.tag_string,
:parent_id => post.parent_id,
:user_id => CurrentUser.id,
:ip_addr => CurrentUser.ip_addr,
:updated_at => revision_time
}
end
def self.revision_time
Time.now
end
def initialize_revisions
write_attribute(:revisions, "[]")
end
def revisions
if read_attribute(:revisions).blank?
[]
else
JSON.parse(read_attribute(:revisions))
end
end
def <<(post)
revision = self.class.build_revision_for_post(post)
write_attribute(:revisions, (revisions << revision).to_json)
save
end
end

View File

@@ -1,6 +0,0 @@
class PostVersion < ActiveRecord::Base
class Error < Exception ; end
validates_presence_of :updater_id, :updater_ip_addr
belongs_to :post
end

View File

@@ -1,5 +1,5 @@
<div id="search-form" style="margin-bottom: 1em;"> <div id="search-form" style="margin-bottom: 1em;">
<% form_tag(artists_path, :method => :get) do %> <%= form_tag(artists_path, :method => :get) do %>
<%= text_field_tag "name", params[:name], :size => 40 %> <%= text_field_tag "name", params[:name], :size => 40 %>
<%= submit_tag "Search" %> <%= submit_tag "Search" %>
<% end %> <% end %>

View File

@@ -1013,6 +1013,38 @@ CREATE SEQUENCE pools_id_seq
ALTER SEQUENCE pools_id_seq OWNED BY pools.id; ALTER SEQUENCE pools_id_seq OWNED BY pools.id;
--
-- Name: post_histories; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE post_histories (
id integer NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone,
post_id integer NOT NULL,
revisions text NOT NULL
);
--
-- Name: post_histories_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE post_histories_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
--
-- Name: post_histories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE post_histories_id_seq OWNED BY post_histories.id;
-- --
-- Name: post_moderation_details; Type: TABLE; Schema: public; Owner: -; Tablespace: -- Name: post_moderation_details; Type: TABLE; Schema: public; Owner: -; Tablespace:
-- --
@@ -1045,42 +1077,6 @@ CREATE SEQUENCE post_moderation_details_id_seq
ALTER SEQUENCE post_moderation_details_id_seq OWNED BY post_moderation_details.id; ALTER SEQUENCE post_moderation_details_id_seq OWNED BY post_moderation_details.id;
--
-- Name: post_versions; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE post_versions (
id integer NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone,
post_id integer NOT NULL,
source character varying(255),
rating character(1) DEFAULT 'q'::bpchar NOT NULL,
tag_string text NOT NULL,
updater_id integer NOT NULL,
updater_ip_addr inet NOT NULL
);
--
-- Name: post_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE post_versions_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
--
-- Name: post_versions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE post_versions_id_seq OWNED BY post_versions.id;
-- --
-- Name: post_votes; Type: TABLE; Schema: public; Owner: -; Tablespace: -- Name: post_votes; Type: TABLE; Schema: public; Owner: -; Tablespace:
-- --
@@ -1812,14 +1808,14 @@ ALTER TABLE pools ALTER COLUMN id SET DEFAULT nextval('pools_id_seq'::regclass);
-- Name: id; Type: DEFAULT; Schema: public; Owner: - -- Name: id; Type: DEFAULT; Schema: public; Owner: -
-- --
ALTER TABLE post_moderation_details ALTER COLUMN id SET DEFAULT nextval('post_moderation_details_id_seq'::regclass); ALTER TABLE post_histories ALTER COLUMN id SET DEFAULT nextval('post_histories_id_seq'::regclass);
-- --
-- Name: id; Type: DEFAULT; Schema: public; Owner: - -- Name: id; Type: DEFAULT; Schema: public; Owner: -
-- --
ALTER TABLE post_versions ALTER COLUMN id SET DEFAULT nextval('post_versions_id_seq'::regclass); ALTER TABLE post_moderation_details ALTER COLUMN id SET DEFAULT nextval('post_moderation_details_id_seq'::regclass);
-- --
@@ -2137,6 +2133,14 @@ ALTER TABLE ONLY pools
ADD CONSTRAINT pools_pkey PRIMARY KEY (id); ADD CONSTRAINT pools_pkey PRIMARY KEY (id);
--
-- Name: post_histories_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY post_histories
ADD CONSTRAINT post_histories_pkey PRIMARY KEY (id);
-- --
-- Name: post_moderation_details_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- Name: post_moderation_details_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
-- --
@@ -2145,14 +2149,6 @@ ALTER TABLE ONLY post_moderation_details
ADD CONSTRAINT post_moderation_details_pkey PRIMARY KEY (id); ADD CONSTRAINT post_moderation_details_pkey PRIMARY KEY (id);
--
-- Name: post_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY post_versions
ADD CONSTRAINT post_versions_pkey PRIMARY KEY (id);
-- --
-- Name: post_votes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- Name: post_votes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
-- --
@@ -2712,6 +2708,13 @@ CREATE INDEX index_pools_on_creator_id ON pools USING btree (creator_id);
CREATE INDEX index_pools_on_name ON pools USING btree (name); CREATE INDEX index_pools_on_name ON pools USING btree (name);
--
-- Name: index_post_histories_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_post_histories_on_post_id ON post_histories USING btree (post_id);
-- --
-- Name: index_post_moderation_details_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- Name: index_post_moderation_details_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
-- --
@@ -2726,20 +2729,6 @@ CREATE INDEX index_post_moderation_details_on_post_id ON post_moderation_details
CREATE INDEX index_post_moderation_details_on_user_id ON post_moderation_details USING btree (user_id); CREATE INDEX index_post_moderation_details_on_user_id ON post_moderation_details USING btree (user_id);
--
-- Name: index_post_versions_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_post_versions_on_post_id ON post_versions USING btree (post_id);
--
-- Name: index_post_versions_on_updater_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_post_versions_on_updater_id ON post_versions USING btree (updater_id);
-- --
-- Name: index_posts_on_created_at; Type: INDEX; Schema: public; Owner: -; Tablespace: -- Name: index_posts_on_created_at; Type: INDEX; Schema: public; Owner: -; Tablespace:
-- --

View File

@@ -0,0 +1,16 @@
class CreatePostHistories < ActiveRecord::Migration
def self.up
create_table :post_histories do |t|
t.timestamps
t.column :post_id, :integer, :null => false
t.column :revisions, :text, :null => false
end
add_index :post_histories, :post_id
end
def self.down
drop_table :post_histories
end
end

View File

@@ -1,26 +0,0 @@
class CreatePostVersions < ActiveRecord::Migration
def self.up
create_table :post_versions do |t|
t.timestamps
# Post
t.column :post_id, :integer, :null => false
# Versioned
t.column :source, :string
t.column :rating, :character, :null => false, :default => 'q'
t.column :tag_string, :text, :null => false
# Updater
t.column :updater_id, :integer, :null => false
t.column :updater_ip_addr, "inet", :null => false
end
add_index :post_versions, :post_id
add_index :post_versions, :updater_id
end
def self.down
drop_table :post_versions
end
end

View File

@@ -0,0 +1,33 @@
require_relative '../test_helper'
class PostHistoryTest < ActiveSupport::TestCase
context "A post" do
setup do
@user = Factory.create(:user)
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
MEMCACHE.flush_all
end
teardown do
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end
should "create a revision after creation" do
PostHistory.stubs(:revision_time).returns("TIME")
post = Factory.create(:post, :tag_string => "aaa bbb ccc")
assert_equal(1, post.revisions.size)
assert_equal({"source"=>nil, "rating"=>"q", "tag_string"=>"aaa bbb ccc", "parent_id"=>nil, "user_id"=>1, "ip_addr"=>"127.0.0.1", "updated_at"=>"TIME"}, post.revisions.last)
end
should "create additional revisions after updating" do
PostHistory.stubs(:revision_time).returns("TIME")
post = Factory.create(:post, :tag_string => "aaa bbb ccc")
post.update_attributes(:tag_string => "bbb ccc ddd")
post.reload
assert_equal(2, post.revisions.size)
assert_equal({"source"=>nil, "rating"=>"q", "tag_string"=>"bbb ccc ddd", "parent_id"=>nil, "user_id"=>3, "ip_addr"=>"127.0.0.1", "updated_at"=>"TIME"}, post.revisions.last)
end
end
end