diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb index ecdd90ee3..25d1902e6 100644 --- a/app/helpers/repository_datatable_helper.rb +++ b/app/helpers/repository_datatable_helper.rb @@ -9,7 +9,7 @@ module RepositoryDatatableHelper 'DT_RowId': record.id, 'DT_RowAttr': { 'data-state': row_style(record) }, '1': assigned_row(record), - '2': record.id, + '2': record.code, '3': escape_input(record.name), '4': I18n.l(record.created_at, format: :full), '5': escape_input(record.created_by.full_name), @@ -52,7 +52,7 @@ module RepositoryDatatableHelper row = { 'DT_RowId': record.id, 'DT_RowAttr': { 'data-state': row_style(record) }, - '1': record.parent_id, + '1': record.code, '2': escape_input(record.name), '3': I18n.l(record.created_at, format: :full), '4': escape_input(record.created_by.full_name), diff --git a/app/models/concerns/prefixed_id_model.rb b/app/models/concerns/prefixed_id_model.rb new file mode 100644 index 000000000..d7a571c9c --- /dev/null +++ b/app/models/concerns/prefixed_id_model.rb @@ -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 diff --git a/app/models/concerns/searchable_model.rb b/app/models/concerns/searchable_model.rb index 2502c2ce4..770bfb396 100644 --- a/app/models/concerns/searchable_model.rb +++ b/app/models/concerns/searchable_model.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SearchableModel extend ActiveSupport::Concern @@ -43,8 +45,8 @@ module SearchableModel (attrs.map.with_index do |a, i| if %w(repository_rows.id repository_number_values.data).include?(a) "((#{a})::text) #{like} :t#{i} OR " - elsif a == Experiment::EXPERIMENT_CODE_SQL - "#{Experiment::EXPERIMENT_CODE_SQL} #{like} :t#{i} OR " + elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL + "#{a} #{like} :t#{i} OR " else col = options[:at_search].to_s == 'true' ? "lower(#{a})": a "(trim_html_tags(#{col})) #{like} :t#{i} OR " @@ -69,8 +71,8 @@ module SearchableModel (attrs.map.with_index do |a, i| if %w(repository_rows.id repository_number_values.data).include?(a) "((#{a})::text) #{like} ANY (array[:t#{i}]) OR " - elsif a == Experiment::EXPERIMENT_CODE_SQL - "#{Experiment::EXPERIMENT_CODE_SQL} #{like} ANY (array[:t#{i}]) OR " + elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL + "#{a} #{like} ANY (array[:t#{i}]) OR " else "(trim_html_tags(#{a})) #{like} ANY (array[:t#{i}]) OR " end @@ -90,8 +92,8 @@ module SearchableModel (attrs.map.with_index do |a, i| if %w(repository_rows.id repository_number_values.data).include?(a) "((#{a})::text) #{like} :t#{i} OR " - elsif a == Experiment::EXPERIMENT_CODE_SQL - "#{Experiment::EXPERIMENT_CODE_SQL} #{like} :t#{i} OR " + elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL + "#{a} #{like} :t#{i} OR " else "(trim_html_tags(#{a})) #{like} :t#{i} OR " end diff --git a/app/models/experiment.rb b/app/models/experiment.rb index 5ad42ff6e..c4f858e1b 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + class Experiment < ApplicationRecord include ArchivableModel include SearchableModel 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? } @@ -73,7 +76,7 @@ class Experiment < ApplicationRecord Experiment .where('experiments.project_id IN (?)', projects_ids) .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 elsif include_archived @@ -81,7 +84,7 @@ class Experiment < ApplicationRecord Experiment .where(project: project_ids) .where_attributes_like( - [:name, :description, EXPERIMENT_CODE_SQL], query, options + [:name, :description, PREFIXED_ID_SQL], query, options ) else new_query = @@ -89,7 +92,7 @@ class Experiment < ApplicationRecord .active .where(project: project_ids) .where_attributes_like( - [:name, :description, EXPERIMENT_CODE_SQL], query, options + [:name, :description, PREFIXED_ID_SQL], query, options ) end @@ -107,10 +110,6 @@ class Experiment < ApplicationRecord where(project: Project.viewable_by_user(user, teams)) end - def code - "EX#{id}" - end - def archived_branch? archived? || project.archived? end diff --git a/app/models/my_module.rb b/app/models/my_module.rb index ab2b8b907..cc2635eea 100644 --- a/app/models/my_module.rb +++ b/app/models/my_module.rb @@ -313,7 +313,7 @@ class MyModule < ApplicationRecord rows = repository.assigned_rows(self).includes(:created_by).order(created_at: order) rows.find_each do |row| 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 << I18n.l(row.created_at, format: :full) row_json << row.created_by.full_name diff --git a/app/models/repository.rb b/app/models/repository.rb index 920ce8b94..f2b2e4f9e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -80,8 +80,14 @@ class Repository < RepositoryBase 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 = - 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 = repository_rows.or(readable_rows.where(id: matched_by_user)) diff --git a/app/models/repository_row.rb b/app/models/repository_row.rb index 49032962b..87bb3754d 100644 --- a/app/models/repository_row.rb +++ b/app/models/repository_row.rb @@ -5,7 +5,11 @@ class RepositoryRow < ApplicationRecord include SearchableByNameModel include ArchivableModel + ID_PREFIX = 'IT' + include PrefixedIdModel + 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 :last_modified_by, foreign_key: :last_modified_by_id, class_name: 'User' belongs_to :archived_by, @@ -33,6 +37,10 @@ class RepositoryRow < ApplicationRecord scope :active, -> { where(archived: false) } scope :archived, -> { where(archived: true) } + def code + "#{ID_PREFIX}#{parent_id || id}" + end + def self.viewable_by_user(user, teams) where(repository: Repository.viewable_by_user(user, teams)) end diff --git a/app/services/reports/docx/repository_helper.rb b/app/services/reports/docx/repository_helper.rb index 4bc4dff61..6528e6e69 100644 --- a/app/services/reports/docx/repository_helper.rb +++ b/app/services/reports/docx/repository_helper.rb @@ -7,7 +7,7 @@ module Reports::Docx::RepositoryHelper result = [repository_data[:headers]] repository_data[:rows].each do |record| 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(I18n.l(record.created_at, format: :full)) row.push(escape_input(record.created_by.full_name)) diff --git a/app/services/repository_datatable_service.rb b/app/services/repository_datatable_service.rb index b2ddd47db..33c845d4a 100644 --- a/app/services/repository_datatable_service.rb +++ b/app/services/repository_datatable_service.rb @@ -78,8 +78,11 @@ class RepositoryDatatableService if search_value.present? matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value) - repository_row_matches = repository_rows - .where_attributes_like(['repository_rows.name', 'repository_rows.id'], search_value) + repository_row_matches = repository_rows + .where_attributes_like( + ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL], + search_value + ) results = repository_rows.where(id: repository_row_matches) results = results.or(repository_rows.where(id: matched_by_user)) diff --git a/app/views/repositories/_repository_row_info_modal.html.erb b/app/views/repositories/_repository_row_info_modal.html.erb index a121aa76f..2029ff378 100644 --- a/app/views/repositories/_repository_row_info_modal.html.erb +++ b/app/views/repositories/_repository_row_info_modal.html.erb @@ -18,7 +18,7 @@

<%= t('repository_row.modal_info.ID') %> - <%= @repository_row.parent_id || @repository_row.id %> + <%= @repository_row.code %>
diff --git a/app/views/search/results/_repositories.html.erb b/app/views/search/results/_repositories.html.erb index ffd86beae..a8a97146e 100644 --- a/app/views/search/results/_repositories.html.erb +++ b/app/views/search/results/_repositories.html.erb @@ -8,7 +8,7 @@

<%=t 'repository_row.modal_info.ID' %> - <%= repository_row.id %> + <%= repository_row.code %>
diff --git a/db/migrate/20210715125349_add_repository_row_code_index.rb b/db/migrate/20210715125349_add_repository_row_code_index.rb new file mode 100644 index 000000000..c14b37655 --- /dev/null +++ b/db/migrate/20210715125349_add_repository_row_code_index.rb @@ -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 diff --git a/db/structure.sql b/db/structure.sql index 8e83ef5dc..03015835d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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); +-- +-- 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: - -- @@ -7250,6 +7257,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210407143303'), ('20210410100006'), ('20210506125657'), -('20210622101238'); +('20210622101238'), +('20210715125349');