Improve filtering in protocols datatable [SCI-8020] (#5068)

This commit is contained in:
Alex Kriuchykhin 2023-03-07 09:42:18 +01:00 committed by GitHub
parent b2cf5548f6
commit 00c602a818
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -54,8 +54,8 @@ class ProtocolsDatatable < CustomDatatable
def as_json(_options = {})
{
draw: dt_params[:draw].to_i,
recordsTotal: get_raw_records.length,
recordsFiltered: filter_records(get_raw_records).length,
recordsTotal: get_raw_records_base.distinct.count,
recordsFiltered: records.present? ? records.first.filtered_count : 0,
data: data
}
end
@ -79,7 +79,7 @@ class ProtocolsDatatable < CustomDatatable
casted_column = ::Arel::Nodes::NamedFunction.new('CAST',
[model.arel_table[column.to_sym].as(typecast)])
end
casted_column.matches("%#{value}%")
casted_column.matches("%#{ActiveRecord::Base.sanitize_sql_like(value)}%")
end
private
@ -108,6 +108,10 @@ class ProtocolsDatatable < CustomDatatable
end
end
def fetch_records
super.select('COUNT("protocols"."id") OVER() AS filtered_count')
end
def filter_protocols_records(records)
if params[:name_and_keywords].present?
records = records.where_attributes_like(['protocols.name', 'protocol_keywords.name'], params[:name_and_keywords])
@ -164,8 +168,16 @@ class ProtocolsDatatable < CustomDatatable
"(#{new_drafts.to_sql}))"
)
records = @type == :archived ? records.archived : records.active
records.with_granted_permissions(@user, ProtocolPermissions::READ)
end
# Query database for records (this will be later paginated and filtered)
# after that "data" function will return json
def get_raw_records
records =
records
get_raw_records_base
.preload(:parent, :latest_published_version, :draft, :protocol_keywords, user_assignments: %i(user user_role))
.joins("LEFT OUTER JOIN protocols protocol_versions " \
"ON protocol_versions.protocol_type = #{Protocol.protocol_types[:in_repository_published_version]} " \
@ -174,26 +186,16 @@ class ProtocolsDatatable < CustomDatatable
'ON "protocol_protocol_keywords"."protocol_id" = "protocols"."id"')
.joins('LEFT OUTER JOIN "protocol_keywords" ' \
'ON "protocol_protocol_keywords"."protocol_keyword_id" = "protocol_keywords"."id"')
.with_granted_permissions(@user, ProtocolPermissions::READ)
records = records.joins('LEFT OUTER JOIN "users" "archived_users"
ON "archived_users"."id" = "protocols"."archived_by_id"')
records = records.joins('LEFT OUTER JOIN "users" ON "users"."id" = "protocols"."published_by_id"')
records = @type == :archived ? records.archived : records.active
.joins('LEFT OUTER JOIN "users" "archived_users" ON "archived_users"."id" = "protocols"."archived_by_id"')
.joins('LEFT OUTER JOIN "users" ON "users"."id" = "protocols"."published_by_id"')
.group('"protocols"."id"')
records = filter_protocols_records(records)
records.group('"protocols"."id"')
end
# Query database for records (this will be later paginated and filtered)
# after that "data" function will return json
def get_raw_records
get_raw_records_base.select(
records.select(
'"protocols".*',
'COALESCE("protocols"."parent_id", "protocols"."id") AS adjusted_parent_id',
'STRING_AGG(DISTINCT("protocol_keywords"."name"), \', \') AS "protocol_keywords_str"',
"CASE WHEN protocols.protocol_type = #{Protocol.protocol_types[:in_repository_draft]}" \
"CASE WHEN protocols.protocol_type = #{Protocol.protocol_types[:in_repository_draft]} " \
"THEN COUNT(DISTINCT(\"protocol_versions\".\"id\")) ELSE COUNT(DISTINCT(\"protocol_versions\".\"id\")) + 1 " \
"END AS nr_of_versions",
'COUNT("user_assignments"."id") AS "nr_of_assigned_users"',
@ -258,36 +260,4 @@ class ProtocolsDatatable < CustomDatatable
def modified_timestamp(record)
I18n.l(record.updated_at, format: :full)
end
# OVERRIDE - This is only called when filtering results;
# when using GROUP BY function, SQL cannot perform a WHERE
# clause on aggregated columns (protocol keywords & users' full_name), but
# since we want those 2 columns to be searchable/filterable, we do an "inner"
# query where we select only protocol IDs which are filtered by those 2 columns
# using HAVING keyword (which is the correct way to filter aggregated columns).
# Another OR is then appended to the WHERE clause, checking if protocol is inside
# this list of IDs.
# def build_conditions_for(query)
# # Inner query to retrieve list of protocol IDs where concatenated
# # protocol keywords string, or user's full_name contains searched query
# search_val = dt_params[:search][:value]
# records_having = get_raw_records_base.having(
# ::Arel::Nodes::NamedFunction.new(
# 'CAST',
# [::Arel::Nodes::SqlLiteral.new("string_agg(\"protocol_keywords\".\"name\", ' ') AS #{typecast}")]
# ).matches("%#{sanitize_sql_like(search_val)}%").to_sql +
# " OR " +
# ::Arel::Nodes::NamedFunction.new(
# 'CAST',
# [::Arel::Nodes::SqlLiteral.new("max(\"users\".\"full_name\") AS #{typecast}")]
# ).matches("%#{sanitize_sql_like(search_val)}%").to_sql
# ).select(:id)
# # Call parent function
# criteria = super(query)
# # Aight, now append another or
# criteria = criteria.or(Protocol.arel_table[:id].in(records_having.arel))
# criteria
# end
end