X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/667a7121e08d4fffc24cafdc3ed474374782b959..15c688d1c4b41232536d0a275dd5c0fdb8879a00:/services/api/lib/record_filters.rb diff --git a/services/api/lib/record_filters.rb b/services/api/lib/record_filters.rb index ce9fe6b13a..831e357b42 100644 --- a/services/api/lib/record_filters.rb +++ b/services/api/lib/record_filters.rb @@ -63,12 +63,18 @@ module RecordFilters attrs.each do |attr| subproperty = attr.split(".", 2) - if !model_class.searchable_columns(operator).index subproperty[0] - raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' in filter") - end + col = model_class.columns.select { |c| c.name == subproperty[0] }.first if subproperty.length == 2 - # jsonb search + if col.nil? or col.type != :jsonb + raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' for subproperty filter") + end + + if subproperty[1][0] == "<" and subproperty[1][-1] == ">" + subproperty[1] = subproperty[1][1..-2] + end + + # jsonb search case operator.downcase when '=', '!=' not_in = if operator.downcase == "!=" then "NOT " else "" end @@ -84,32 +90,48 @@ module RecordFilters raise ArgumentError.new("Invalid operand type '#{operand.class}' "\ "for '#{operator}' operator in filters") end - # when '<', '<=', '>', '>=' - # cond_out << "#{ar_table_name}.#{subproperty[0]}->? #{operator} ?::jsonb" - # param_out << subproperty[1] - # param_out << SafeJSON.dump(operand) + when '<', '<=', '>', '>=' + cond_out << "#{ar_table_name}.#{subproperty[0]}->? #{operator} ?::jsonb" + param_out << subproperty[1] + param_out << SafeJSON.dump(operand) when 'like', 'ilike' cond_out << "#{ar_table_name}.#{subproperty[0]}->>? #{operator} ?" + param_out << subproperty[1] param_out << operand when 'not in' if operand.is_a? Array - cond_out << "#{ar_table_name}.#{subproperty[0]}->>? NOT IN (?)" + cond_out << "#{ar_table_name}.#{subproperty[0]}->>? NOT IN (?) OR #{ar_table_name}.#{subproperty[0]}->>? IS NULL" param_out << subproperty[1] param_out << operand + param_out << subproperty[1] else raise ArgumentError.new("Invalid operand type '#{operand.class}' "\ "for '#{operator}' operator in filters") end - when '?' - if operand - raise ArgumentError.new("Invalid operand for subproperty existence filter, should be empty or null") - end - cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)" - param_out << subproperty[1] + when 'exists' + if operand == true + cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)" + elsif operand == false + cond_out << "(NOT jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)) OR #{ar_table_name}.#{subproperty[0]} is NULL" + else + raise ArgumentError.new("Invalid operand '#{operand}' for '#{operator}' must be true or false") + end + param_out << subproperty[1] else raise ArgumentError.new("Invalid operator for subproperty search '#{operator}'") end + elsif operator.downcase == "exists" + if col.type != :jsonb + raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' for operator '#{operator}' in filter") + end + + cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)" + param_out << operand else + if !model_class.searchable_columns(operator).index subproperty[0] + raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' in filter") + end + case operator.downcase when '=', '<', '<=', '>', '>=', '!=', 'like', 'ilike' attr_type = model_class.attribute_column(attr).type @@ -150,6 +172,9 @@ module RecordFilters [true, false].include?(operand) cond_out << "#{ar_table_name}.#{attr} #{operator} ?" param_out << operand + elsif (attr_type == :integer) + cond_out << "#{ar_table_name}.#{attr} #{operator} ?" + param_out << operand else raise ArgumentError.new("Invalid operand type '#{operand.class}' "\ "for '#{operator}' operator in filters") @@ -172,8 +197,17 @@ module RecordFilters operand.each do |op| cl = ArvadosModel::kind_class op if cl - cond << "#{ar_table_name}.#{attr} like ?" - param_out << cl.uuid_like_pattern + if attr == 'uuid' + if model_class.uuid_prefix == cl.uuid_prefix + cond << "1=1" + else + cond << "1=0" + end + else + # Use a substring query to support remote uuids + cond << "substring(#{ar_table_name}.#{attr}, 7, 5) = ?" + param_out << cl.uuid_prefix + end else cond << "1=0" end