caoutsearch 0.0.0

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +43 -0
  4. data/lib/caoutsearch/config/client.rb +13 -0
  5. data/lib/caoutsearch/config/mappings.rb +40 -0
  6. data/lib/caoutsearch/config/settings.rb +29 -0
  7. data/lib/caoutsearch/filter/base.rb +101 -0
  8. data/lib/caoutsearch/filter/boolean.rb +19 -0
  9. data/lib/caoutsearch/filter/date.rb +49 -0
  10. data/lib/caoutsearch/filter/default.rb +51 -0
  11. data/lib/caoutsearch/filter/geo_point.rb +11 -0
  12. data/lib/caoutsearch/filter/match.rb +57 -0
  13. data/lib/caoutsearch/filter/none.rb +7 -0
  14. data/lib/caoutsearch/filter/range.rb +28 -0
  15. data/lib/caoutsearch/filter.rb +29 -0
  16. data/lib/caoutsearch/index/base.rb +35 -0
  17. data/lib/caoutsearch/index/document.rb +107 -0
  18. data/lib/caoutsearch/index/indice.rb +55 -0
  19. data/lib/caoutsearch/index/indice_versions.rb +123 -0
  20. data/lib/caoutsearch/index/instrumentation.rb +19 -0
  21. data/lib/caoutsearch/index/internal_dsl.rb +77 -0
  22. data/lib/caoutsearch/index/naming.rb +29 -0
  23. data/lib/caoutsearch/index/reindex.rb +77 -0
  24. data/lib/caoutsearch/index/scoping.rb +54 -0
  25. data/lib/caoutsearch/index/serialization.rb +136 -0
  26. data/lib/caoutsearch/index.rb +7 -0
  27. data/lib/caoutsearch/instrumentation/base.rb +69 -0
  28. data/lib/caoutsearch/instrumentation/index.rb +57 -0
  29. data/lib/caoutsearch/instrumentation/search.rb +41 -0
  30. data/lib/caoutsearch/mappings.rb +79 -0
  31. data/lib/caoutsearch/search/base.rb +27 -0
  32. data/lib/caoutsearch/search/dsl/item.rb +42 -0
  33. data/lib/caoutsearch/search/query/base.rb +16 -0
  34. data/lib/caoutsearch/search/query/boolean.rb +63 -0
  35. data/lib/caoutsearch/search/query/cleaning.rb +29 -0
  36. data/lib/caoutsearch/search/query/getters.rb +35 -0
  37. data/lib/caoutsearch/search/query/merge.rb +27 -0
  38. data/lib/caoutsearch/search/query/nested.rb +23 -0
  39. data/lib/caoutsearch/search/query/setters.rb +68 -0
  40. data/lib/caoutsearch/search/sanitizer.rb +28 -0
  41. data/lib/caoutsearch/search/search/delete_methods.rb +21 -0
  42. data/lib/caoutsearch/search/search/inspect.rb +36 -0
  43. data/lib/caoutsearch/search/search/instrumentation.rb +21 -0
  44. data/lib/caoutsearch/search/search/internal_dsl.rb +77 -0
  45. data/lib/caoutsearch/search/search/naming.rb +47 -0
  46. data/lib/caoutsearch/search/search/query_builder.rb +94 -0
  47. data/lib/caoutsearch/search/search/query_methods.rb +180 -0
  48. data/lib/caoutsearch/search/search/resettable.rb +35 -0
  49. data/lib/caoutsearch/search/search/response.rb +88 -0
  50. data/lib/caoutsearch/search/search/scroll_methods.rb +113 -0
  51. data/lib/caoutsearch/search/search/search_methods.rb +230 -0
  52. data/lib/caoutsearch/search/type_cast.rb +76 -0
  53. data/lib/caoutsearch/search/value.rb +111 -0
  54. data/lib/caoutsearch/search/value_overflow.rb +17 -0
  55. data/lib/caoutsearch/search.rb +6 -0
  56. data/lib/caoutsearch/settings.rb +22 -0
  57. data/lib/caoutsearch/version.rb +5 -0
  58. data/lib/caoutsearch.rb +38 -0
  59. metadata +268 -0
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Search
5
+ module Search
6
+ module SearchMethods
7
+ attr_reader :current_context, :current_order, :current_aggregations,
8
+ :current_suggestions, :current_fields, :current_source
9
+
10
+ # Public API
11
+ # ------------------------------------------------------------------------
12
+ def search(...)
13
+ spawn.search!(...)
14
+ end
15
+
16
+ def context(...)
17
+ spawn.context!(...)
18
+ end
19
+
20
+ def order(...)
21
+ spawn.order!(...)
22
+ end
23
+
24
+ def page(...)
25
+ spawn.page!(...)
26
+ end
27
+
28
+ def limit(...)
29
+ spawn.limit!(...)
30
+ end
31
+ alias_method :per, :limit
32
+
33
+ def offset(...)
34
+ spawn.offset!(...)
35
+ end
36
+
37
+ def aggregate(...)
38
+ spawn.aggregate!(...)
39
+ end
40
+
41
+ def suggest(...)
42
+ spawn.suggest!(...)
43
+ end
44
+
45
+ def fields(...)
46
+ spawn.fields!(...)
47
+ end
48
+ alias_method :returns, :fields
49
+
50
+ def source(...)
51
+ spawn.source!(...)
52
+ end
53
+ alias_method :with_sources, :source
54
+
55
+ def without_sources
56
+ spawn.source!(false)
57
+ end
58
+
59
+ def without_hits
60
+ spawn.source!(false).limit!(0)
61
+ end
62
+
63
+ def track_total_hits(...)
64
+ spawn.track_total_hits!(...)
65
+ end
66
+
67
+ def prepend(...)
68
+ spawn.prepend!(...)
69
+ end
70
+
71
+ def append(...)
72
+ spawn.append!(...)
73
+ end
74
+
75
+ def unscope(...)
76
+ spawn.unscope!(...)
77
+ end
78
+
79
+ # Setters
80
+ # ------------------------------------------------------------------------
81
+ def search!(*values)
82
+ values = values.flatten.map do |value|
83
+ value = value.stringify_keys if value.is_a?(Hash)
84
+ value
85
+ end
86
+
87
+ @search_criteria ||= []
88
+ @search_criteria += values
89
+ self
90
+ end
91
+
92
+ def context!(value)
93
+ @current_context = value
94
+ self
95
+ end
96
+
97
+ def order!(value)
98
+ @current_order = value
99
+ self
100
+ end
101
+
102
+ def page!(value)
103
+ @current_page = value
104
+ self
105
+ end
106
+
107
+ def limit!(value)
108
+ @current_limit = value
109
+ self
110
+ end
111
+
112
+ def offset!(value)
113
+ @current_offset = value
114
+ self
115
+ end
116
+
117
+ def aggregate!(*values)
118
+ @current_aggregations ||= []
119
+ @current_aggregations += values.flatten
120
+ self
121
+ end
122
+
123
+ def suggest!(values, **options)
124
+ raise ArgumentError unless values.is_a?(Hash)
125
+
126
+ @current_suggestions ||= []
127
+ @current_suggestions << [values, options]
128
+ self
129
+ end
130
+
131
+ def fields!(*values)
132
+ @current_fields ||= []
133
+ @current_fields += values.flatten
134
+ self
135
+ end
136
+
137
+ def source!(*values)
138
+ @current_source ||= []
139
+ @current_source += values.flatten
140
+ self
141
+ end
142
+
143
+ def track_total_hits!(value = true)
144
+ @track_total_hits = value
145
+ self
146
+ end
147
+
148
+ def prepend!(hash)
149
+ @prepend_hash = hash
150
+ self
151
+ end
152
+
153
+ def append!(hash)
154
+ @append_hash = hash
155
+ self
156
+ end
157
+
158
+ UNSCOPE_KEYS = {
159
+ "search" => :@search_criteria,
160
+ "search_criteria" => :@search_criteria,
161
+ "limit" => :@current_limit,
162
+ "per" => :@current_limit,
163
+ "aggregate" => :@current_aggregations,
164
+ "aggregations" => :@current_aggregations,
165
+ "suggest" => :@current_suggestions,
166
+ "suggestions" => :@current_suggestions,
167
+ "context" => :@current_context,
168
+ "order" => :@current_order,
169
+ "page" => :@current_page,
170
+ "offset" => :@current_offset,
171
+ "fields" => :@current_fields,
172
+ "source" => :@current_source
173
+ }.freeze
174
+
175
+ def unscope!(key)
176
+ raise ArgumentError unless (variable = UNSCOPE_KEYS[key.to_s])
177
+
178
+ reset_variable(variable)
179
+ self
180
+ end
181
+
182
+ # Getters
183
+ # ------------------------------------------------------------------------
184
+ def search_criteria
185
+ @search_criteria ||= []
186
+ end
187
+
188
+ def current_page
189
+ @current_page&.to_i || 1
190
+ end
191
+
192
+ def current_limit
193
+ @current_limit&.to_i || 10
194
+ end
195
+
196
+ def current_offset
197
+ if @current_offset
198
+ @current_offset.to_i
199
+ elsif @current_page
200
+ (current_limit * (current_page - 1))
201
+ else
202
+ 0
203
+ end
204
+ end
205
+
206
+ # Criteria handlers
207
+ # ------------------------------------------------------------------------
208
+ def find_criterion(key)
209
+ key = key.to_s
210
+ search_criteria.find do |value|
211
+ return value[key] if value.is_a?(Hash) && value.key?(key)
212
+ end
213
+ end
214
+
215
+ def has_criterion?(key)
216
+ key = key.to_s
217
+ search_criteria.any? do |value|
218
+ return true if value.is_a?(Hash) && value.key?(key)
219
+ end
220
+ end
221
+
222
+ def search_criteria_keys
223
+ search_criteria.each_with_object([]) do |criterion, keys|
224
+ keys.concat(criterion.keys) if criterion.is_a?(Hash)
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Search
5
+ module TypeCast
6
+ class << self
7
+ def cast(type, value)
8
+ case type.to_s
9
+ when "integer", "long", "short", "byte"
10
+ cast_as_integer(value)
11
+ when "double", "float", "half_float", "scaled_float"
12
+ cast_as_float(value)
13
+ when "boolean"
14
+ cast_as_boolean(value)
15
+ when "geo_point"
16
+ cast_as_geo_point(value)
17
+ when "date"
18
+ value.to_date.as_json
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ def cast_as_integer(value)
25
+ case value
26
+ when nil then nil
27
+ when Array then value.map { |v| cast_as_integer(v) }
28
+ when String then value.delete(" ").to_i
29
+ else value.to_i
30
+ end
31
+ end
32
+
33
+ def cast_as_float(value)
34
+ case value
35
+ when nil then nil
36
+ when Array then value.map { |v| cast_as_float(v) }
37
+ when String then value.to_s.delete(" ").tr(",", ".").to_f
38
+ else value.to_f
39
+ end
40
+ end
41
+
42
+ # rubocop:disable Lint/BooleanSymbol
43
+ BOOLEAN_FALSE_VALUES = [
44
+ false, 0,
45
+ "0", :"0",
46
+ "f", :f,
47
+ "F", :F,
48
+ "false", :false,
49
+ "FALSE", :FALSE,
50
+ "off", :off,
51
+ "OFF", :OFF
52
+ ].to_set.freeze
53
+ # rubocop:enable Lint/BooleanSymbol
54
+
55
+ def cast_as_boolean(value)
56
+ if value == ""
57
+ nil
58
+ else
59
+ BOOLEAN_FALSE_VALUES.exclude?(value)
60
+ end
61
+ end
62
+
63
+ def cast_as_geo_point(value)
64
+ if value.is_a? Hash
65
+ value = value.stringify_keys
66
+ value = [value["lat"], value["lon"] || value["lng"]]
67
+ end
68
+
69
+ raise ArgumentError, "invalid geo point: #{value.inspect}" unless value.is_a?(Array) && value.length == 2
70
+
71
+ value.map(&:to_f).reverse
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Search
5
+ class Value
6
+ attr_reader :original_value, :type, :null_values, :transform_proc, :overflow_strategy
7
+
8
+ def initialize(original_value, type, null_values: nil, transform: nil, sanitize: false)
9
+ @original_value = original_value
10
+ @type = type
11
+ @null_values = Array.wrap(null_values)
12
+ @sanitize_value = sanitize
13
+ @transform_proc = transform
14
+ @transform_proc = transform.to_proc if transform.is_a?(Symbol)
15
+ end
16
+
17
+ def value
18
+ unless defined?(@value)
19
+ @value = transform_value(original_value)
20
+ @value = cast_value(@value)
21
+ @value = check_value_overflow(@value)
22
+ @value = strip_value(@value)
23
+ @value = sanitize_value(@value) if sanitize_value?
24
+ @value = replace_null_values(@value)
25
+ @value = reduce_value(@value)
26
+ end
27
+
28
+ @value
29
+ end
30
+
31
+ def cast_value(value)
32
+ Caoutsearch::Search::TypeCast.cast(type, value)
33
+ end
34
+
35
+ def sanitize_value(value)
36
+ Caoutsearch::Search::Sanitizer.sanitize(value)
37
+ end
38
+
39
+ def sanitize_value?
40
+ !!@sanitize_value
41
+ end
42
+
43
+ def check_value_overflow(value)
44
+ if value.is_a?(Array)
45
+ value.map { |v| check_value_overflow(v) }
46
+ elsif value.nil?
47
+ value
48
+ else
49
+ range = INTEGER_TYPE_LIMITS[type.to_s]
50
+
51
+ if range
52
+ raise Caoutsearch::Search::ValueOverflow.new(:lower, value, range.first) if value < range.first
53
+ raise Caoutsearch::Search::ValueOverflow.new(:upper, value, range.last) if range && value > range.last
54
+ end
55
+
56
+ value
57
+ end
58
+ end
59
+
60
+ def self.bytes_size_to_integer_range(bytes_size)
61
+ limit = 2**(bytes_size - 1)
62
+ -limit..(limit - 1)
63
+ end
64
+
65
+ INTEGER_TYPE_LIMITS = {
66
+ "long" => bytes_size_to_integer_range(64),
67
+ "integer" => bytes_size_to_integer_range(32),
68
+ "short" => bytes_size_to_integer_range(16),
69
+ "byte" => bytes_size_to_integer_range(8)
70
+ }.freeze
71
+
72
+ def transform_value(value)
73
+ if value.is_a?(Array)
74
+ value.flat_map { |v| transform_value(v) }
75
+
76
+ elsif transform_proc.respond_to?(:call)
77
+ transform_proc.call(value)
78
+
79
+ else
80
+ value
81
+ end
82
+ end
83
+
84
+ def strip_value(value)
85
+ value = value.strip if value.is_a?(String)
86
+ value
87
+ end
88
+
89
+ def replace_null_values(value)
90
+ if value.is_a?(Array)
91
+ value.map { |v| replace_null_values(v) }
92
+
93
+ elsif null_values.include?(value)
94
+ nil
95
+
96
+ else
97
+ value
98
+ end
99
+ end
100
+
101
+ def reduce_value(value)
102
+ if value.is_a?(Array) && value.size == 1
103
+ value[0]
104
+
105
+ else
106
+ value
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Search
5
+ class ValueOverflow < StandardError
6
+ attr_reader :value, :limit, :type
7
+
8
+ def initialize(type, value, limit)
9
+ @type = type
10
+ @value = value
11
+ @limit = limit
12
+
13
+ super("the value #{value.inspect} exceeds the #{type} limit (#{limit})")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ module Search
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ class Settings
5
+ # Build an index mapping
6
+ #
7
+ # Caoutsearch::Settings.new(number_of_replicas: 2)
8
+ #
9
+ def initialize(settings = {})
10
+ @settings = settings.deep_symbolize_keys
11
+ end
12
+
13
+ def to_hash
14
+ @settings
15
+ end
16
+ alias_method :as_json, :to_hash
17
+
18
+ def to_json(*)
19
+ as_json.to_json
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caoutsearch
4
+ VERSION = "0.0.0"
5
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.inflector.inflect(
6
+ "dsl" => "DSL",
7
+ "internal_dsl" => "InternalDSL",
8
+ "none" => "NONE"
9
+ )
10
+ loader.setup
11
+
12
+ module Caoutsearch
13
+ class << self
14
+ attr_writer :client
15
+
16
+ def client
17
+ @client ||= Elasticsearch::Client.new
18
+ end
19
+
20
+ def settings
21
+ @settings ||= Caoutsearch::Settings.new({})
22
+ end
23
+
24
+ def settings=(settings)
25
+ @settings = Caoutsearch::Settings.new(settings)
26
+ end
27
+
28
+ def instrument!(**options)
29
+ @instrumentation_options = options
30
+ Caoutsearch::Instrumentation::Index.attach_to :caoutsearch_index if options[:index]
31
+ Caoutsearch::Instrumentation::Search.attach_to :caoutsearch_search if options[:search]
32
+ end
33
+
34
+ def instrumentation_options
35
+ @instrumentation_options ||= {}
36
+ end
37
+ end
38
+ end