Module: QueryStream::FilterEngine

Defined in:
lib/query_stream/filter_engine.rb

Overview

フィルタリング&ソートエンジンモジュール

Class Method Summary collapse

Class Method Details

.apply_filters(records, filters) ⇒ Array<Hash>

フィルタ条件をレコード群に適用する

Parameters:

  • records (Array<Hash>)

    レコード群

  • filters (Array<Hash>)

    フィルタ条件の配列

Returns:

  • (Array<Hash>)

    フィルタ後のレコード群



22
23
24
25
26
27
28
# File 'lib/query_stream/filter_engine.rb', line 22

def apply_filters(records, filters)
  return records if filters.nil? || filters.empty?

  records.select do |record|
    filters.all? { evaluate_filter(record, it) }
  end
end

.apply_sort(records, sort) ⇒ Array<Hash>

ソート条件を適用する

Parameters:

  • records (Array<Hash>)

    レコード群

  • sort (Hash)

    ソート条件 { field:, direction: }

Returns:

  • (Array<Hash>)

    ソート後のレコード群



34
35
36
37
# File 'lib/query_stream/filter_engine.rb', line 34

def apply_sort(records, sort)
  sorted = records.sort_by { to_comparable(it[sort[:field]]) }
  sort[:direction] == :desc ? sorted.reverse : sorted
end

.evaluate_filter(record, filter) ⇒ Boolean

単一フィルタ条件をレコードに対して評価する

Parameters:

  • record (Hash)

    単一レコード

  • filter (Hash)

    フィルタ条件 { field:, op:, value: }

Returns:

  • (Boolean)

    条件に合致するか



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/query_stream/filter_engine.rb', line 43

def evaluate_filter(record, filter)
  # 主キー検索(_primary_key)の特別処理
  if filter[:field] == :_primary_key
    return evaluate_primary_key_lookup(record, filter[:value])
  end

  field_value = record[filter[:field]]

  case filter[:op]
  when :eq
    match_eq(field_value, filter[:value])
  when :neq
    !match_eq(field_value, filter[:value])
  when :gt
    to_comparable(field_value) > to_comparable(filter[:value])
  when :gte
    to_comparable(field_value) >= to_comparable(filter[:value])
  when :lt
    to_comparable(field_value) < to_comparable(filter[:value])
  when :lte
    to_comparable(field_value) <= to_comparable(filter[:value])
  when :range
    range = filter[:value]
    range.cover?(to_comparable(field_value))
  else
    false
  end
end

.evaluate_primary_key_lookup(record, query_value) ⇒ Boolean

主キー候補フィールドを順番に走査して一致するものを探す

Parameters:

  • record (Hash)

    単一レコード

  • query_value (Object)

    検索値

Returns:

  • (Boolean)

    いずれかの主キー候補と一致するか



76
77
78
79
80
# File 'lib/query_stream/filter_engine.rb', line 76

def evaluate_primary_key_lookup(record, query_value)
  QueryStreamParser::PRIMARY_KEY_FIELDS.any? do |key|
    record[key]&.to_s == query_value.to_s
  end
end

.match_eq(field_value, filter_values) ⇒ Object

等値比較(配列・カンマ区切り文字列を透過的に扱う)データ側が配列/カンマ区切りの場合、ORの値リストと交差判定する



84
85
86
87
88
89
90
# File 'lib/query_stream/filter_engine.rb', line 84

def match_eq(field_value, filter_values)
  field_list = normalize_to_list(field_value)
  value_list = Array(filter_values).map { it.to_s.strip }

  # フィールド側の値リストと条件値リストに交差があれば一致
  (field_list & value_list).any?
end

.normalize_to_list(value) ⇒ Object

フィールド値をリスト化する(配列/カンマ区切り/単値を統一)



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/query_stream/filter_engine.rb', line 93

def normalize_to_list(value)
  case value
  when Array
    value.map { it.to_s.strip }
  when String
    value.split(',').map { it.strip }
  when nil
    []
  else
    [value.to_s.strip]
  end
end

.to_comparable(value) ⇒ Object

比較用に値を正規化する(数値変換・日付変換を試みる)



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/query_stream/filter_engine.rb', line 107

def to_comparable(value)
  case value
  when Date, Time
    value
  when String
    # 日付形式(YYYY-MM-DD)を検出して Date に変換
    if value.match?(/\A\d{4}-\d{2}-\d{2}\z/)
      begin
        return Date.parse(value)
      rescue Date::Error
        raise InvalidDateError, "無効な日付: #{value}"
      end
    end
    # 数値変換を試みる
    case value
    when /\A-?\d+\z/
      value.to_i
    when /\A-?\d+\.\d+\z/
      value.to_f
    else
      value
    end
  when Integer, Float
    value
  else
    value.to_s
  end
end