Module: Factbase::IndexedTerm
- Defined in:
- lib/factbase/indexed/indexed_term.rb
Overview
Term with an index.
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2024-2025 Yegor Bugayenko
- License
-
MIT
Instance Method Summary collapse
-
#predict(maps, params) ⇒ Array<Hash>|nil
Reduces the provided list of facts (maps) to a smaller array, if it’s possible.
Instance Method Details
#predict(maps, params) ⇒ Array<Hash>|nil
Reduces the provided list of facts (maps) to a smaller array, if it’s possible.
NIL must be returned if indexing is prohibited in this case.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/factbase/indexed/indexed_term.rb', line 22 def predict(maps, params) key = [maps.object_id, @operands.first, @op] case @op when :one if @idx[key].nil? @idx[key] = [] prop = @operands.first.to_s maps.to_a.each do |m| @idx[key].append(m) if !m[prop].nil? && m[prop].size == 1 end end (maps & []) | @idx[key] when :exists if @idx[key].nil? @idx[key] = [] prop = @operands.first.to_s maps.to_a.each do |m| @idx[key].append(m) unless m[prop].nil? end end (maps & []) | @idx[key] when :absent if @idx[key].nil? @idx[key] = [] prop = @operands.first.to_s maps.to_a.each do |m| @idx[key].append(m) if m[prop].nil? end end (maps & []) | @idx[key] when :eq if @operands.first.is_a?(Symbol) && _scalar?(@operands[1]) if @idx[key].nil? @idx[key] = {} prop = @operands.first.to_s maps.to_a.each do |m| m[prop]&.each do |v| @idx[key][v] = [] if @idx[key][v].nil? @idx[key][v].append(m) end end end vv = if @operands[1].is_a?(Symbol) params[@operands[1].to_s] || [] else [@operands[1]] end if vv.empty? (maps & []) else j = vv.map { |v| @idx[key][v] || [] }.reduce(&:|) (maps & []) | j end end when :gt if @operands.first.is_a?(Symbol) && _scalar?(@operands[1]) prop = @operands.first.to_s cache_key = [maps.object_id, @operands.first, :sorted] if @idx[cache_key].nil? @idx[cache_key] = [] maps.to_a.each do |m| values = m[prop] next if values.nil? values.each do |v| @idx[cache_key] << [v, m] end end @idx[cache_key].sort_by! { |pair| pair[0] } end threshold = @operands[1].is_a?(Symbol) ? params[@operands[1].to_s]&.first : @operands[1] return nil if threshold.nil? i = @idx[cache_key].bsearch_index { |pair| pair[0] > threshold } || @idx[cache_key].size result = @idx[cache_key][i..].map { |pair| pair[1] }.uniq (maps & []) | result end when :lt if @operands.first.is_a?(Symbol) && _scalar?(@operands[1]) prop = @operands.first.to_s cache_key = [maps.object_id, @operands.first, :sorted] if @idx[cache_key].nil? @idx[cache_key] = [] maps.to_a.each do |m| values = m[prop] next if values.nil? values.each do |v| @idx[cache_key] << [v, m] end end @idx[cache_key].sort_by! { |pair| pair[0] } end threshold = @operands[1].is_a?(Symbol) ? params[@operands[1].to_s]&.first : @operands[1] return nil if threshold.nil? i = @idx[cache_key].bsearch_index { |pair| pair[0] >= threshold } || @idx[cache_key].size result = @idx[cache_key][0...i].map { |pair| pair[1] }.uniq (maps & []) | result end when :and r = nil if @operands.all? { |o| o.op == :eq } && @operands.size > 1 \ && @operands.all? { |o| o.operands.first.is_a?(Symbol) && _scalar?(o.operands[1]) } props = @operands.map { |o| o.operands.first }.sort key = [maps.object_id, props, :multi_and_eq] if @idx[key].nil? @idx[key] = {} maps.to_a.each do |m| _all_tuples(m, props).each do |t| @idx[key][t] = [] if @idx[key][t].nil? @idx[key][t].append(m) end end end tuples = Enumerator.product( *@operands.sort_by { |o| o.operands.first }.map do |o| if o.operands[1].is_a?(Symbol) params[o.operands[1].to_s] || [] else [o.operands[1]] end end ) j = tuples.map { |t| @idx[key][t] || [] }.reduce(&:|) r = (maps & []) | j else @operands.each do |o| n = o.predict(maps, params) break if n.nil? if r.nil? r = n elsif n.size < r.size * 8 # to skip some obvious matchings r &= n.to_a end break if r.size < maps.size / 32 # it's already small enough break if r.size < 128 # it's obviously already small enough end end r when :or r = nil @operands.each do |o| n = o.predict(maps, params) if n.nil? r = nil break end r = maps & [] if r.nil? r |= n.to_a return maps if r.size > maps.size / 4 # it's big enough already end r when :not if @idx[key].nil? yes = @operands.first.predict(maps, params) if yes.nil? @idx[key] = { r: nil } else yes = yes.to_a.to_set @idx[key] = { r: maps.to_a.reject { |m| yes.include?(m) } } end end r = @idx[key][:r] if r.nil? nil else (maps & []) | r end end end |