h4. Filtering on subproperties
-Some record type have an additional @properties@ attribute that allows recording and filtering on additional key-value pairs. To filter on a subproperty, the value in the @attribute@ position has the form @properties.<user_property>@.
+Some record type have an additional @properties@ attribute that allows recording and filtering on additional key-value pairs. To filter on a subproperty, the value in the @attribute@ position has the form @properties.user_property@. You may also use JSON-LD / RDF style URIs for property keys by enclosing them in @<...>@ for example @properties.<http://example.com/user_property>@. Alternately you may also provide a JSON-LD "@context" field, however at this time JSON-LD contexts are not interpreted by Arvados.
table(table table-bordered table-condensed).
|_. Operator|_. Operand type|_. Description|_. Example|
textonly_operator = !operator.match(/[<=>]/)
self.columns.select do |col|
case col.type
- when :string, :text, :jsonb
+ when :string, :text
true
when :datetime, :integer, :boolean
!textonly_operator
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
+ if col.type != :jsonb
+ raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' for operator '#{operator}' in filter")
+ end
+
+ if subproperty[1][0] == "<" and subproperty[1][-1] == ">"
+ subproperty[1] = subproperty[1][1..-2]
+ end
+
# jsonb search
case operator.downcase
when '=', '!='
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
end
end
cond_out << cond.join(' OR ')
- when 'exists'
- cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)"
- param_out << operand
else
raise ArgumentError.new("Invalid operator '#{operator}'")
end
properties:
prop2: 5
+collection_with_uri_prop:
+ uuid: zzzzz-4zz18-withuripropval1
+ 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 RDF-style URI property key
+ properties:
+ "http://schema.org/example": "value1"
+
# Test Helper trims the rest of the file
# Do not add your fixtures below this line as the rest of this file will be trimmed by test_helper
['prop2', '>', 1, [:collection_with_prop2_5], [:collection_with_prop2_1]],
['prop2', '<', 5, [:collection_with_prop2_1], [:collection_with_prop2_5]],
['prop2', '<=', 5, [:collection_with_prop2_1, :collection_with_prop2_5], []],
- ['prop2', '>=', 1, [:collection_with_prop2_1, :collection_with_prop2_5], []]
+ ['prop2', '>=', 1, [:collection_with_prop2_1, :collection_with_prop2_5], []],
+ ['<http://schema.org/example>', '=', "value1", [:collection_with_uri_prop], []],
].each do |prop, op, opr, inc, ex|
test "jsonb filter properties.#{prop} #{op} #{opr})" do
@controller = Arvados::V1::CollectionsController.new
search_index_columns = table_class.searchable_columns('ilike')
# Disappointing, but text columns aren't indexed yet.
search_index_columns -= table_class.columns.select { |c|
- c.type == :text or c.name == 'description' or c.name == 'file_names'
+ c.type == :text or c.name == 'description' or c.name == 'file_names' or c.name == 'properties'
}.collect(&:name)
indexes = ActiveRecord::Base.connection.indexes(table)