このコミットが含まれているのは:
@@ -44,12 +44,31 @@ class CountsCondition
|
||||
result.find { _1[:key] == 'tags_json' }.merge!(type: :string, multiple: true, collection: true,
|
||||
operators: CountsResourceDefinition::OPERATORS.fetch(:string))
|
||||
end
|
||||
result + [{ key: 'user.id', type: :integer, nullable: true,
|
||||
result += [{ key: 'user.id', type: :integer, nullable: true,
|
||||
labels: ['history:actor', 'counts:columns.resourceId'],
|
||||
operators: CountsResourceDefinition::OPERATORS.fetch(:integer) },
|
||||
{ key: 'user.name', type: :string, nullable: true,
|
||||
labels: ['history:actor', 'history:name'],
|
||||
operators: CountsResourceDefinition::OPERATORS.fetch(:string) }]
|
||||
result.each do |field|
|
||||
next if field[:type] == :interval
|
||||
|
||||
if field[:multiple]
|
||||
field[:operators] = field[:operators] - ['in', 'not_in'] + ['any', 'all', 'none']
|
||||
end
|
||||
field[:input_types] = ['single', 'multiple']
|
||||
if ['category', 'tag_category'].include?(field[:key].split('.').last)
|
||||
field[:candidate_source] = 'categories'
|
||||
field[:choices] = Tag.categories.keys.map { |value|
|
||||
{ value:, label: "common:categories.#{ value }" }
|
||||
}
|
||||
elsif definition.resource == 'posts' && ['tags', 'tags_json'].include?(field[:key])
|
||||
field[:candidate_source] = 'tags'
|
||||
field[:set_type] = 'tag_reference'
|
||||
end
|
||||
field[:input_types] << 'list' if field[:candidate_source]
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def initialize(population)
|
||||
@@ -61,11 +80,17 @@ class CountsCondition
|
||||
|
||||
def apply(scope, entry)
|
||||
raise ArgumentError unless entry.is_a?(Hash)
|
||||
raise ArgumentError if (entry.keys - ['field', 'operator', 'value', 'end', 'blank']).any?
|
||||
allowed = ['field', 'operator', 'value', 'end', 'blank', 'input', 'values']
|
||||
raise ArgumentError if (entry.keys - allowed).any?
|
||||
|
||||
field = @definition.filter_metadata.find { _1[:key] == entry.fetch('field') }
|
||||
raise ArgumentError unless field && field[:operators].include?(entry.fetch('operator'))
|
||||
raise ArgumentError unless [true, false, nil].include?(entry['blank'])
|
||||
input = entry.fetch('input', 'single')
|
||||
raise ArgumentError unless ['single', 'multiple', 'list'].include?(input)
|
||||
if input == 'list' && !field[:candidate_source]
|
||||
raise ArgumentError
|
||||
end
|
||||
values = [entry['value'], entry['end']]
|
||||
raise ArgumentError unless values.all? { _1.nil? || (_1.is_a?(String) && _1.bytesize <= 8192) }
|
||||
if entry['operator'] != 'range' && !values[1].nil? && values[1] != ''
|
||||
@@ -84,6 +109,10 @@ class CountsCondition
|
||||
end
|
||||
return scope.where(Arel.sql(sql))
|
||||
end
|
||||
if ['in', 'not_in', 'any', 'all', 'none'].include?(entry['operator'])
|
||||
return set_condition(scope, entry, field)
|
||||
end
|
||||
raise ArgumentError if entry.key?('values') || input != 'single'
|
||||
return scope if values.all? { _1.nil? || _1 == '' }
|
||||
|
||||
parsed = values.map { parse(_1, type) }
|
||||
@@ -122,6 +151,58 @@ class CountsCondition
|
||||
|
||||
private
|
||||
|
||||
def set_condition(scope, entry, field)
|
||||
return scope unless entry.key?('values')
|
||||
|
||||
values = entry['values']
|
||||
raise ArgumentError unless values.is_a?(Array) && values.length <= 100
|
||||
raise ArgumentError if [entry['value'], entry['end']].any? { !_1.nil? && _1 != '' }
|
||||
|
||||
parsed = values.map do |value|
|
||||
unless (value.is_a?(String) || value.is_a?(Numeric) || [true, false].include?(value))
|
||||
raise ArgumentError
|
||||
end
|
||||
raise ArgumentError if value.to_s.bytesize > 8192
|
||||
|
||||
if field[:set_type] == 'tag_reference'
|
||||
raise ArgumentError unless value.is_a?(String) &&
|
||||
value.match?(/\A(?:internal|external):[1-9][0-9]{0,17}\z/)
|
||||
value
|
||||
else
|
||||
raise ArgumentError if value == '' && ![:string, :enum].include?(field[:type])
|
||||
result = value == '' ? '' : parse(value.to_s, field[:type])
|
||||
validate_enum!(field[:key], result) if field[:type] == :enum
|
||||
result
|
||||
end
|
||||
end
|
||||
raise ArgumentError unless parsed.uniq == parsed
|
||||
|
||||
operator = entry['operator']
|
||||
if parsed.empty?
|
||||
return ['in', 'any'].include?(operator) ? scope.none : scope
|
||||
end
|
||||
source, expression = source_for(field[:key])
|
||||
if field[:set_type] == 'tag_reference'
|
||||
source = post_tag_source
|
||||
id = "COALESCE(#{ scalar('tag_snapshot.item', 'tag_id', :integer) }, " +
|
||||
"#{ scalar('tag_snapshot.item', 'external_tag_id', :integer) })"
|
||||
expression = "CONCAT(CASE WHEN #{ scalar('tag_snapshot.item', 'tag_id') } IS NULL " +
|
||||
"THEN 'external:' ELSE 'internal:' END, #{ id })"
|
||||
end
|
||||
predicates = parsed.map do |_value|
|
||||
clause = "#{ expression } = ?"
|
||||
clause = "EXISTS (SELECT 1 FROM #{ source } WHERE #{ clause })" if source
|
||||
clause
|
||||
end
|
||||
conjunction = operator == 'all' ? ' AND ' : ' OR '
|
||||
predicate = "(#{ predicates.join(conjunction) })"
|
||||
if ['not_in', 'none'].include?(operator)
|
||||
predicate = source ? "NOT #{ predicate }" :
|
||||
"(NOT #{ predicate } OR #{ blank_sql(expression, field[:type]) })"
|
||||
end
|
||||
scope.where(predicate, *parsed)
|
||||
end
|
||||
|
||||
def parse(value, type)
|
||||
return nil if value.nil? || value == ''
|
||||
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする