tiny-quick-gem 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/searchkick-6.1.2/CHANGELOG.md +919 -0
  3. data/searchkick-6.1.2/LICENSE.txt +22 -0
  4. data/searchkick-6.1.2/README.md +2348 -0
  5. data/searchkick-6.1.2/lib/searchkick/bulk_reindex_job.rb +19 -0
  6. data/searchkick-6.1.2/lib/searchkick/controller_runtime.rb +40 -0
  7. data/searchkick-6.1.2/lib/searchkick/hash_wrapper.rb +41 -0
  8. data/searchkick-6.1.2/lib/searchkick/index.rb +480 -0
  9. data/searchkick-6.1.2/lib/searchkick/index_cache.rb +30 -0
  10. data/searchkick-6.1.2/lib/searchkick/index_options.rb +652 -0
  11. data/searchkick-6.1.2/lib/searchkick/indexer.rb +43 -0
  12. data/searchkick-6.1.2/lib/searchkick/log_subscriber.rb +57 -0
  13. data/searchkick-6.1.2/lib/searchkick/middleware.rb +19 -0
  14. data/searchkick-6.1.2/lib/searchkick/model.rb +120 -0
  15. data/searchkick-6.1.2/lib/searchkick/multi_search.rb +46 -0
  16. data/searchkick-6.1.2/lib/searchkick/process_batch_job.rb +20 -0
  17. data/searchkick-6.1.2/lib/searchkick/process_queue_job.rb +33 -0
  18. data/searchkick-6.1.2/lib/searchkick/query.rb +1372 -0
  19. data/searchkick-6.1.2/lib/searchkick/railtie.rb +7 -0
  20. data/searchkick-6.1.2/lib/searchkick/record_data.rb +147 -0
  21. data/searchkick-6.1.2/lib/searchkick/record_indexer.rb +174 -0
  22. data/searchkick-6.1.2/lib/searchkick/reindex_queue.rb +57 -0
  23. data/searchkick-6.1.2/lib/searchkick/reindex_v2_job.rb +17 -0
  24. data/searchkick-6.1.2/lib/searchkick/relation.rb +728 -0
  25. data/searchkick-6.1.2/lib/searchkick/relation_indexer.rb +184 -0
  26. data/searchkick-6.1.2/lib/searchkick/reranking.rb +28 -0
  27. data/searchkick-6.1.2/lib/searchkick/results.rb +359 -0
  28. data/searchkick-6.1.2/lib/searchkick/script.rb +11 -0
  29. data/searchkick-6.1.2/lib/searchkick/version.rb +3 -0
  30. data/searchkick-6.1.2/lib/searchkick/where.rb +11 -0
  31. data/searchkick-6.1.2/lib/searchkick.rb +388 -0
  32. data/searchkick-6.1.2/lib/tasks/searchkick.rake +37 -0
  33. data/tiny-quick-gem.gemspec +12 -0
  34. metadata +73 -0
@@ -0,0 +1,7 @@
1
+ module Searchkick
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load "tasks/searchkick.rake"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,147 @@
1
+ module Searchkick
2
+ class RecordData
3
+ TYPE_KEYS = ["type", :type]
4
+
5
+ attr_reader :index, :record
6
+
7
+ def initialize(index, record)
8
+ @index = index
9
+ @record = record
10
+ end
11
+
12
+ def index_data
13
+ data = record_data
14
+ data[:data] = search_data
15
+ {index: data}
16
+ end
17
+
18
+ def update_data(method_name)
19
+ data = record_data
20
+ data[:data] = {doc: search_data(method_name)}
21
+ {update: data}
22
+ end
23
+
24
+ def delete_data
25
+ {delete: record_data}
26
+ end
27
+
28
+ # custom id can be useful for load: false
29
+ def search_id
30
+ id = record.respond_to?(:search_document_id) ? record.search_document_id : record.id
31
+ id.is_a?(Numeric) ? id : id.to_s
32
+ end
33
+
34
+ def document_type(ignore_type = false)
35
+ index.klass_document_type(record.class, ignore_type)
36
+ end
37
+
38
+ def record_data
39
+ data = {
40
+ _index: index.name,
41
+ _id: search_id
42
+ }
43
+ data[:routing] = record.search_routing if record.respond_to?(:search_routing)
44
+ data
45
+ end
46
+
47
+ private
48
+
49
+ def search_data(method_name = nil)
50
+ partial_reindex = !method_name.nil?
51
+
52
+ source = record.send(method_name || :search_data)
53
+
54
+ # conversions
55
+ index.conversions_fields.each do |conversions_field|
56
+ if source[conversions_field]
57
+ source[conversions_field] = source[conversions_field].map { |k, v| {query: k, count: v} }
58
+ end
59
+ end
60
+
61
+ index.conversions_v2_fields.each do |conversions_field|
62
+ key = source.key?(conversions_field) ? conversions_field : conversions_field.to_sym
63
+ if !partial_reindex || source[key]
64
+ if index.options[:case_sensitive]
65
+ source[key] =
66
+ (source[key] || {}).reduce(Hash.new(0)) do |memo, (k, v)|
67
+ memo[k.to_s.gsub(".", "*")] += v
68
+ memo
69
+ end
70
+ else
71
+ source[key] =
72
+ (source[key] || {}).reduce(Hash.new(0)) do |memo, (k, v)|
73
+ memo[k.to_s.downcase.gsub(".", "*")] += v
74
+ memo
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # hack to prevent generator field doesn't exist error
81
+ if !partial_reindex
82
+ index.suggest_fields.each do |field|
83
+ if !source.key?(field) && !source.key?(field.to_sym)
84
+ source[field] = nil
85
+ end
86
+ end
87
+ end
88
+
89
+ # locations
90
+ index.locations_fields.each do |field|
91
+ if source[field]
92
+ if !source[field].is_a?(Hash) && (source[field].first.is_a?(Array) || source[field].first.is_a?(Hash))
93
+ # multiple locations
94
+ source[field] = source[field].map { |a| location_value(a) }
95
+ else
96
+ source[field] = location_value(source[field])
97
+ end
98
+ end
99
+ end
100
+
101
+ if index.options[:inheritance]
102
+ if !TYPE_KEYS.any? { |tk| source.key?(tk) }
103
+ source[:type] = document_type(true)
104
+ end
105
+ end
106
+
107
+ cast_big_decimal(source)
108
+
109
+ source
110
+ end
111
+
112
+ def location_value(value)
113
+ if value.is_a?(Array)
114
+ value.map(&:to_f).reverse
115
+ elsif value.is_a?(Hash)
116
+ {lat: value[:lat].to_f, lon: value[:lon].to_f}
117
+ else
118
+ value
119
+ end
120
+ end
121
+
122
+ # change all BigDecimal values to floats due to
123
+ # https://github.com/rails/rails/issues/6033
124
+ # possible loss of precision :/
125
+ def cast_big_decimal(obj)
126
+ case obj
127
+ when BigDecimal
128
+ obj.to_f
129
+ when Hash
130
+ obj.each do |k, v|
131
+ # performance
132
+ if v.is_a?(BigDecimal)
133
+ obj[k] = v.to_f
134
+ elsif v.is_a?(Enumerable)
135
+ obj[k] = cast_big_decimal(v)
136
+ end
137
+ end
138
+ when Enumerable
139
+ obj.map do |v|
140
+ cast_big_decimal(v)
141
+ end
142
+ else
143
+ obj
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,174 @@
1
+ module Searchkick
2
+ class RecordIndexer
3
+ attr_reader :index
4
+
5
+ def initialize(index)
6
+ @index = index
7
+ end
8
+
9
+ def reindex(records, mode:, method_name:, ignore_missing:, full: false, single: false, job_options: nil)
10
+ # prevents exists? check if records is a relation
11
+ records = records.to_a
12
+ return if records.empty?
13
+
14
+ case mode
15
+ when :async
16
+ unless defined?(ActiveJob)
17
+ raise Error, "Active Job not found"
18
+ end
19
+
20
+ job_options ||= {}
21
+
22
+ # only add if set for backwards compatibility
23
+ extra_options = {}
24
+ if ignore_missing
25
+ extra_options[:ignore_missing] = ignore_missing
26
+ end
27
+
28
+ # we could likely combine ReindexV2Job, BulkReindexJob, and ProcessBatchJob
29
+ # but keep them separate for now
30
+ if single
31
+ record = records.first
32
+
33
+ # always pass routing in case record is deleted
34
+ # before the async job runs
35
+ if record.respond_to?(:search_routing)
36
+ routing = record.search_routing
37
+ end
38
+
39
+ Searchkick::ReindexV2Job.set(**job_options).perform_later(
40
+ record.class.name,
41
+ record.id.to_s,
42
+ method_name ? method_name.to_s : nil,
43
+ routing: routing,
44
+ index_name: index.name,
45
+ **extra_options
46
+ )
47
+ else
48
+ Searchkick::BulkReindexJob.set(**job_options).perform_later(
49
+ class_name: records.first.class.searchkick_options[:class_name],
50
+ record_ids: records.map { |r| r.id.to_s },
51
+ index_name: index.name,
52
+ method_name: method_name ? method_name.to_s : nil,
53
+ **extra_options
54
+ )
55
+ end
56
+ when :queue
57
+ if method_name
58
+ raise Error, "Partial reindex not supported with queue option"
59
+ end
60
+
61
+ index.reindex_queue.push_records(records)
62
+ when true, :inline
63
+ index_records, other_records = records.partition { |r| index_record?(r) }
64
+ import_inline(index_records, !full ? other_records : [], method_name: method_name, ignore_missing: ignore_missing, single: single)
65
+ else
66
+ raise ArgumentError, "Invalid value for mode"
67
+ end
68
+
69
+ # return true like model and relation reindex for now
70
+ true
71
+ end
72
+
73
+ def reindex_items(klass, items, method_name:, ignore_missing:, single: false)
74
+ routing = items.to_h { |r| [r[:id], r[:routing]] }
75
+ record_ids = routing.keys
76
+
77
+ relation = Searchkick.load_records(klass, record_ids)
78
+ # call search_import even for single records for nested associations
79
+ relation = relation.search_import if relation.respond_to?(:search_import)
80
+ records = relation.select(&:should_index?)
81
+
82
+ # determine which records to delete
83
+ delete_ids = record_ids - records.map { |r| r.id.to_s }
84
+ delete_records =
85
+ delete_ids.map do |id|
86
+ construct_record(klass, id, routing[id])
87
+ end
88
+
89
+ import_inline(records, delete_records, method_name: method_name, ignore_missing: ignore_missing, single: single)
90
+ end
91
+
92
+ private
93
+
94
+ def index_record?(record)
95
+ record.persisted? && !record.destroyed? && record.should_index?
96
+ end
97
+
98
+ # import in single request with retries
99
+ def import_inline(index_records, delete_records, method_name:, ignore_missing:, single:)
100
+ return if index_records.empty? && delete_records.empty?
101
+
102
+ maybe_bulk(index_records, delete_records, method_name, single) do
103
+ if index_records.any?
104
+ if method_name
105
+ index.bulk_update(index_records, method_name, ignore_missing: ignore_missing)
106
+ else
107
+ index.bulk_index(index_records)
108
+ end
109
+ end
110
+
111
+ if delete_records.any?
112
+ index.bulk_delete(delete_records)
113
+ end
114
+ end
115
+ end
116
+
117
+ def maybe_bulk(index_records, delete_records, method_name, single)
118
+ if Searchkick.callbacks_value == :bulk
119
+ yield
120
+ else
121
+ # set action and data
122
+ action =
123
+ if single && index_records.empty?
124
+ "Remove"
125
+ elsif method_name
126
+ "Update"
127
+ else
128
+ single ? "Store" : "Import"
129
+ end
130
+ record = index_records.first || delete_records.first
131
+ name = record.class.searchkick_klass.name
132
+ message = lambda do |event|
133
+ event[:name] = "#{name} #{action}"
134
+ if single
135
+ event[:id] = index.search_id(record)
136
+ else
137
+ event[:count] = index_records.size + delete_records.size
138
+ end
139
+ end
140
+
141
+ with_retries do
142
+ Searchkick.callbacks(:bulk, message: message) do
143
+ yield
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ def construct_record(klass, id, routing)
150
+ record = klass.new
151
+ record.id = id
152
+ if routing
153
+ record.define_singleton_method(:search_routing) do
154
+ routing
155
+ end
156
+ end
157
+ record
158
+ end
159
+
160
+ def with_retries
161
+ retries = 0
162
+
163
+ begin
164
+ yield
165
+ rescue Faraday::ClientError => e
166
+ if retries < 1
167
+ retries += 1
168
+ retry
169
+ end
170
+ raise e
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,57 @@
1
+ module Searchkick
2
+ class ReindexQueue
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+
8
+ raise Error, "Searchkick.redis not set" unless Searchkick.redis
9
+ end
10
+
11
+ # supports single and multiple ids
12
+ def push(record_ids)
13
+ Searchkick.with_redis { |r| r.call("LPUSH", redis_key, record_ids) }
14
+ end
15
+
16
+ def push_records(records)
17
+ record_ids =
18
+ records.map do |record|
19
+ # always pass routing in case record is deleted
20
+ # before the queue job runs
21
+ if record.respond_to?(:search_routing)
22
+ routing = record.search_routing
23
+ end
24
+
25
+ # escape pipe with double pipe
26
+ value = escape(record.id.to_s)
27
+ value = "#{value}|#{escape(routing)}" if routing
28
+ value
29
+ end
30
+
31
+ push(record_ids)
32
+ end
33
+
34
+ # TODO use reliable queuing
35
+ def reserve(limit: 1000)
36
+ Searchkick.with_redis { |r| r.call("RPOP", redis_key, limit) }.to_a
37
+ end
38
+
39
+ def clear
40
+ Searchkick.with_redis { |r| r.call("DEL", redis_key) }
41
+ end
42
+
43
+ def length
44
+ Searchkick.with_redis { |r| r.call("LLEN", redis_key) }
45
+ end
46
+
47
+ private
48
+
49
+ def redis_key
50
+ "searchkick:reindex_queue:#{name}"
51
+ end
52
+
53
+ def escape(value)
54
+ value.to_s.gsub("|", "||")
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ module Searchkick
2
+ class ReindexV2Job < Searchkick.parent_job.constantize
3
+ queue_as { Searchkick.queue_name }
4
+
5
+ def perform(class_name, id, method_name = nil, routing: nil, index_name: nil, ignore_missing: nil)
6
+ model = Searchkick.load_model(class_name, allow_child: true)
7
+ index = model.searchkick_index(name: index_name)
8
+ # use should_index? to decide whether to index (not default scope)
9
+ # just like saving inline
10
+ # could use Searchkick.scope() in future
11
+ # but keep for now for backwards compatibility
12
+ model = model.unscoped if model.respond_to?(:unscoped)
13
+ items = [{id: id, routing: routing}]
14
+ RecordIndexer.new(index).reindex_items(model, items, method_name: method_name, ignore_missing: ignore_missing, single: true)
15
+ end
16
+ end
17
+ end