diff --git a/config/initializers/ransack.rb b/config/initializers/ransack.rb index 6bba9dfbc1..cbbf303d8b 100644 --- a/config/initializers/ransack.rb +++ b/config/initializers/ransack.rb @@ -5,10 +5,18 @@ module Predications # rubocop:disable Style/Documentation def contains_key(other) Arel::Nodes::InfixOperation.new(:'?', self, Arel::Nodes::Quoted.new(other)) end + + # contains but case insensitive + def contains_ci(other) + Arel::Nodes::Contains.new( + Arel::Nodes::SqlLiteral.new("lower(\"#{relation.name}\".\"#{name}\"::text)::jsonb"), + Arel::Nodes.build_quoted(other, self) + ) + end end end Ransack.configure do |config| - config.add_predicate 'jcont', arel_predicate: 'contains', formatter: proc { |v| JSON.parse(v.to_s) } - config.add_predicate 'jcont_key', arel_predicate: 'contains_key' + config.add_predicate 'jcont', arel_predicate: 'contains_ci', formatter: proc { |v| JSON.parse(v.to_s.downcase) } + config.add_predicate 'jcont_key', arel_predicate: 'contains_key', formatter: proc { |v| v.to_s.downcase } end diff --git a/db/migrate/20241003125314_update_metadata_indexes.rb b/db/migrate/20241003125314_update_metadata_indexes.rb new file mode 100644 index 0000000000..0c9ed61838 --- /dev/null +++ b/db/migrate/20241003125314_update_metadata_indexes.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Update metadata index for attachments and samples to be on lower case +class UpdateMetadataIndexes < ActiveRecord::Migration[7.2] + def change # rubocop:disable Metrics/MethodLength + reversible do |dir| + dir.up do + execute <<~SQL.squish + DROP INDEX index_attachments_on_metadata; + CREATE INDEX index_attachments_on_metadata_ci ON attachments USING GIN ((LOWER(metadata::text)::jsonb)); + + DROP INDEX index_samples_on_metadata; + CREATE INDEX index_samples_on_metadata_ci ON samples USING GIN ((LOWER(metadata::text)::jsonb)); + SQL + end + + dir.down do + execute <<~SQL.squish + DROP INDEX index_attachments_on_metadata_ci; + DROP INDEX index_samples_on_metadata_ci; + SQL + add_index :attachments, :metadata, using: :gin + add_index :samples, :metadata, using: :gin + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9ffd626b38..3bd6139984 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_09_25_155747) do +ActiveRecord::Schema[7.2].define(version: 2024_10_03_125314) do # These are extensions that must be enabled in order to support this database enable_extension "hstore" enable_extension "plpgsql" @@ -74,9 +74,9 @@ t.jsonb "log_data" t.uuid "attachable_id", null: false t.string "puid", null: false + t.index "((lower((metadata)::text))::jsonb)", name: "index_attachments_on_metadata_ci", using: :gin t.index ["attachable_id"], name: "index_attachments_on_attachable_id" t.index ["created_at"], name: "index_attachments_on_created_at" - t.index ["metadata"], name: "index_attachments_on_metadata", using: :gin t.index ["puid"], name: "index_attachments_on_puid" end @@ -238,10 +238,10 @@ t.string "puid", null: false t.uuid "project_id", null: false t.datetime "attachments_updated_at" + t.index "((lower((metadata)::text))::jsonb)", name: "index_samples_on_metadata_ci", using: :gin t.index ["created_at"], name: "index_samples_on_created_at" t.index ["deleted_at"], name: "index_samples_on_deleted_at" t.index ["id", "project_id"], name: "index_samples_on_id_and_project_id", unique: true - t.index ["metadata"], name: "index_samples_on_metadata", using: :gin t.index ["metadata_provenance"], name: "index_samples_on_metadata_provenance", using: :gin t.index ["name", "project_id"], name: "index_sample_name_with_project", unique: true, where: "(deleted_at IS NULL)" t.index ["project_id"], name: "index_samples_on_project_id" diff --git a/test/graphql/samples_query_ransack_test.rb b/test/graphql/samples_query_ransack_test.rb index c1dc719ac6..2aa9e6aa50 100644 --- a/test/graphql/samples_query_ransack_test.rb +++ b/test/graphql/samples_query_ransack_test.rb @@ -192,6 +192,18 @@ def setup assert_equal 4, data.count end + test 'ransack samples query with metadata_jcont_key filter should work ignoring case' do + result = IridaSchema.execute(SAMPLES_RANSACK_QUERY, + context: { current_user: @user }, + variables: { filter: { metadata_jcont_key: 'MetadataField1' } }) + + assert_nil result['errors'], 'should work and have no errors.' + + data = result['data']['samples']['nodes'] + + assert_equal 4, data.count + end + test 'ransack samples query with metadata_jcont filter should work' do result = IridaSchema.execute(SAMPLES_RANSACK_QUERY, context: { current_user: @user }, @@ -204,6 +216,18 @@ def setup assert_equal 4, data.count end + test 'ransack samples query with metadata_jcont filter should work ignoring case' do + result = IridaSchema.execute(SAMPLES_RANSACK_QUERY, + context: { current_user: @user }, + variables: { filter: { metadata_jcont: { MetadataField1: 'Value1' } } }) + + assert_nil result['errors'], 'should work and have no errors.' + + data = result['data']['samples']['nodes'] + + assert_equal 4, data.count + end + test 'ransack samples query with metadata_jcont_key filter should work with non_existent key' do result = IridaSchema.execute(SAMPLES_RANSACK_QUERY, context: { current_user: @user },