Permalink
Browse files

58810758 Refactor search reference association

Uses polymorphic associaton instead of having separate foreign key
fields for each record
  • Loading branch information...
1 parent 6fb83e7 commit cd2b7c7d34246907d3bc5621808c62824aac882d @saulius saulius committed Nov 15, 2013
@@ -5,11 +5,11 @@ class SearchReferencesController < Api::V1::SearchReferencesBaseController
private
def search_reference_collection
- chapter.search_references_dataset.eager(:chapter)
+ chapter.search_references_dataset
end
def search_reference_resource_association_hash
- { chapter_id: chapter.short_code }
+ { chapter: chapter }
end
def collection_url
@@ -5,11 +5,11 @@ class SearchReferencesController < Api::V1::SearchReferencesBaseController
private
def search_reference_collection
- heading.search_references_dataset.eager(:heading)
+ heading.search_references_dataset
end
def search_reference_resource_association_hash
- { heading_id: heading.short_code }
+ { heading: heading }
end
def collection_url
@@ -5,11 +5,11 @@ class SearchReferencesController < Api::V1::SearchReferencesBaseController
private
def search_reference_collection
- section.search_references_dataset.eager(:section)
+ section.search_references_dataset
end
def search_reference_resource_association_hash
- { section_id: section.id }
+ { section: section }
end
def collection_url
View
@@ -22,7 +22,10 @@ class Chapter < GoodsNomenclature
}
one_to_one :chapter_note, primary_key: :to_param
- one_to_many :search_references, primary_key: :short_code
+ one_to_many :search_references, key: :referenced_id, primary_key: :short_code, reciprocal: :referenced, conditions: { referenced_class: 'Chapter' },
+ adder: proc{ |search_reference| search_reference.update(referenced_id: short_code, referenced_class: 'Chapter') },
+ remover: proc{ |search_reference| search_reference.update(referenced_id: nil, referenced_class: nil)},
+ clearer: proc{ search_references_dataset.update(referenced_id: nil, referenced_class: nil) }
# Tire configuration
tire do
View
@@ -25,7 +25,10 @@ class Heading < GoodsNomenclature
actual_or_relevant(Chapter).filter("goods_nomenclatures.goods_nomenclature_item_id LIKE ?", chapter_id)
}
- one_to_many :search_references, primary_key: :short_code
+ one_to_many :search_references, key: :referenced_id, primary_key: :short_code, reciprocal: :referenced, conditions: { referenced_class: 'Heading' },
+ adder: proc{ |search_reference| search_reference.update(referenced_id: short_code, referenced_class: 'Heading') },
+ remover: proc{ |search_reference| search_reference.update(referenced_id: nil, referenced_class: nil)},
+ clearer: proc{ search_references_dataset.update(referenced_id: nil, referenced_class: nil) }
dataset_module do
def by_code(code = "")
@@ -2,13 +2,50 @@ class SearchReference < Sequel::Model
plugin :active_model
plugin :tire
- many_to_one :section
- many_to_one :chapter, dataset: -> {
- Chapter.by_code(chapter_id)
- }
- many_to_one :heading, dataset: -> {
- Heading.by_code(heading_id)
- }
+ one_to_many :goods_nomenclatures_search_references
+
+ many_to_one :referenced, reciprocal: :referenced,
+ setter: (proc do |referenced|
+ self.set(
+ referenced_id: referenced.to_param,
+ referenced_class: referenced.class.name
+ ) if referenced.present?
+ end),
+ dataset: (proc do
+ klass = referenced_class.constantize
+
+ case klass.name
+ when 'Section'
+ klass.where(klass.primary_key => referenced_id)
+ when 'Chapter'
+ klass.where(
+ Sequel.qualify(:goods_nomenclatures, :goods_nomenclature_item_id) => chapter_id
+ )
+ when 'Heading'
+ klass.where(
+ Sequel.qualify(:goods_nomenclatures, :goods_nomenclature_item_id) => heading_id
+ )
+ end
+ end),
+ eager_loader: (proc do |eo|
+ id_map = {}
+ eo[:rows].each do |referenced|
+ referenced.associations[:referenced] = nil
+ ((id_map[referenced.referenced_class] ||= {})[referenced.referenced_id] ||= []) << referenced
+ end
+ id_map.each do |klass_name, id_map|
+ klass = klass_name.constantize
+ klass.where(klass.primary_key=>id_map.keys).all do |referenced|
+ id_map[rerencedef.pk].each do |ref|
+ ref.associations[:referenced] = referenced
+ end
+ end
+ end
+ end)
+
+ many_to_one :section do |ds|
+ referenced
+ end
self.raise_on_save_failure = false
@@ -24,6 +61,30 @@ def by_title
def for_letter(letter)
where(Sequel.ilike(:title, "#{letter}%"))
end
+
+ def for_chapters
+ where(referenced_class: 'Chapter')
+ end
+
+ def for_chapter(chapter)
+ for_chapters.where(referenced_id: chapter.to_param)
+ end
+
+ def for_headings
+ where(referenced_class: 'Heading')
+ end
+
+ def for_heading(heading)
+ for_headings.where(referenced_id: heading.to_param)
+ end
+
+ def for_sections
+ where(referenced_class: 'Section')
+ end
+
+ def for_section(section)
+ for_sections.where(referenced_id: section.to_param)
+ end
end
tire do
@@ -36,33 +97,57 @@ def for_letter(letter)
end
end
+ alias :section= :referenced=
+ alias :chapter= :referenced=
+ alias :heading= :referenced=
+ alias :heading :referenced
+ alias :chapter :referenced
+ alias :section :referenced
+
+ def chapter_id=(chapter_id)
+ self.referenced = Chapter.by_code(chapter_id).take if chapter_id.present?
+ end
+
+ def heading_id=(heading_id)
+ self.referenced = Heading.by_code(heading_id).take if heading_id.present?
+ end
+
+ def section_id=(section_id)
+ self.referenced = Section.with_pk(section_id) if section_id.present?
+ end
+
def validate
super
- if heading_id.blank? && chapter_id.blank? && section_id.blank?
- errors.add(:reference, 'has to be associated to Section/Chapter/Heading')
- end
+ errors.add(:reference_id, 'has to be associated to Section/Chapter/Heading') if referenced_id.blank?
+ errors.add(:reference_class, 'has to be associated to Section/Chapter/Heading') if referenced_id.blank?
errors.add(:title, 'missing title') if title.blank?
end
- def referenced_entity
- heading || chapter || section || NullObject.new
+ def section_id
+ referenced_id
+ end
+
+ def heading_id
+ "#{referenced_id}000000"
end
- def reference_class
- referenced_entity.class.name
+ def chapter_id
+ "#{referenced_id}00000000"
end
def to_indexed_json
# Cannot return nil from #to_indexed_json because ElasticSearch does not like that.
# It will eat all memory and timeout indexing requests.
- result = if referenced_entity.blank?
+ result = if referenced.blank?
{}
else
{
title: title,
- reference_class: reference_class,
- reference: referenced_entity.serializable_hash.merge({class: referenced_entity.class.name})
+ reference_class: referenced_class,
+ reference: referenced.serializable_hash.merge({
+ class: referenced_class
+ })
}
end
View
@@ -31,7 +31,11 @@ class Section < Sequel::Model
end)
one_to_one :section_note
- one_to_many :search_references
+
+ one_to_many :search_references, key: :referenced_id, reciprocal: :referenced, conditions: { referenced_class: 'Section' },
+ adder: proc{ |search_reference| search_reference.update(referenced_id: id, referenced_class: 'Section') },
+ remover: proc{ |search_reference| search_reference.update(referenced_id: nil, referenced_class: nil)},
+ clearer: proc{ search_references_dataset.update(referenced_id: nil, referenced_class: nil) }
# Tire configuration
tire do
@@ -46,13 +46,21 @@ def create_multiple_records(title, reference)
def associated_entity(reference)
case reference
when HEADING_IDENTITY_REGEX
- { heading_id: Heading.by_code("#{$1}#{$2}").first_or_null.short_code }
+ {
+ referenced_id: Heading.by_code("#{$1}#{$2}").first_or_null.short_code,
+ referenced_class: 'Heading'
+ }
when CHAPTER_IDENTITY_REGEX
- { chapter_id: Chapter.by_code($1).first_or_null.short_code }
+ {
+ referenced_id: Chapter.by_code($1).first_or_null.short_code,
+ referenced_class: 'Chapter'
+ }
when SECTION_IDENTITY_REGEX
- { section_id: Section.where(position: $1).first_or_null.id }
+ {
+ referenced_id: Section.where(position: $1).first_or_null.id,
+ referenced_class: 'Section'
+ }
else
- # unprocessable
{}
end
end
@@ -1,3 +0,0 @@
-object @search_reference
-
-attributes :id, :title, :referenced_entity
@@ -1,7 +1,7 @@
object @search_reference
-attributes :id, :title, :referenced_entity
+attributes :id, :title, :referenced, :referenced_id, :referenced_class
node(false) { |search_reference|
- partial("api/v1/search_references_base/#{search_reference.referenced_entity.class.name.underscore}", object: :referenced_entity)
+ partial("api/v1/search_references_base/#{search_reference.referenced_class.underscore}", object: :referenced)
}
@@ -1,3 +1,3 @@
collection @search_references
-attributes :id, :title, :referenced_entity, :reference_class
+attributes :id, :title, :referenced_id, :referenced_class, :referenced
@@ -1,3 +1,3 @@
object @search_reference
-attributes :id, :title, :referenced_entity, :reference_class, :section_id, :chapter_id, :heading_id
+attributes :id, :title, :referenced_id, :referenced_class, :referenced
@@ -0,0 +1,80 @@
+Sequel.migration do
+ up do
+ alter_table(:search_references) do
+ add_column :referenced_id, String, size: 10
+ add_column :referenced_class, String, size: 10
+ end
+
+ add_index :search_references, [:referenced_id, :referenced_class]
+
+ self[:search_references].each { |search_reference|
+ if search_reference[:heading_id].present?
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ referenced_id: search_reference[:heading_id],
+ referenced_class: 'Heading'
+ )
+ elsif search_reference[:chapter_id].present?
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ referenced_id: search_reference[:chapter_id],
+ referenced_class: 'Chapter'
+ )
+ elsif search_reference[:section_id].present?
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ referenced_id: search_reference[:section_id],
+ referenced_class: 'Section'
+ )
+ end
+ }
+
+ alter_table(:search_references) do
+ drop_column :heading_id
+ drop_column :chapter_id
+ drop_column :section_id
+ end
+ end
+
+ down do
+ alter_table(:search_references) do
+ add_column :heading_id, String, size: 4
+ add_column :chapter_id, String, size: 2
+ add_column :section_id, Integer
+ end
+
+ self[:search_references].each { |search_reference|
+ if search_reference[:referenced_class] == 'Heading'
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ heading_id: search_reference[:referenced_id]
+ )
+ end
+
+ if search_reference[:referenced_class] == 'Chapter'
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ chapter_id: search_reference[:referenced_id]
+ )
+ end
+
+ if search_reference[:referenced_class] == 'Section'
+ self[:search_references].where(
+ id: search_reference[:id]
+ ).update(
+ section_id: search_reference[:referenced_id]
+ )
+ end
+ }
+
+ alter_table(:search_references) do
+ drop_column :referenced_id
+ drop_column :referenced_class
+ end
+ end
+end
@@ -21,8 +21,8 @@
context 'with letter param provided' do
let(:pattern) {
[
- {id: Integer, title: String, referenced_entity: Hash, reference_class: 'Section' },
- {id: Integer, title: String, referenced_entity: Hash, reference_class: 'Chapter' }
+ {id: Integer, title: String, referenced: Hash, referenced_class: 'Section', referenced_id: String },
+ {id: Integer, title: String, referenced: Hash, referenced_class: 'Chapter', referenced_id: String }
]
}
@@ -36,7 +36,7 @@
context 'with no letter param provided' do
let(:pattern) {
[
- {id: Integer, title: String, referenced_entity: Hash, reference_class: 'Heading' }
+ {id: Integer, title: String, referenced: Hash, referenced_class: 'Heading', referenced_id: String }
]
}
@@ -5,8 +5,7 @@
sequence(:sid) { |n| n}
factory :search_reference do
- association :heading
-
title { Forgery(:basic).text }
+ association :heading
end
end
Oops, something went wrong.

0 comments on commit cd2b7c7

Please sign in to comment.