mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-27 10:08:11 +08:00
Added prefixed IDs to repository rows (items) [SCI-5909] (#3441)
* Added prefixed IDs to repository rows (items) [SCI-5909] * Fix prefixed id queries to work with joins, use subquery in repository search [SCI-5909] * Fixed accessing repository_row parent code [SCI-5909] * Better handling of repository_row code display [SCI-5909] * Fix index warning for id prefixed models issue setting up project [SCI-5909]
This commit is contained in:
parent
ef6c46a19c
commit
5f9c04844f
13 changed files with 91 additions and 24 deletions
|
@ -9,7 +9,7 @@ module RepositoryDatatableHelper
|
||||||
'DT_RowId': record.id,
|
'DT_RowId': record.id,
|
||||||
'DT_RowAttr': { 'data-state': row_style(record) },
|
'DT_RowAttr': { 'data-state': row_style(record) },
|
||||||
'1': assigned_row(record),
|
'1': assigned_row(record),
|
||||||
'2': record.id,
|
'2': record.code,
|
||||||
'3': escape_input(record.name),
|
'3': escape_input(record.name),
|
||||||
'4': I18n.l(record.created_at, format: :full),
|
'4': I18n.l(record.created_at, format: :full),
|
||||||
'5': escape_input(record.created_by.full_name),
|
'5': escape_input(record.created_by.full_name),
|
||||||
|
@ -52,7 +52,7 @@ module RepositoryDatatableHelper
|
||||||
row = {
|
row = {
|
||||||
'DT_RowId': record.id,
|
'DT_RowId': record.id,
|
||||||
'DT_RowAttr': { 'data-state': row_style(record) },
|
'DT_RowAttr': { 'data-state': row_style(record) },
|
||||||
'1': record.parent_id,
|
'1': record.code,
|
||||||
'2': escape_input(record.name),
|
'2': escape_input(record.name),
|
||||||
'3': I18n.l(record.created_at, format: :full),
|
'3': I18n.l(record.created_at, format: :full),
|
||||||
'4': escape_input(record.created_by.full_name),
|
'4': escape_input(record.created_by.full_name),
|
||||||
|
|
27
app/models/concerns/prefixed_id_model.rb
Normal file
27
app/models/concerns/prefixed_id_model.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PrefixedIdModel
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
begin
|
||||||
|
indexdef = "CREATE INDEX index_#{table_name}_on_#{name.underscore}_code"\
|
||||||
|
" ON public.#{table_name} USING gin ((('#{self::ID_PREFIX}'::text || id)) gin_trgm_ops)"
|
||||||
|
|
||||||
|
index_exists = ActiveRecord::Base.connection.execute(
|
||||||
|
"SELECT indexdef FROM pg_indexes WHERE tablename NOT LIKE 'pg%';"
|
||||||
|
).to_a.map(&:values).flatten.include?(indexdef)
|
||||||
|
|
||||||
|
# rubocop:disable Rails/Output
|
||||||
|
puts("\nWARNING missing index\n#{indexdef}\nfor prefixed id model #{name}!\n\n") unless index_exists
|
||||||
|
# rubocop:enable Rails/Output
|
||||||
|
rescue ActiveRecord::NoDatabaseError # only applicable when setting up project
|
||||||
|
end
|
||||||
|
|
||||||
|
self::PREFIXED_ID_SQL = "('#{self::ID_PREFIX}' || #{table_name}.id)"
|
||||||
|
|
||||||
|
def code
|
||||||
|
"#{self.class::ID_PREFIX}#{id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module SearchableModel
|
module SearchableModel
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
@ -43,8 +45,8 @@ module SearchableModel
|
||||||
(attrs.map.with_index do |a, i|
|
(attrs.map.with_index do |a, i|
|
||||||
if %w(repository_rows.id repository_number_values.data).include?(a)
|
if %w(repository_rows.id repository_number_values.data).include?(a)
|
||||||
"((#{a})::text) #{like} :t#{i} OR "
|
"((#{a})::text) #{like} :t#{i} OR "
|
||||||
elsif a == Experiment::EXPERIMENT_CODE_SQL
|
elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL
|
||||||
"#{Experiment::EXPERIMENT_CODE_SQL} #{like} :t#{i} OR "
|
"#{a} #{like} :t#{i} OR "
|
||||||
else
|
else
|
||||||
col = options[:at_search].to_s == 'true' ? "lower(#{a})": a
|
col = options[:at_search].to_s == 'true' ? "lower(#{a})": a
|
||||||
"(trim_html_tags(#{col})) #{like} :t#{i} OR "
|
"(trim_html_tags(#{col})) #{like} :t#{i} OR "
|
||||||
|
@ -69,8 +71,8 @@ module SearchableModel
|
||||||
(attrs.map.with_index do |a, i|
|
(attrs.map.with_index do |a, i|
|
||||||
if %w(repository_rows.id repository_number_values.data).include?(a)
|
if %w(repository_rows.id repository_number_values.data).include?(a)
|
||||||
"((#{a})::text) #{like} ANY (array[:t#{i}]) OR "
|
"((#{a})::text) #{like} ANY (array[:t#{i}]) OR "
|
||||||
elsif a == Experiment::EXPERIMENT_CODE_SQL
|
elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL
|
||||||
"#{Experiment::EXPERIMENT_CODE_SQL} #{like} ANY (array[:t#{i}]) OR "
|
"#{a} #{like} ANY (array[:t#{i}]) OR "
|
||||||
else
|
else
|
||||||
"(trim_html_tags(#{a})) #{like} ANY (array[:t#{i}]) OR "
|
"(trim_html_tags(#{a})) #{like} ANY (array[:t#{i}]) OR "
|
||||||
end
|
end
|
||||||
|
@ -90,8 +92,8 @@ module SearchableModel
|
||||||
(attrs.map.with_index do |a, i|
|
(attrs.map.with_index do |a, i|
|
||||||
if %w(repository_rows.id repository_number_values.data).include?(a)
|
if %w(repository_rows.id repository_number_values.data).include?(a)
|
||||||
"((#{a})::text) #{like} :t#{i} OR "
|
"((#{a})::text) #{like} :t#{i} OR "
|
||||||
elsif a == Experiment::EXPERIMENT_CODE_SQL
|
elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL
|
||||||
"#{Experiment::EXPERIMENT_CODE_SQL} #{like} :t#{i} OR "
|
"#{a} #{like} :t#{i} OR "
|
||||||
else
|
else
|
||||||
"(trim_html_tags(#{a})) #{like} :t#{i} OR "
|
"(trim_html_tags(#{a})) #{like} :t#{i} OR "
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Experiment < ApplicationRecord
|
class Experiment < ApplicationRecord
|
||||||
include ArchivableModel
|
include ArchivableModel
|
||||||
include SearchableModel
|
include SearchableModel
|
||||||
include SearchableByNameModel
|
include SearchableByNameModel
|
||||||
|
|
||||||
EXPERIMENT_CODE_SQL = "('EX' || id)".freeze
|
ID_PREFIX = 'EX'
|
||||||
|
include PrefixedIdModel
|
||||||
|
|
||||||
before_save -> { report_elements.destroy_all }, if: -> { !new_record? && project_id_changed? }
|
before_save -> { report_elements.destroy_all }, if: -> { !new_record? && project_id_changed? }
|
||||||
|
|
||||||
|
@ -73,7 +76,7 @@ class Experiment < ApplicationRecord
|
||||||
Experiment
|
Experiment
|
||||||
.where('experiments.project_id IN (?)', projects_ids)
|
.where('experiments.project_id IN (?)', projects_ids)
|
||||||
.where_attributes_like(
|
.where_attributes_like(
|
||||||
[:name, :description, EXPERIMENT_CODE_SQL], query, options
|
[:name, :description, PREFIXED_ID_SQL], query, options
|
||||||
)
|
)
|
||||||
return include_archived ? new_query : new_query.active
|
return include_archived ? new_query : new_query.active
|
||||||
elsif include_archived
|
elsif include_archived
|
||||||
|
@ -81,7 +84,7 @@ class Experiment < ApplicationRecord
|
||||||
Experiment
|
Experiment
|
||||||
.where(project: project_ids)
|
.where(project: project_ids)
|
||||||
.where_attributes_like(
|
.where_attributes_like(
|
||||||
[:name, :description, EXPERIMENT_CODE_SQL], query, options
|
[:name, :description, PREFIXED_ID_SQL], query, options
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
new_query =
|
new_query =
|
||||||
|
@ -89,7 +92,7 @@ class Experiment < ApplicationRecord
|
||||||
.active
|
.active
|
||||||
.where(project: project_ids)
|
.where(project: project_ids)
|
||||||
.where_attributes_like(
|
.where_attributes_like(
|
||||||
[:name, :description, EXPERIMENT_CODE_SQL], query, options
|
[:name, :description, PREFIXED_ID_SQL], query, options
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -107,10 +110,6 @@ class Experiment < ApplicationRecord
|
||||||
where(project: Project.viewable_by_user(user, teams))
|
where(project: Project.viewable_by_user(user, teams))
|
||||||
end
|
end
|
||||||
|
|
||||||
def code
|
|
||||||
"EX#{id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def archived_branch?
|
def archived_branch?
|
||||||
archived? || project.archived?
|
archived? || project.archived?
|
||||||
end
|
end
|
||||||
|
|
|
@ -313,7 +313,7 @@ class MyModule < ApplicationRecord
|
||||||
rows = repository.assigned_rows(self).includes(:created_by).order(created_at: order)
|
rows = repository.assigned_rows(self).includes(:created_by).order(created_at: order)
|
||||||
rows.find_each do |row|
|
rows.find_each do |row|
|
||||||
row_json = []
|
row_json = []
|
||||||
row_json << (row.repository.is_a?(RepositorySnapshot) ? row.parent_id : row.id)
|
row_json << row.code
|
||||||
row_json << (row.archived ? "#{row.name} [#{I18n.t('general.archived')}]" : row.name)
|
row_json << (row.archived ? "#{row.name} [#{I18n.t('general.archived')}]" : row.name)
|
||||||
row_json << I18n.l(row.created_at, format: :full)
|
row_json << I18n.l(row.created_at, format: :full)
|
||||||
row_json << row.created_by.full_name
|
row_json << row.created_by.full_name
|
||||||
|
|
|
@ -80,8 +80,14 @@ class Repository < RepositoryBase
|
||||||
|
|
||||||
matched_by_user = readable_rows.joins(:created_by).where_attributes_like('users.full_name', query, options)
|
matched_by_user = readable_rows.joins(:created_by).where_attributes_like('users.full_name', query, options)
|
||||||
|
|
||||||
|
prefixed_repository_row_ids = RepositoryRow.where(repository_id: repositories)
|
||||||
|
.where_attributes_like([RepositoryRow::PREFIXED_ID_SQL], query, options)
|
||||||
|
.select(:id)
|
||||||
|
|
||||||
repository_row_matches =
|
repository_row_matches =
|
||||||
readable_rows.where_attributes_like(['repository_rows.name', 'repository_rows.id'], query, options)
|
readable_rows.where_attributes_like(['repository_rows.name'], query, options).or(
|
||||||
|
readable_rows.where('repository_rows.id' => prefixed_repository_row_ids)
|
||||||
|
)
|
||||||
|
|
||||||
repository_rows = readable_rows.where(id: repository_row_matches)
|
repository_rows = readable_rows.where(id: repository_row_matches)
|
||||||
repository_rows = repository_rows.or(readable_rows.where(id: matched_by_user))
|
repository_rows = repository_rows.or(readable_rows.where(id: matched_by_user))
|
||||||
|
|
|
@ -5,7 +5,11 @@ class RepositoryRow < ApplicationRecord
|
||||||
include SearchableByNameModel
|
include SearchableByNameModel
|
||||||
include ArchivableModel
|
include ArchivableModel
|
||||||
|
|
||||||
|
ID_PREFIX = 'IT'
|
||||||
|
include PrefixedIdModel
|
||||||
|
|
||||||
belongs_to :repository, class_name: 'RepositoryBase'
|
belongs_to :repository, class_name: 'RepositoryBase'
|
||||||
|
belongs_to :parent, class_name: 'RepositoryRow', optional: true
|
||||||
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
|
||||||
belongs_to :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User'
|
belongs_to :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User'
|
||||||
belongs_to :archived_by,
|
belongs_to :archived_by,
|
||||||
|
@ -33,6 +37,10 @@ class RepositoryRow < ApplicationRecord
|
||||||
scope :active, -> { where(archived: false) }
|
scope :active, -> { where(archived: false) }
|
||||||
scope :archived, -> { where(archived: true) }
|
scope :archived, -> { where(archived: true) }
|
||||||
|
|
||||||
|
def code
|
||||||
|
"#{ID_PREFIX}#{parent_id || id}"
|
||||||
|
end
|
||||||
|
|
||||||
def self.viewable_by_user(user, teams)
|
def self.viewable_by_user(user, teams)
|
||||||
where(repository: Repository.viewable_by_user(user, teams))
|
where(repository: Repository.viewable_by_user(user, teams))
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ module Reports::Docx::RepositoryHelper
|
||||||
result = [repository_data[:headers]]
|
result = [repository_data[:headers]]
|
||||||
repository_data[:rows].each do |record|
|
repository_data[:rows].each do |record|
|
||||||
row = []
|
row = []
|
||||||
row.push(record.repository.is_a?(RepositorySnapshot) ? record.parent_id : record.id)
|
row.push(record.code)
|
||||||
row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
|
row.push(escape_input(record.archived ? "#{record.name} [#{I18n.t('general.archived')}]" : record.name))
|
||||||
row.push(I18n.l(record.created_at, format: :full))
|
row.push(I18n.l(record.created_at, format: :full))
|
||||||
row.push(escape_input(record.created_by.full_name))
|
row.push(escape_input(record.created_by.full_name))
|
||||||
|
|
|
@ -78,8 +78,11 @@ class RepositoryDatatableService
|
||||||
if search_value.present?
|
if search_value.present?
|
||||||
matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value)
|
matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value)
|
||||||
|
|
||||||
repository_row_matches = repository_rows
|
repository_row_matches = repository_rows
|
||||||
.where_attributes_like(['repository_rows.name', 'repository_rows.id'], search_value)
|
.where_attributes_like(
|
||||||
|
['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL],
|
||||||
|
search_value
|
||||||
|
)
|
||||||
results = repository_rows.where(id: repository_row_matches)
|
results = repository_rows.where(id: repository_row_matches)
|
||||||
results = results.or(repository_rows.where(id: matched_by_user))
|
results = results.or(repository_rows.where(id: matched_by_user))
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<p>
|
<p>
|
||||||
<span>
|
<span>
|
||||||
<%= t('repository_row.modal_info.ID') %>
|
<%= t('repository_row.modal_info.ID') %>
|
||||||
<%= @repository_row.parent_id || @repository_row.id %>
|
<%= @repository_row.code %>
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<p>
|
<p>
|
||||||
<span>
|
<span>
|
||||||
<%=t 'repository_row.modal_info.ID' %>
|
<%=t 'repository_row.modal_info.ID' %>
|
||||||
<%= repository_row.id %>
|
<%= repository_row.code %>
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span>
|
<span>
|
||||||
|
|
14
db/migrate/20210715125349_add_repository_row_code_index.rb
Normal file
14
db/migrate/20210715125349_add_repository_row_code_index.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddRepositoryRowCodeIndex < ActiveRecord::Migration[6.1]
|
||||||
|
def up
|
||||||
|
ActiveRecord::Base.connection.execute(
|
||||||
|
"CREATE INDEX index_repository_rows_on_repository_row_code ON "\
|
||||||
|
"repository_rows using gin (('IT'::text || id) gin_trgm_ops);"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_index :repository_rows, name: 'index_repository_rows_on_repository_row_code'
|
||||||
|
end
|
||||||
|
end
|
|
@ -5196,6 +5196,13 @@ CREATE INDEX index_repository_rows_on_name ON public.repository_rows USING gin (
|
||||||
CREATE INDEX index_repository_rows_on_repository_id ON public.repository_rows USING btree (repository_id);
|
CREATE INDEX index_repository_rows_on_repository_id ON public.repository_rows USING btree (repository_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: index_repository_rows_on_repository_row_code; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX index_repository_rows_on_repository_row_code ON public.repository_rows USING gin ((('IT'::text || id)) public.gin_trgm_ops);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: index_repository_rows_on_restored_by_id; Type: INDEX; Schema: public; Owner: -
|
-- Name: index_repository_rows_on_restored_by_id; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -7250,6 +7257,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||||
('20210407143303'),
|
('20210407143303'),
|
||||||
('20210410100006'),
|
('20210410100006'),
|
||||||
('20210506125657'),
|
('20210506125657'),
|
||||||
('20210622101238');
|
('20210622101238'),
|
||||||
|
('20210715125349');
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue