このコミットが含まれているのは:
2026-11-08 01:56:45 +09:00
コミット b9d7c4fef5
8個のファイルの変更、472行の追加、140行の削除
+41 -2
ファイルの表示
@@ -82,14 +82,32 @@ class CountsCondition
def apply(scope, entry)
raise ArgumentError unless entry.is_a?(Hash)
if entry['operator'] == 'axis_eq'
raise ArgumentError unless (entry.keys - ['field', 'operator', 'grain', 'value']).empty?
value = entry['value']
unless value.nil? || value.is_a?(String) || value.is_a?(Numeric) || [true, false].include?(value)
unless (value.nil? || value.is_a?(String) || value.is_a?(Numeric) ||
[true, false].include?(value))
raise ArgumentError
end
raise ArgumentError if value.is_a?(String) && value.bytesize > 8192
if (!HistoryAccess.admin? && CountsAxes.private_field?(entry['field']) &&
(value == CountsAxes::MASKED_VALUE ||
(value == CountsAxes::MASKED_TEXT &&
(@definition.fields[entry['field']] != :string ||
['tags', 'changed_field'].include?(entry['field'])))))
field = entry.fetch('field')
raise ArgumentError unless @definition.axes(@population.history).include?(field)
if entry.key?('grain')
unless (@definition.fields[field] == :datetime &&
CountsAxes::GRAINS.include?(entry['grain']))
raise ArgumentError
end
end
return scope.none
end
scope = @population.condition_scope(scope, entry.fetch('field'))
return @population.axis_condition(scope, entry)
end
allowed = ['field', 'operator', 'value', 'end', 'blank', 'input', 'values']
@@ -111,6 +129,13 @@ class CountsCondition
source, expression = source_for(field[:key])
type = field[:type]
# Preserve typed input compatibility; display markers are never real typed values.
if (!HistoryAccess.admin? && CountsAxes.private_field?(field[:key]) &&
entry['value'] == CountsAxes::MASKED_TEXT && type != :string && !entry['blank'])
return scope.none unless entry['operator'] == 'ne'
return @population.condition_scope(scope, field[:key])
end
blank = blank_sql(expression, type)
if entry['blank']
sql = if source && field[:collection]
@@ -119,6 +144,7 @@ class CountsCondition
"NOT EXISTS (SELECT 1 FROM #{ source } WHERE NOT (#{ blank }))"
else blank
end
scope = @population.condition_scope(scope, field[:key])
return scope.where(Arel.sql(sql))
end
if ['in', 'not_in', 'any', 'all', 'none'].include?(entry['operator'])
@@ -131,6 +157,7 @@ class CountsCondition
validate_enum!(field[:key], parsed.first) if type == :enum
operator = entry['operator']
if type == :interval
scope = @population.condition_scope(scope, field[:key])
return interval(scope, source, expression, parsed)
end
clauses = []
@@ -158,6 +185,7 @@ class CountsCondition
elsif operator == 'ne'
predicate = "(NOT (#{ predicate }) OR #{ blank })"
end
scope = @population.condition_scope(scope, field[:key])
scope.where(predicate, *binds)
end
@@ -168,6 +196,13 @@ class CountsCondition
values = entry['values']
raise ArgumentError unless values.is_a?(Array) && values.length <= 100
if (!HistoryAccess.admin? && CountsAxes.private_field?(field[:key]) &&
values.include?(CountsAxes::MASKED_TEXT) &&
(field[:type] != :string || field[:set_type] == 'tag_reference'))
return scope.none if entry['operator'] == 'all'
values = values - [CountsAxes::MASKED_TEXT]
end
raise ArgumentError if [entry['value'], entry['end']].any? { !_1.nil? && _1 != '' }
parsed = values.map do |value|
@@ -191,7 +226,10 @@ class CountsCondition
operator = entry['operator']
if parsed.empty?
return ['in', 'any'].include?(operator) ? scope.none : scope
return scope.none if ['in', 'any'].include?(operator)
return scope if entry['values'].empty?
return @population.condition_scope(scope, field[:key])
end
source, expression = source_for(field[:key])
if field[:set_type] == 'tag_reference'
@@ -212,6 +250,7 @@ class CountsCondition
predicate = source ? "NOT #{ predicate }" :
"(NOT #{ predicate } OR #{ blank_sql(expression, field[:type]) })"
end
scope = @population.condition_scope(scope, field[:key])
scope.where(predicate, *parsed)
end