From: Lucas Di Pentima Date: Wed, 19 Feb 2020 21:41:39 +0000 (-0300) Subject: Merge branch '15781-multi-value-property-search' X-Git-Tag: 2.1.0~292 X-Git-Url: https://git.arvados.org/arvados.git/commitdiff_plain/64e387b2f4f0fe6c4c7bf16232706c7cf194caf0?hp=a1d031db9dc26e2b2da06b612ddd81fbd7fcd9e9 Merge branch '15781-multi-value-property-search' Refs #15781 --- diff --git a/doc/api/methods.html.textile.liquid b/doc/api/methods.html.textile.liquid index e08dbb2837..872a1bca71 100644 --- a/doc/api/methods.html.textile.liquid +++ b/doc/api/methods.html.textile.liquid @@ -130,6 +130,7 @@ table(table table-bordered table-condensed). |@like@, @ilike@|string|SQL pattern match, single character match is @_@ and wildcard is @%@, ilike is case-insensitive|@["properties.my_subproperty", "like", "d00220fb%"]@| |@in@, @not in@|array of strings|Set membership|@["properties.my_subproperty", "in", ["fizz", "buzz"]]@| |@exists@|boolean|Test if a subproperty is present or not (determined by operand).|@["properties.my_subproperty", "exists", true]@| +|@contains@|string, number|Filter where subproperty has a value either by exact match or value is element of subproperty list.|@["foo", "contains", "bar"]@ will find both @{"foo": "bar"}@ and @{"foo": ["bar", "baz"]}@.| Note that exclusion filters @!=@ and @not in@ will return records for which the property is not defined at all. To restrict filtering to records on which the subproperty is defined, combine with an @exists@ filter. diff --git a/services/api/app/controllers/arvados/v1/schema_controller.rb b/services/api/app/controllers/arvados/v1/schema_controller.rb index aee5d1f951..5c22341015 100644 --- a/services/api/app/controllers/arvados/v1/schema_controller.rb +++ b/services/api/app/controllers/arvados/v1/schema_controller.rb @@ -36,7 +36,7 @@ class Arvados::V1::SchemaController < ApplicationController # format is YYYYMMDD, must be fixed with (needs to be linearly # sortable), updated manually, may be used by clients to # determine availability of API server features. - revision: "20190926", + revision: "20200212", source_version: AppVersion.hash, sourceVersion: AppVersion.hash, # source_version should be deprecated in the future packageVersion: AppVersion.package_version, diff --git a/services/api/lib/record_filters.rb b/services/api/lib/record_filters.rb index 994e850310..5688ca6140 100644 --- a/services/api/lib/record_filters.rb +++ b/services/api/lib/record_filters.rb @@ -19,7 +19,7 @@ module RecordFilters # +model_class+ subclass of ActiveRecord being filtered # # Output: - # Hash with two keys: + # Hash with the following keys: # :cond_out array of SQL fragments for each filter expression # :param_out array of values for parameter substitution in cond_out # :joins array of joins: either [] or ["JOIN containers ON ..."] @@ -140,6 +140,10 @@ module RecordFilters raise ArgumentError.new("Invalid operand '#{operand}' for '#{operator}' must be true or false") end param_out << proppath + when 'contains' + cond_out << "#{attr_table_name}.#{attr} @> ?::jsonb OR #{attr_table_name}.#{attr} @> ?::jsonb" + param_out << SafeJSON.dump({proppath => operand}) + param_out << SafeJSON.dump({proppath => [operand]}) else raise ArgumentError.new("Invalid operator for subproperty search '#{operator}'") end diff --git a/services/api/test/fixtures/collections.yml b/services/api/test/fixtures/collections.yml index 1503f6bc01..1581039bb3 100644 --- a/services/api/test/fixtures/collections.yml +++ b/services/api/test/fixtures/collections.yml @@ -953,7 +953,7 @@ collection_with_prop2_1: modified_at: 2015-02-13T17:22:54Z updated_at: 2015-02-13T17:22:54Z manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" - name: collection with prop1 1 + name: collection with prop2 1 properties: prop2: 1 @@ -968,10 +968,55 @@ collection_with_prop2_5: modified_at: 2015-02-13T17:22:54Z updated_at: 2015-02-13T17:22:54Z manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" - name: collection with prop1 5 + name: collection with prop2 5 properties: prop2: 5 +collection_with_list_prop_odd: + uuid: zzzzz-4zz18-listpropertyodd + current_version_uuid: zzzzz-4zz18-listpropertyodd + portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45 + owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + created_at: 2015-02-13T17:22:54Z + modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr + modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f + modified_at: 2015-02-13T17:22:54Z + updated_at: 2015-02-13T17:22:54Z + manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" + name: collection with list property with odd values + properties: + listprop: [elem1, elem3, 5] + +collection_with_list_prop_even: + uuid: zzzzz-4zz18-listpropertyeven + current_version_uuid: zzzzz-4zz18-listpropertyeven + portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45 + owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + created_at: 2015-02-13T17:22:54Z + modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr + modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f + modified_at: 2015-02-13T17:22:54Z + updated_at: 2015-02-13T17:22:54Z + manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" + name: collection with list property with even values + properties: + listprop: [elem2, 4, elem6, ELEM8] + +collection_with_listprop_elem1: + uuid: zzzzz-4zz18-listpropelem1 + current_version_uuid: zzzzz-4zz18-listpropelem1 + portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45 + owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + created_at: 2015-02-13T17:22:54Z + modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr + modified_by_user_uuid: zzzzz-tpzed-d9tiejq69daie8f + modified_at: 2015-02-13T17:22:54Z + updated_at: 2015-02-13T17:22:54Z + manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n" + name: collection with list property with string value + properties: + listprop: elem1 + collection_with_uri_prop: uuid: zzzzz-4zz18-withuripropval1 current_version_uuid: zzzzz-4zz18-withuripropval1 diff --git a/services/api/test/functional/arvados/v1/filters_test.rb b/services/api/test/functional/arvados/v1/filters_test.rb index d49fe7a3e6..b30afd7453 100644 --- a/services/api/test/functional/arvados/v1/filters_test.rb +++ b/services/api/test/functional/arvados/v1/filters_test.rb @@ -172,6 +172,13 @@ class Arvados::V1::FiltersTest < ActionController::TestCase ['prop2', '<=', 5, [:collection_with_prop2_1, :collection_with_prop2_5], []], ['prop2', '>=', 1, [:collection_with_prop2_1, :collection_with_prop2_5], []], ['', '=', "value1", [:collection_with_uri_prop], []], + ['listprop', 'contains', 'elem1', [:collection_with_list_prop_odd, :collection_with_listprop_elem1], [:collection_with_list_prop_even]], + ['listprop', '=', 'elem1', [:collection_with_listprop_elem1], [:collection_with_list_prop_odd]], + ['listprop', 'contains', 5, [:collection_with_list_prop_odd], [:collection_with_list_prop_even, :collection_with_listprop_elem1]], + ['listprop', 'contains', 'elem2', [:collection_with_list_prop_even], [:collection_with_list_prop_odd, :collection_with_listprop_elem1]], + ['listprop', 'contains', 'ELEM2', [], [:collection_with_list_prop_even]], + ['listprop', 'contains', 'elem8', [], [:collection_with_list_prop_even]], + ['listprop', 'contains', 4, [:collection_with_list_prop_even], [:collection_with_list_prop_odd, :collection_with_listprop_elem1]], ].each do |prop, op, opr, inc, ex| test "jsonb filter properties.#{prop} #{op} #{opr})" do @controller = Arvados::V1::CollectionsController.new