mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-11 15:45:34 +08:00
Implement global search backend [SCI-10573]
This commit is contained in:
parent
2ad7c36f0f
commit
3c4184c73e
21 changed files with 308 additions and 649 deletions
|
@ -21,7 +21,9 @@ module Dashboard
|
|||
|
||||
def project_filter
|
||||
projects = Project.readable_by_user(current_user)
|
||||
.search(current_user, false, params[:query], 1, current_team)
|
||||
.search(current_user, false, params[:query], current_team)
|
||||
.page(1)
|
||||
.per(Constants::SEARCH_LIMIT)
|
||||
.select(:id, :name)
|
||||
projects = projects.map { |i| { value: i.id, label: escape_input(i.name) } }
|
||||
if (projects.map { |i| i[:label] }.exclude? params[:query]) && params[:query].present?
|
||||
|
@ -36,7 +38,9 @@ module Dashboard
|
|||
elsif @project
|
||||
experiments = @project.experiments
|
||||
.managable_by_user(current_user)
|
||||
.search(current_user, false, params[:query], 1, current_team)
|
||||
.search(current_user, false, params[:query], current_team)
|
||||
.page(1)
|
||||
.per(Constants::SEARCH_LIMIT)
|
||||
.select(:id, :name)
|
||||
experiments = experiments.map { |i| { value: i.id, label: escape_input(i.name) } }
|
||||
if (experiments.map { |i| i[:label] }.exclude? params[:query]) &&
|
||||
|
|
|
@ -165,7 +165,9 @@ class ResultsController < ApplicationController
|
|||
|
||||
def apply_filters!
|
||||
if params[:query].present?
|
||||
@results = @results.search(current_user, params[:view_mode] == 'archived', params[:query], params[:page] || 1)
|
||||
@results = @results.search(current_user, params[:view_mode] == 'archived', params[:query])
|
||||
.page(params[:page] || 1)
|
||||
.per(Constants::SEARCH_LIMIT)
|
||||
end
|
||||
|
||||
@results = @results.where('results.created_at >= ?', params[:created_at_from]) if params[:created_at_from]
|
||||
|
|
|
@ -117,7 +117,6 @@ class SearchController < ApplicationController
|
|||
}
|
||||
return
|
||||
when 'protocols'
|
||||
@protocol_search_count = fetch_cached_count(Protocol)
|
||||
search_protocols
|
||||
results = if params[:preview] == 'true'
|
||||
@protocol_results.take(4)
|
||||
|
@ -184,38 +183,6 @@ class SearchController < ApplicationController
|
|||
}
|
||||
return
|
||||
end
|
||||
|
||||
#@search_id = params[:search_id] ? params[:search_id] : generate_search_id
|
||||
#
|
||||
#count_search_results
|
||||
#
|
||||
#search_projects if @search_category == :projects
|
||||
#search_project_folders if @search_category == :project_folders
|
||||
#search_experiments if @search_category == :experiments
|
||||
#search_modules if @search_category == :modules
|
||||
#search_results if @search_category == :results
|
||||
#search_tags if @search_category == :tags
|
||||
#search_reports if @search_category == :reports
|
||||
#search_protocols if @search_category == :protocols
|
||||
#search_steps if @search_category == :steps
|
||||
#search_checklists if @search_category == :checklists
|
||||
#if @search_category == :repositories && params[:repository]
|
||||
# search_repository
|
||||
#end
|
||||
#search_assets if @search_category == :assets
|
||||
#search_tables if @search_category == :tables
|
||||
#search_comments if @search_category == :comments
|
||||
|
||||
#@search_pages = (@search_count.to_f / Constants::SEARCH_LIMIT.to_f).ceil
|
||||
#@start_page = @search_page - 2
|
||||
#@start_page = 1 if @start_page < 1
|
||||
#@end_page = @start_page + 4
|
||||
|
||||
#if @end_page > @search_pages
|
||||
# @end_page = @search_pages
|
||||
# @start_page = @end_page - 4
|
||||
# @start_page = 1 if @start_page < 1
|
||||
#end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -252,46 +219,35 @@ class SearchController < ApplicationController
|
|||
|
||||
def load_vars
|
||||
query = (params.fetch(:q) { '' }).strip
|
||||
@search_category = params[:category] || ''
|
||||
@search_category = @search_category.to_sym
|
||||
@search_page = params[:page].to_i || 1
|
||||
@search_case = params[:match_case] == 'true'
|
||||
@search_whole_word = params[:whole_word] == 'true'
|
||||
@search_whole_phrase = params[:whole_phrase] == 'true'
|
||||
@filters = params[:filters]
|
||||
@include_archived = if @filters.present?
|
||||
@filters[:include_archived] == 'true'
|
||||
else
|
||||
true
|
||||
end
|
||||
@teams = if @filters.present?
|
||||
@filters[:teams]&.values || current_user.teams
|
||||
else
|
||||
current_user.teams
|
||||
end
|
||||
@display_query = query
|
||||
|
||||
if @search_whole_phrase || query.count(' ').zero?
|
||||
if query.length < Constants::NAME_MIN_LENGTH
|
||||
flash[:error] = t('general.query.length_too_short',
|
||||
min_length: Constants::NAME_MIN_LENGTH)
|
||||
redirect_back(fallback_location: root_path)
|
||||
elsif query.length > Constants::TEXT_MAX_LENGTH
|
||||
flash[:error] = t('general.query.length_too_long',
|
||||
max_length: Constants::TEXT_MAX_LENGTH)
|
||||
redirect_back(fallback_location: root_path)
|
||||
else
|
||||
@search_query = query
|
||||
end
|
||||
else
|
||||
# splits the search query to validate all entries
|
||||
splited_query = query.split
|
||||
@search_query = ''
|
||||
splited_query.each_with_index do |w, i|
|
||||
if w.length >= Constants::NAME_MIN_LENGTH &&
|
||||
w.length <= Constants::TEXT_MAX_LENGTH
|
||||
@search_query += "#{splited_query[i]} "
|
||||
end
|
||||
end
|
||||
if @search_query.blank?
|
||||
flash[:error] = t('general.query.wrong_query',
|
||||
min_length: Constants::NAME_MIN_LENGTH,
|
||||
max_length: Constants::TEXT_MAX_LENGTH)
|
||||
redirect_back(fallback_location: root_path)
|
||||
else
|
||||
@search_query.strip!
|
||||
splited_query = query.split
|
||||
@search_query = ''
|
||||
splited_query.each_with_index do |w, i|
|
||||
if w.length >= Constants::NAME_MIN_LENGTH &&
|
||||
w.length <= Constants::TEXT_MAX_LENGTH
|
||||
@search_query += "#{splited_query[i]} "
|
||||
end
|
||||
end
|
||||
@search_page = 1 if @search_page < 1
|
||||
if @search_query.blank?
|
||||
flash[:error] = t('general.query.wrong_query',
|
||||
min_length: Constants::NAME_MIN_LENGTH,
|
||||
max_length: Constants::TEXT_MAX_LENGTH)
|
||||
redirect_back(fallback_location: root_path)
|
||||
else
|
||||
@search_query.strip!
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -300,109 +256,27 @@ class SearchController < ApplicationController
|
|||
SecureRandom.urlsafe_base64(32)
|
||||
end
|
||||
|
||||
def search_by_name(model)
|
||||
def search_by_name(model, options={})
|
||||
records = model.search(current_user,
|
||||
@include_archived,
|
||||
@search_query,
|
||||
nil,
|
||||
teams: @teams,
|
||||
users: @users,
|
||||
options: options)
|
||||
|
||||
records = filter_records(records, model) if @filters.present?
|
||||
sort_records(records)
|
||||
end
|
||||
|
||||
def count_by_name(model, options = {})
|
||||
model.search(current_user,
|
||||
true,
|
||||
@include_archived,
|
||||
@search_query,
|
||||
@search_page,
|
||||
nil,
|
||||
match_case: @search_case,
|
||||
whole_word: @search_whole_word,
|
||||
whole_phrase: @search_whole_phrase)
|
||||
.order(created_at: :desc)
|
||||
end
|
||||
|
||||
def count_by_name(model)
|
||||
model.search(current_user,
|
||||
true,
|
||||
@search_query,
|
||||
Constants::SEARCH_NO_LIMIT,
|
||||
nil,
|
||||
match_case: @search_case,
|
||||
whole_word: @search_whole_word,
|
||||
whole_phrase: @search_whole_phrase).size
|
||||
end
|
||||
|
||||
def count_by_repository
|
||||
@repository_search_count =
|
||||
Rails.cache.fetch("#{@search_id}/repository_search_count",
|
||||
expires_in: 5.minutes) do
|
||||
search_count = {}
|
||||
search_results = Repository.search(current_user,
|
||||
@search_query,
|
||||
Constants::SEARCH_NO_LIMIT,
|
||||
nil,
|
||||
match_case: @search_case,
|
||||
whole_word: @search_whole_word,
|
||||
whole_phrase: @search_whole_phrase)
|
||||
|
||||
current_user.teams.includes(:repositories).each do |team|
|
||||
team_results = {}
|
||||
team_results[:team] = team
|
||||
team_results[:count] = 0
|
||||
team_results[:repositories] = {}
|
||||
Repository.accessible_by_teams(team).each do |repository|
|
||||
repository_results = {}
|
||||
repository_results[:id] = repository.id
|
||||
repository_results[:repository] = repository
|
||||
repository_results[:count] = 0
|
||||
search_results.each do |result|
|
||||
repository_results[:count] += result.counter if repository.id == result.id
|
||||
end
|
||||
team_results[:repositories][repository.name] = repository_results
|
||||
team_results[:count] += repository_results[:count]
|
||||
end
|
||||
search_count[team.name] = team_results
|
||||
end
|
||||
search_count
|
||||
end
|
||||
|
||||
count_total = 0
|
||||
@repository_search_count.each_value do |team_results|
|
||||
count_total += team_results[:count]
|
||||
end
|
||||
count_total
|
||||
end
|
||||
|
||||
def current_repository_search_count
|
||||
@repository_search_count.each_value do |counter|
|
||||
res = counter[:repositories].values.detect do |rep|
|
||||
rep[:id] == @repository.id
|
||||
end
|
||||
return res[:count] if res && res[:count]
|
||||
end
|
||||
end
|
||||
|
||||
def count_search_results
|
||||
@project_search_count = fetch_cached_count Project
|
||||
@project_folder_search_count = fetch_cached_count ProjectFolder
|
||||
@experiment_search_count = fetch_cached_count Experiment
|
||||
@module_search_count = fetch_cached_count MyModule
|
||||
@result_search_count = fetch_cached_count Result
|
||||
@tag_search_count = fetch_cached_count Tag
|
||||
@report_search_count = fetch_cached_count Report
|
||||
@protocol_search_count = fetch_cached_count Protocol
|
||||
@step_search_count = fetch_cached_count Step
|
||||
@checklist_search_count = fetch_cached_count Checklist
|
||||
@repository_search_count_total = count_by_repository
|
||||
@asset_search_count = fetch_cached_count Asset
|
||||
@table_search_count = fetch_cached_count Table
|
||||
@comment_search_count = fetch_cached_count Comment
|
||||
|
||||
@search_results_count = @project_search_count
|
||||
@search_results_count += @project_folder_search_count
|
||||
@search_results_count += @experiment_search_count
|
||||
@search_results_count += @module_search_count
|
||||
@search_results_count += @result_search_count
|
||||
@search_results_count += @tag_search_count
|
||||
@search_results_count += @report_search_count
|
||||
@search_results_count += @protocol_search_count
|
||||
@search_results_count += @step_search_count
|
||||
@search_results_count += @checklist_search_count
|
||||
@search_results_count += @repository_search_count_total
|
||||
@search_results_count += @asset_search_count
|
||||
@search_results_count += @table_search_count
|
||||
@search_results_count += @comment_search_count
|
||||
teams: @teams,
|
||||
users: @users,
|
||||
options: options).size
|
||||
end
|
||||
|
||||
def fetch_cached_count(type)
|
||||
|
@ -410,7 +284,7 @@ class SearchController < ApplicationController
|
|||
Rails.cache.fetch(
|
||||
"#{@search_id}/#{type.name.underscore}_search_count", expires_in: exp
|
||||
) do
|
||||
count_by_name type
|
||||
count_by_name(type)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -439,8 +313,8 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
def search_module_protocols
|
||||
@module_protocol_results = search_by_name(Protocol)
|
||||
@search_count = @module_protocol_results.joins(:my_module).count
|
||||
@module_protocol_results = search_by_name(Protocol, { in_repository: false })
|
||||
@search_count = @module_protocol_results.count
|
||||
end
|
||||
|
||||
def search_results
|
||||
|
@ -449,12 +323,6 @@ class SearchController < ApplicationController
|
|||
@search_count = @result_search_count
|
||||
end
|
||||
|
||||
def search_tags
|
||||
@tag_results = []
|
||||
@tag_results = search_by_name(Tag) if @tag_search_count.positive?
|
||||
@search_count = @tag_search_count
|
||||
end
|
||||
|
||||
def search_reports
|
||||
@report_results = Report.none
|
||||
@report_results = search_by_name(Report) if @report_search_count.positive?
|
||||
|
@ -462,9 +330,8 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
def search_protocols
|
||||
@protocol_results = Protocol.none
|
||||
@protocol_results = search_by_name(Protocol) if @protocol_search_count.positive?
|
||||
@search_count = @protocol_search_count
|
||||
@protocol_results = search_by_name(Protocol, { in_repository: true })
|
||||
@search_count = @protocol_results.count
|
||||
end
|
||||
|
||||
def search_label_templates
|
||||
|
@ -479,29 +346,6 @@ class SearchController < ApplicationController
|
|||
@search_count = @step_search_count
|
||||
end
|
||||
|
||||
def search_checklists
|
||||
@checklist_results = []
|
||||
@checklist_results = search_by_name(Checklist) if @checklist_search_count.positive?
|
||||
@search_count = @checklist_search_count
|
||||
end
|
||||
|
||||
def search_repository
|
||||
@repository = Repository.find_by(id: params[:repository])
|
||||
unless current_user.teams.include?(@repository.team) || @repository.private_shared_with?(current_user.teams)
|
||||
render_403
|
||||
end
|
||||
@repository_results = []
|
||||
if @repository_search_count_total.positive?
|
||||
@repository_results =
|
||||
Repository.search(current_user, @search_query, @search_page,
|
||||
@repository,
|
||||
match_case: @search_case,
|
||||
whole_word: @search_whole_word,
|
||||
whole_phrase: @search_whole_phrase)
|
||||
end
|
||||
@search_count = current_repository_search_count
|
||||
end
|
||||
|
||||
def search_repository_rows
|
||||
@repository_row_results = RepositoryRow.none
|
||||
@repository_row_results = search_by_name(RepositoryRow) if @repository_row_search_count.positive?
|
||||
|
@ -514,15 +358,52 @@ class SearchController < ApplicationController
|
|||
@search_count = @asset_search_count
|
||||
end
|
||||
|
||||
def search_tables
|
||||
@table_results = []
|
||||
@table_results = search_by_name(Table) if @table_search_count.positive?
|
||||
@search_count = @table_search_count
|
||||
def filter_records(records, model)
|
||||
model_name = model.model_name.collection
|
||||
if @filters[:created_at].present?
|
||||
if @filters[:created_at][:on].present?
|
||||
from_date = Time.zone.parse(@filters[:created_at][:on]).beginning_of_day.utc
|
||||
to_date = Time.zone.parse(@filters[:created_at][:on]).end_of_day.utc
|
||||
else
|
||||
from_date = Time.zone.parse(@filters[:created_at][:from])
|
||||
to_date = Time.zone.parse(@filters[:created_at][:to])
|
||||
end
|
||||
|
||||
records = records.where("#{model_name}.created_at >= ?", from_date)
|
||||
records = records.where("#{model_name}.created_at <= ?", to_date)
|
||||
end
|
||||
|
||||
if @filters[:updated_at].present?
|
||||
if @filters[:updated_at][:on].present?
|
||||
from_date = Time.zone.parse(@filters[:updated_at][:on]).beginning_of_day.utc
|
||||
to_date = Time.zone.parse(@filters[:updated_at][:on]).end_of_day.utc
|
||||
else
|
||||
from_date = Time.zone.parse(@filters[:updated_at][:from])
|
||||
to_date = Time.zone.parse(@filters[:updated_at][:to])
|
||||
end
|
||||
|
||||
records = records.where("#{model_name}.updated_at >= ?", from_date)
|
||||
records = records.where("#{model_name}.updated_at <= ?", to_date)
|
||||
end
|
||||
|
||||
if @filters[:users].present?
|
||||
records = records.joins("INNER JOIN activities ON #{model_name}.id = activities.subject_id
|
||||
AND activities.subject_type= '#{model.name}'")
|
||||
.where('activities.owner_id': @filters[:users]&.values)
|
||||
end
|
||||
records
|
||||
end
|
||||
|
||||
def search_comments
|
||||
@comment_results = []
|
||||
@comment_results = search_by_name(Comment) if @comment_search_count.positive?
|
||||
@search_count = @comment_search_count
|
||||
def sort_records(records)
|
||||
case params[:sort]
|
||||
when 'atoz'
|
||||
records.order(name: :asc)
|
||||
when 'ztoa'
|
||||
records.order(name: :desc)
|
||||
when 'created_asc'
|
||||
records.order(created_at: :asc)
|
||||
else
|
||||
records.order(created_at: :desc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ class Asset < ApplicationRecord
|
|||
require 'tempfile'
|
||||
# Lock duration set to 30 minutes
|
||||
LOCK_DURATION = 60 * 30
|
||||
SEARCHABLE_ATTRIBUTES = ['active_storage_blobs.filename', 'asset_text_data.data_vector'].freeze
|
||||
|
||||
enum view_mode: { thumbnail: 0, list: 1, inline: 2 }
|
||||
|
||||
|
@ -58,101 +59,36 @@ class Asset < ApplicationRecord
|
|||
user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
|
||||
teams = user.teams.select(:id)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
|
||||
assets_in_steps = Asset.joins(:step).where(
|
||||
'steps.id IN (?)',
|
||||
Step.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
Step.search(user, include_archived, nil, teams)
|
||||
.select(:id)
|
||||
).pluck(:id)
|
||||
|
||||
assets_in_results = Asset.joins(:result).where(
|
||||
'results.id IN (?)',
|
||||
Result.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.select(:id)
|
||||
Result.search(user, include_archived, nil, teams).select(:id)
|
||||
).pluck(:id)
|
||||
|
||||
assets_in_inventories = Asset.joins(
|
||||
repository_cell: { repository_column: :repository }
|
||||
).where('repositories.team_id IN (?)', teams).pluck(:id)
|
||||
).where(repositories: { team_id: teams }).pluck(:id)
|
||||
|
||||
assets =
|
||||
Asset.distinct
|
||||
.where('assets.id IN (?) OR assets.id IN (?) OR assets.id IN (?)',
|
||||
assets_in_steps, assets_in_results, assets_in_inventories)
|
||||
|
||||
new_query = Asset.left_outer_joins(:asset_text_datum)
|
||||
.joins(file_attachment: :blob)
|
||||
.from(assets, 'assets')
|
||||
|
||||
a_query = s_query = ''
|
||||
|
||||
if options[:whole_word].to_s == 'true' ||
|
||||
options[:whole_phrase].to_s == 'true'
|
||||
like = options[:match_case].to_s == 'true' ? '~' : '~*'
|
||||
s_query = query.gsub(/[!()&|:]/, ' ')
|
||||
.strip
|
||||
.split(/\s+/)
|
||||
.map { |t| t + ':*' }
|
||||
if options[:whole_word].to_s == 'true'
|
||||
a_query = query.split
|
||||
.map { |a| Regexp.escape(a) }
|
||||
.join('|')
|
||||
s_query = s_query.join('|')
|
||||
else
|
||||
a_query = Regexp.escape(query)
|
||||
s_query = s_query.join('&')
|
||||
end
|
||||
a_query = '\\y(' + a_query + ')\\y'
|
||||
s_query = s_query.tr('\'', '"')
|
||||
|
||||
new_query = new_query.where(
|
||||
"(active_storage_blobs.filename #{like} ? " \
|
||||
"OR asset_text_data.data_vector @@ plainto_tsquery(?))",
|
||||
a_query,
|
||||
s_query
|
||||
)
|
||||
else
|
||||
like = options[:match_case].to_s == 'true' ? 'LIKE' : 'ILIKE'
|
||||
a_query = query.split.map { |a| "%#{sanitize_sql_like(a)}%" }
|
||||
|
||||
# Trim whitespace and replace it with OR character. Make prefixed
|
||||
# wildcard search term and escape special characters.
|
||||
# For example, search term 'demo project' is transformed to
|
||||
# 'demo:*|project:*' which makes word inclusive search with postfix
|
||||
# wildcard.
|
||||
s_query = query.gsub(/[!()&|:]/, ' ')
|
||||
.strip
|
||||
.split(/\s+/)
|
||||
.map { |t| t + ':*' }
|
||||
.join('|')
|
||||
.tr('\'', '"')
|
||||
new_query = new_query.where(
|
||||
"(active_storage_blobs.filename #{like} ANY (array[?]) " \
|
||||
"OR asset_text_data.data_vector @@ plainto_tsquery(?))",
|
||||
a_query,
|
||||
s_query
|
||||
)
|
||||
end
|
||||
|
||||
# Show all results if needed
|
||||
if page != Constants::SEARCH_NO_LIMIT
|
||||
new_query = new_query.select('assets.*, asset_text_data.data AS data')
|
||||
.limit(Constants::SEARCH_LIMIT)
|
||||
.offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
Asset.select(
|
||||
"assets_search.*, " \
|
||||
"ts_headline(assets_search.data, plainto_tsquery('#{sanitize_sql_for_conditions(s_query)}'), " \
|
||||
"'StartSel=<mark>, StopSel=</mark>') AS headline"
|
||||
).from(new_query, 'assets_search')
|
||||
else
|
||||
new_query
|
||||
end
|
||||
Asset.left_outer_joins(:asset_text_datum)
|
||||
.joins(file_attachment: :blob)
|
||||
.from(assets, 'assets')
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
end
|
||||
|
||||
def blob
|
||||
|
|
|
@ -31,28 +31,6 @@ class Checklist < ApplicationRecord
|
|||
|
||||
scope :asc, -> { order('checklists.created_at ASC') }
|
||||
|
||||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
options = {})
|
||||
step_ids = Step.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
|
||||
new_query = Checklist.distinct
|
||||
.where(checklists: { step_id: step_ids })
|
||||
.left_outer_joins(:checklist_items)
|
||||
.where_attributes_like(['checklists.name', 'checklist_items.text'], query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
end
|
||||
|
||||
def duplicate(step, user, position = nil)
|
||||
ActiveRecord::Base.transaction do
|
||||
new_checklist = step.checklists.create!(
|
||||
|
|
|
@ -12,45 +12,6 @@ class Comment < ApplicationRecord
|
|||
|
||||
scope :unseen_by, ->(user) { where('? = ANY (unseen_by)', user.id) }
|
||||
|
||||
def self.search(
|
||||
user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
project_ids = Project.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
my_module_ids = MyModule.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
step_ids = Step.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
result_ids = Result.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
|
||||
new_query = Comment.distinct
|
||||
.joins(:user)
|
||||
.where(
|
||||
'(comments.associated_id IN (?) AND comments.type = ?) OR ' \
|
||||
'(comments.associated_id IN (?) AND comments.type = ?) OR ' \
|
||||
'(comments.associated_id IN (?) AND comments.type = ?) OR ' \
|
||||
'(comments.associated_id IN (?) AND comments.type = ?)',
|
||||
project_ids, 'ProjectComment',
|
||||
my_module_ids, 'TaskComment',
|
||||
step_ids, 'StepComment',
|
||||
result_ids, 'ResultComment'
|
||||
)
|
||||
.where_attributes_like(['message', 'users.full_name'], query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
end
|
||||
|
||||
def self.mark_as_seen_by(user, commentable)
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
all.where('? = ANY (unseen_by)', user.id).update_all("unseen_by = array_remove(unseen_by, #{user.id.to_i}::bigint)")
|
||||
|
|
|
@ -10,18 +10,7 @@ module SearchableModel
|
|||
scope :where_attributes_like, lambda { |attributes, query, options = {}|
|
||||
return unless query
|
||||
|
||||
attrs = []
|
||||
if attributes.blank?
|
||||
# Do nothing in this case
|
||||
elsif attributes.is_a? Symbol
|
||||
attrs = [attributes.to_s]
|
||||
elsif attributes.is_a? String
|
||||
attrs = [attributes]
|
||||
elsif attributes.is_a? Array
|
||||
attrs = attributes.collect(&:to_s)
|
||||
else
|
||||
raise ArgumentError, ':attributes must be an array, symbol or string'
|
||||
end
|
||||
attrs = convert_input(attributes)
|
||||
|
||||
if options[:whole_word].to_s == 'true' ||
|
||||
options[:whole_phrase].to_s == 'true' ||
|
||||
|
@ -109,5 +98,100 @@ module SearchableModel
|
|||
end
|
||||
end
|
||||
}
|
||||
|
||||
scope :where_attributes_like_boolean, lambda { |attributes, query, options = {}|
|
||||
return unless query
|
||||
|
||||
attrs = convert_input(attributes)
|
||||
where_array = []
|
||||
value_array = {}
|
||||
current_phrase = ''
|
||||
exact_match = false
|
||||
negate = false
|
||||
index = 0
|
||||
|
||||
query.split.each do |phrase|
|
||||
phrase = phrase.strip
|
||||
if phrase.start_with?('"') && phrase.ends_with?('"')
|
||||
create_query(attrs, index, negate, where_array, value_array, phrase[1..-2], true)
|
||||
negate = false
|
||||
elsif phrase.start_with?('"')
|
||||
exact_match = true
|
||||
current_phrase = phrase[1..]
|
||||
elsif exact_match && phrase.ends_with?('"')
|
||||
exact_match = false
|
||||
create_query(attrs, index, negate, where_array, value_array, "#{current_phrase} #{phrase[0..-2]}", true)
|
||||
current_phrase = ''
|
||||
negate = false
|
||||
elsif exact_match
|
||||
current_phrase = "#{current_phrase} #{phrase}"
|
||||
elsif phrase.casecmp('and').zero?
|
||||
next
|
||||
elsif phrase.casecmp('not').zero?
|
||||
negate = true
|
||||
elsif phrase.casecmp('or').zero?
|
||||
where_array[-1] = "#{where_array.last[0..-5]} OR "
|
||||
else
|
||||
create_query(attrs, index, negate, where_array, value_array, "%#{phrase}%")
|
||||
negate = false
|
||||
end
|
||||
index += 1
|
||||
end
|
||||
|
||||
if current_phrase.present?
|
||||
current_phrase = current_phrase[0..-2] if current_phrase.ends_with?('"')
|
||||
create_query(attrs, index, negate, where_array, value_array, current_phrase, true)
|
||||
end
|
||||
|
||||
where(where_array.join[0..-5], value_array)
|
||||
}
|
||||
|
||||
def self.convert_input(attributes)
|
||||
attrs = []
|
||||
if attributes.blank?
|
||||
# Do nothing in this case
|
||||
elsif attributes.is_a? Symbol
|
||||
attrs = [attributes.to_s]
|
||||
elsif attributes.is_a? String
|
||||
attrs = [attributes]
|
||||
elsif attributes.is_a? Array
|
||||
attrs = attributes.collect(&:to_s)
|
||||
else
|
||||
raise ArgumentError, ':attributes must be an array, symbol or string'
|
||||
end
|
||||
|
||||
attrs
|
||||
end
|
||||
|
||||
def self.create_query(attrs, index, negate, where_array, value_array, phrase, exact_match=false)
|
||||
like = exact_match ? '~' : 'ILIKE'
|
||||
phrase = "\\m#{phrase}\\M" if exact_match
|
||||
|
||||
where_clause = (attrs.map.with_index do |a, i|
|
||||
i = (index * attrs.count) + i
|
||||
if %w(repository_rows.id repository_number_values.data).include?(a)
|
||||
"#{a} IS NOT NULL AND (((#{a})::text) #{like} :t#{i}) OR "
|
||||
elsif defined?(model::PREFIXED_ID_SQL) && a == model::PREFIXED_ID_SQL
|
||||
"#{a} IS NOT NULL AND (#{a} #{like} :t#{i}) OR "
|
||||
elsif a == 'asset_text_data.data_vector'
|
||||
"asset_text_data.data_vector @@ plainto_tsquery(:t#{i})) OR"
|
||||
else
|
||||
"#{a} IS NOT NULL AND ((trim_html_tags(#{a})) #{like} :t#{i}) OR "
|
||||
end
|
||||
end).join[0..-5]
|
||||
|
||||
where_array << if negate
|
||||
"NOT (#{where_clause}) AND "
|
||||
else
|
||||
"(#{where_clause}) AND "
|
||||
end
|
||||
|
||||
value_array.merge!(
|
||||
(attrs.map.with_index do |_, i|
|
||||
i = (index * attrs.count) + i
|
||||
["t#{i}".to_sym, phrase]
|
||||
end).to_h
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,24 +64,18 @@ class Experiment < ApplicationRecord
|
|||
user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
viewable_projects = Project.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT, current_team)
|
||||
.pluck(:id)
|
||||
new_query = Experiment.with_granted_permissions(user, ExperimentPermissions::READ)
|
||||
.where(project: viewable_projects)
|
||||
.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
|
||||
new_query = distinct.with_granted_permissions(user, ExperimentPermissions::READ)
|
||||
.where(user_assignments: { team: teams })
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query
|
||||
end
|
||||
|
||||
def self.viewable_by_user(user, teams)
|
||||
|
|
|
@ -37,22 +37,15 @@ class LabelTemplate < ApplicationRecord
|
|||
end
|
||||
|
||||
def self.search(
|
||||
_user,
|
||||
user,
|
||||
_include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
|
||||
new_query = LabelTemplate.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
distinct.viewable_by_user(user, teams)
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
end
|
||||
|
||||
def icon
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
class MyModule < ApplicationRecord
|
||||
ID_PREFIX = 'TA'
|
||||
include PrefixedIdModel
|
||||
SEARCHABLE_ATTRIBUTES = ['my_modules.name', 'my_modules.description', PREFIXED_ID_SQL].freeze
|
||||
SEARCHABLE_ATTRIBUTES = ['my_modules.name', 'my_modules.description', PREFIXED_ID_SQL,
|
||||
'comments.message', 'tags.name', 'users.full_name', 'users.email'].freeze
|
||||
|
||||
include ArchivableModel
|
||||
include SearchableModel
|
||||
|
@ -108,25 +109,18 @@ class MyModule < ApplicationRecord
|
|||
user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
viewable_experiments = Experiment.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT, current_team)
|
||||
.pluck(:id)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
|
||||
new_query = MyModule.with_granted_permissions(user, MyModulePermissions::READ)
|
||||
.where(experiment: viewable_experiments)
|
||||
.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
new_query = distinct.left_joins(:task_comments, my_module_tags: :tag, user_my_modules: :user)
|
||||
.with_granted_permissions(user, MyModulePermissions::READ)
|
||||
.where(user_assignments: { team: teams })
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query
|
||||
end
|
||||
|
||||
def self.viewable_by_user(user, teams)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class Project < ApplicationRecord
|
||||
ID_PREFIX = 'PR'
|
||||
include PrefixedIdModel
|
||||
SEARCHABLE_ATTRIBUTES = ['projects.name', PREFIXED_ID_SQL].freeze
|
||||
SEARCHABLE_ATTRIBUTES = ['projects.name', PREFIXED_ID_SQL, 'comments.message'].freeze
|
||||
|
||||
include ArchivableModel
|
||||
include SearchableModel
|
||||
|
@ -84,21 +84,17 @@ class Project < ApplicationRecord
|
|||
user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
new_query = distinct.viewable_by_user(user, teams)
|
||||
.left_joins(:project_comments)
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
new_query = Project.viewable_by_user(user, current_team || user.teams)
|
||||
.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query
|
||||
end
|
||||
|
||||
def self.viewable_by_user(user, teams)
|
||||
|
|
|
@ -39,21 +39,14 @@ class ProjectFolder < ApplicationRecord
|
|||
.where(team: teams)
|
||||
end
|
||||
|
||||
def self.search(user, _include_archived, query = nil, page = 1, current_team = nil, options = {})
|
||||
new_query = if current_team
|
||||
current_team.project_folders.where_attributes_like(:name, query, options)
|
||||
else
|
||||
distinct.joins(team: :users)
|
||||
.where(teams: { user_assignments: { user: user } })
|
||||
.where_attributes_like('project_folders.name', query, options)
|
||||
end
|
||||
def self.search(user, include_archived, query = nil, current_team = nil, options = {})
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query = distinct.viewable_by_user(user, teams)
|
||||
.where_attributes_like_boolean('project_folders.name', query, options)
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
new_query
|
||||
end
|
||||
|
||||
def self.inner_folders(team, project_folder = nil)
|
||||
|
|
|
@ -6,7 +6,8 @@ class Protocol < ApplicationRecord
|
|||
include ArchivableModel
|
||||
include PrefixedIdModel
|
||||
SEARCHABLE_ATTRIBUTES = ['protocols.name', 'protocols.description',
|
||||
'protocols.authors', 'protocol_keywords.name', PREFIXED_ID_SQL].freeze
|
||||
'step_texts.name', 'step_texts.text', 'tables.name',
|
||||
'checklists.name', 'checklist_items.text', PREFIXED_ID_SQL].freeze
|
||||
REPOSITORY_TYPES = %i(in_repository_published_original in_repository_draft in_repository_published_version).freeze
|
||||
|
||||
include SearchableModel
|
||||
|
@ -165,34 +166,27 @@ class Protocol < ApplicationRecord
|
|||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {})
|
||||
repository_protocols = latest_available_versions(user.teams)
|
||||
.with_granted_permissions(user, ProtocolPermissions::READ)
|
||||
.select(:id)
|
||||
repository_protocols = repository_protocols.active unless include_archived
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
new_query = if options[:options][:in_repository]
|
||||
latest_available_versions(teams)
|
||||
.with_granted_permissions(user, ProtocolPermissions::READ)
|
||||
else
|
||||
distinct.joins(:my_module)
|
||||
.joins("INNER JOIN user_assignments my_module_user_assignments " \
|
||||
"ON my_module_user_assignments.assignable_type = 'MyModule' " \
|
||||
"AND my_module_user_assignments.assignable_id = my_modules.id")
|
||||
.where(my_module_user_assignments: { user_id: user })
|
||||
.where(team: teams)
|
||||
end
|
||||
|
||||
module_ids = MyModule.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT).pluck(:id)
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
new_query = Protocol
|
||||
.where(
|
||||
'(protocol_type IN (?) AND my_module_id IN (?)) OR (protocols.id IN (?))',
|
||||
[Protocol.protocol_types[:unlinked], Protocol.protocol_types[:linked]],
|
||||
module_ids,
|
||||
repository_protocols
|
||||
)
|
||||
|
||||
new_query = new_query.left_outer_joins(:protocol_keywords)
|
||||
.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
.distinct
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query.left_outer_joins(steps: [:step_texts, { step_tables: :table },
|
||||
{ checklists: :checklist_items }, :step_comments])
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
.distinct
|
||||
end
|
||||
|
||||
def self.latest_available_versions(teams)
|
||||
|
|
|
@ -68,26 +68,15 @@ class Report < ApplicationRecord
|
|||
|
||||
def self.search(
|
||||
user,
|
||||
include_archived,
|
||||
_include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {}
|
||||
)
|
||||
|
||||
project_ids = Project.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
|
||||
new_query = Report.distinct
|
||||
.where(reports: { project_id: project_ids })
|
||||
.where_attributes_like(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
distinct.with_granted_permissions(user, ReportPermissions::READ)
|
||||
.where(team: teams)
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
end
|
||||
|
||||
def self.viewable_by_user(user, teams)
|
||||
|
|
|
@ -78,36 +78,6 @@ class Repository < RepositoryBase
|
|||
team.repositories.count < Rails.configuration.x.team_repositories_limit
|
||||
end
|
||||
|
||||
def self.search(
|
||||
user,
|
||||
query = nil,
|
||||
page = 1,
|
||||
repository = nil,
|
||||
options = {}
|
||||
)
|
||||
serchable_row_fields = [RepositoryRow::PREFIXED_ID_SQL, 'repository_rows.name', 'users.full_name']
|
||||
|
||||
repositories = repository&.id || Repository.accessible_by_teams(user.teams).pluck(:id)
|
||||
|
||||
readable_rows = RepositoryRow.joins(:repository, :created_by).where(repository_id: repositories)
|
||||
|
||||
repository_rows = readable_rows.where_attributes_like(serchable_row_fields, query, options)
|
||||
|
||||
Extends::REPOSITORY_EXTRA_SEARCH_ATTR.each do |_data_type, config|
|
||||
custom_cell_matches = readable_rows.joins(config[:includes])
|
||||
.where_attributes_like(config[:field], query, options)
|
||||
repository_rows = repository_rows.or(readable_rows.where(id: custom_cell_matches))
|
||||
end
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
repository_rows.select('repositories.id AS id, COUNT(DISTINCT repository_rows.id) AS counter')
|
||||
.group('repositories.id')
|
||||
else
|
||||
repository_rows.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
end
|
||||
|
||||
def self.filter_by_teams(teams = [])
|
||||
teams.blank? ? self : where(team: teams)
|
||||
end
|
||||
|
|
|
@ -123,30 +123,28 @@ class RepositoryRow < ApplicationRecord
|
|||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
options = {})
|
||||
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
searchable_row_fields = [RepositoryRow::PREFIXED_ID_SQL, 'repository_rows.name', 'users.full_name']
|
||||
repositories = Repository.search(user).pluck(:id)
|
||||
|
||||
new_query =
|
||||
RepositoryRow
|
||||
.joins(:repository, :created_by)
|
||||
.where(repository_id: repositories)
|
||||
.distinct
|
||||
.where_attributes_like(
|
||||
searchable_row_fields, query, options
|
||||
)
|
||||
readable_rows = distinct.joins(:repository, :created_by)
|
||||
.joins("INNER JOIN user_assignments repository_user_assignments " \
|
||||
"ON repository_user_assignments.assignable_type = 'RepositoryBase' " \
|
||||
"AND repository_user_assignments.assignable_id = repositories.id")
|
||||
.where(repository_user_assignments: { user_id: user, team_id: teams })
|
||||
|
||||
new_query = new_query.active unless include_archived
|
||||
readable_rows = readable_rows.active unless include_archived
|
||||
repository_rows = readable_rows.where_attributes_like_boolean(searchable_row_fields, query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
Extends::REPOSITORY_EXTRA_SEARCH_ATTR.each do |_data_type, config|
|
||||
custom_cell_matches = readable_rows.joins(config[:includes])
|
||||
.where_attributes_like_boolean(config[:field], query, options)
|
||||
repository_rows = repository_rows.or(readable_rows.where(id: custom_cell_matches))
|
||||
end
|
||||
|
||||
repository_rows
|
||||
end
|
||||
|
||||
def self.filter_by_teams(teams = [])
|
||||
|
|
|
@ -9,6 +9,9 @@ class Result < ApplicationRecord
|
|||
auto_strip_attributes :name, nullify: false
|
||||
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
|
||||
SEARCHABLE_ATTRIBUTES = ['results.name', 'result_texts.name', 'result_texts.text',
|
||||
'tables.name', 'comments.message'].freeze
|
||||
|
||||
enum assets_view_mode: { thumbnail: 0, list: 1, inline: 2 }
|
||||
|
||||
belongs_to :user, inverse_of: :results
|
||||
|
@ -35,33 +38,21 @@ class Result < ApplicationRecord
|
|||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {})
|
||||
module_ids = MyModule.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT).pluck(:id)
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
|
||||
new_query =
|
||||
Result
|
||||
.distinct
|
||||
.left_outer_joins(:result_texts, result_tables: :table)
|
||||
.where(results: { my_module_id: module_ids })
|
||||
.where_attributes_like(
|
||||
[
|
||||
'results.name',
|
||||
'result_texts.name',
|
||||
'result_texts.text',
|
||||
'tables.name'
|
||||
], query, options
|
||||
)
|
||||
new_query = distinct.left_joins(:result_comments, :result_texts, result_tables: :table)
|
||||
.joins(:my_module)
|
||||
.joins("INNER JOIN user_assignments my_module_user_assignments " \
|
||||
"ON my_module_user_assignments.assignable_type = 'MyModule' " \
|
||||
"AND my_module_user_assignments.assignable_id = my_modules.id")
|
||||
.where(my_module_user_assignments: { user_id: user, team_id: teams })
|
||||
.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, options)
|
||||
|
||||
new_query = new_query.active unless include_archived
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
new_query
|
||||
end
|
||||
|
||||
def duplicate(my_module, user, result_name: nil)
|
||||
|
|
|
@ -61,23 +61,22 @@ class Step < ApplicationRecord
|
|||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
current_team = nil,
|
||||
options = {})
|
||||
protocol_ids = Protocol.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
|
||||
teams = options[:teams] || current_team || user.teams.select(:id)
|
||||
protocol_ids = Protocol.search(user, include_archived, nil, teams,
|
||||
options: { in_repository: false })
|
||||
.pluck(:id)
|
||||
my_module_ids = Protocol.search(user, include_archived, nil, teams,
|
||||
options: { in_repository: true })
|
||||
.pluck(:id)
|
||||
|
||||
new_query = Step.distinct
|
||||
.left_outer_joins(:step_texts)
|
||||
.where(steps: { protocol_id: protocol_ids })
|
||||
.where_attributes_like(['steps.name', 'step_texts.text'], query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
Step.distinct
|
||||
.where(protocol_id: protocol_ids + my_module_ids)
|
||||
.left_outer_joins(:step_texts)
|
||||
.where(steps: { protocol_id: protocol_ids })
|
||||
.where_attributes_like_boolean(['steps.name', 'step_texts.text'], query, options)
|
||||
end
|
||||
|
||||
def self.filter_by_teams(teams = [])
|
||||
|
|
|
@ -29,82 +29,6 @@ class Table < ApplicationRecord
|
|||
after_save :update_ts_index
|
||||
after_save { result&.touch; step&.touch }
|
||||
|
||||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
options = {})
|
||||
step_ids = Step.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.joins(:step_tables)
|
||||
.distinct
|
||||
.pluck('step_tables.id')
|
||||
|
||||
result_ids = Result.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.joins(:result_tables)
|
||||
.distinct
|
||||
.pluck('result_tables.id')
|
||||
|
||||
table_query = Table.distinct
|
||||
.left_outer_joins(:step_table, :result_table, :result)
|
||||
.where('step_tables.id IN (?) OR result_tables.id IN (?)', step_ids, result_ids)
|
||||
|
||||
if options[:whole_word].to_s == 'true' ||
|
||||
options[:whole_phrase].to_s == 'true'
|
||||
like = options[:match_case].to_s == 'true' ? '~' : '~*'
|
||||
s_query = query.gsub(/[!()&|:]/, ' ')
|
||||
.strip
|
||||
.split(/\s+/)
|
||||
.map { |t| t + ':*' }
|
||||
if options[:whole_word].to_s == 'true'
|
||||
a_query = query.split
|
||||
.map { |a| Regexp.escape(a) }
|
||||
.join('|')
|
||||
s_query = s_query.join('|')
|
||||
else
|
||||
a_query = Regexp.escape(query)
|
||||
s_query = s_query.join('&')
|
||||
end
|
||||
a_query = '\\y(' + a_query + ')\\y'
|
||||
s_query = s_query.tr('\'', '"')
|
||||
|
||||
new_query = table_query.where(
|
||||
"(trim_html_tags(tables.name) #{like} ?" \
|
||||
"OR tables.data_vector @@ to_tsquery(?))",
|
||||
a_query,
|
||||
s_query
|
||||
)
|
||||
else
|
||||
like = options[:match_case].to_s == 'true' ? 'LIKE' : 'ILIKE'
|
||||
a_query = query.split.map { |a| "%#{sanitize_sql_like(a)}%" }
|
||||
|
||||
# Trim whitespace and replace it with OR character. Make prefixed
|
||||
# wildcard search term and escape special characters.
|
||||
# For example, search term 'demo project' is transformed to
|
||||
# 'demo:*|project:*' which makes word inclusive search with postfix
|
||||
# wildcard.
|
||||
s_query = query.gsub(/[!()&|:]/, ' ')
|
||||
.strip
|
||||
.split(/\s+/)
|
||||
.map { |t| t + ':*' }
|
||||
.join('|')
|
||||
.tr('\'', '"')
|
||||
new_query = table_query.where(
|
||||
"(trim_html_tags(tables.name) #{like} ANY (array[?])" \
|
||||
"OR tables.data_vector @@ to_tsquery(?))",
|
||||
a_query,
|
||||
s_query
|
||||
)
|
||||
end
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
end
|
||||
|
||||
def metadata
|
||||
attributes['metadata'].is_a?(String) ? JSON.parse(attributes['metadata']) : attributes['metadata']
|
||||
end
|
||||
|
|
|
@ -17,26 +17,4 @@ class Tag < ApplicationRecord
|
|||
belongs_to :project
|
||||
has_many :my_module_tags, inverse_of: :tag, dependent: :destroy
|
||||
has_many :my_modules, through: :my_module_tags, dependent: :destroy
|
||||
|
||||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
page = 1,
|
||||
_current_team = nil,
|
||||
options = {})
|
||||
project_ids = Project.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
||||
.pluck(:id)
|
||||
|
||||
new_query = Tag
|
||||
.distinct
|
||||
.where(tags: { project_id: project_ids })
|
||||
.where_attributes_like(:name, query, options)
|
||||
|
||||
# Show all results if needed
|
||||
if page == Constants::SEARCH_NO_LIMIT
|
||||
new_query
|
||||
else
|
||||
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module GlobalSearch
|
|||
|
||||
def created_by
|
||||
{
|
||||
name: object.created_by ? object.created_by.name : object.created_by_user,
|
||||
name: object.type == 'FluicsLabelTemplate' ? object.created_by_user : object.created_by&.name,
|
||||
avatar_url: object.created_by ? avatar_path(object.created_by, :icon_small) : nil
|
||||
}
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue