caoutsearch 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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