stretchy-model 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,7 +9,15 @@ module Stretchy
9
9
  end
10
10
 
11
11
  def first!
12
- spawn.sort(Hash[default_sort_key, :asc]).spawn.size(1)
12
+ spawned = spawn
13
+ if spawned.order_values.length.zero?
14
+ spawn.sort(Hash[default_sort_key, :asc]).spawn.size(1)
15
+ elsif spawned.order_values.length >= 1
16
+ first_order_value = spawned.order_values.shift
17
+ new_direction = Hash[first_order_value.keys.first, :asc]
18
+ spawned.order_values.unshift(new_direction)
19
+ spawned.size(1)
20
+ end
13
21
  self
14
22
  end
15
23
 
@@ -19,7 +27,15 @@ module Stretchy
19
27
  end
20
28
 
21
29
  def last!
22
- spawn.sort(Hash[default_sort_key, :desc]).spawn.size(1)
30
+ spawned = spawn
31
+ if spawned.order_values.length.zero?
32
+ spawn.sort(Hash[default_sort_key, :desc]).spawn.size(1)
33
+ elsif spawned.order_values.length >= 1
34
+ first_order_value = spawned.order_values.shift
35
+ new_direction = Hash[first_order_value.keys.first, :desc]
36
+ spawned.order_values.unshift(new_direction)
37
+ spawned.size(1)
38
+ end
23
39
  self
24
40
  end
25
41
 
@@ -31,7 +47,9 @@ module Stretchy
31
47
  def count!
32
48
  @values[:count] = true
33
49
  @values.delete(:size)
34
- spawn.results
50
+ spawned = spawn
51
+ spawned.order_values.clear
52
+ spawned.results
35
53
  end
36
54
 
37
55
  end
@@ -50,7 +50,7 @@ module Stretchy
50
50
  @other = other
51
51
  end
52
52
 
53
- NORMAL_VALUES = [:where, :first, :last, :filter]
53
+ NORMAL_VALUES = [:where, :first, :last, :filter_query]
54
54
 
55
55
  def normal_values
56
56
  NORMAL_VALUES
@@ -67,7 +67,7 @@ module Stretchy
67
67
  unless value.nil? || (value.blank? && false != value)
68
68
  if name == :select
69
69
  relation._select!(*value)
70
- elsif name == :filter
70
+ elsif name == :filter_query
71
71
  values.each do |v|
72
72
  relation.send("#{name}!", v.first, v.last)
73
73
  end
@@ -114,19 +114,19 @@ module Stretchy
114
114
  lhs_wheres = relation.where_values
115
115
  rhs_wheres = values[:where] || []
116
116
 
117
- lhs_filters = relation.filter_values
118
- rhs_filters = values[:filter] || []
117
+ lhs_filters = relation.filter_query_values
118
+ rhs_filters = values[:filter_query] || []
119
119
 
120
120
  removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
121
121
 
122
122
  where_values = kept + rhs_wheres
123
123
 
124
124
  filters_removed, filters_kept = partition_overwrites(lhs_wheres, rhs_wheres)
125
- filter_values = rhs_filters
125
+ filter_query_values = rhs_filters
126
126
 
127
127
 
128
128
  relation.where_values = where_values.empty? ? nil : where_values
129
- relation.filter_values = filter_values.empty? ? nil : filter_values
129
+ relation.filter_query_values = filter_query_values.empty? ? nil : filter_query_values
130
130
 
131
131
  if values[:reordering]
132
132
  # override any order specified in the original relation
@@ -2,9 +2,10 @@ module Stretchy
2
2
  module Relations
3
3
  class QueryBuilder
4
4
 
5
- attr_reader :structure, :values
5
+ attr_reader :structure, :values, :attribute_types
6
6
 
7
- def initialize(values)
7
+ def initialize(values, attribute_types = nil)
8
+ @attribute_types = attribute_types
8
9
  @structure = Jbuilder.new ignore_nil: true
9
10
  @values = values
10
11
  end
@@ -14,7 +15,7 @@ module Stretchy
14
15
  end
15
16
 
16
17
  def filters
17
- values[:filter]
18
+ values[:filter_query]
18
19
  end
19
20
 
20
21
  def or_filters
@@ -58,7 +59,7 @@ module Stretchy
58
59
  end
59
60
 
60
61
  def query_filters
61
- values[:filter]
62
+ values[:filter_query]
62
63
  end
63
64
 
64
65
  def search_options
@@ -149,7 +150,7 @@ module Stretchy
149
150
  end
150
151
 
151
152
  def build_sort
152
- structure.sort sort.flatten #.inject(Hash.new) { |h,v| h.merge(v) }
153
+ structure.sort sort.map { |arg| keyword_transformer.transform(arg) }.flatten
153
154
  end
154
155
 
155
156
  def build_highlights
@@ -165,7 +166,7 @@ module Stretchy
165
166
  def build_aggregations
166
167
  structure.aggregations do
167
168
  aggregations.each do |agg|
168
- structure.set! agg[:name], aggregation(agg[:name], agg[:args])
169
+ structure.set! agg[:name], aggregation(agg[:name], keyword_transformer.transform(agg[:args], :name))
169
170
  end
170
171
  end
171
172
  end
@@ -199,13 +200,23 @@ module Stretchy
199
200
  def as_must(q)
200
201
  _must = []
201
202
  q.each do |arg|
202
- arg.each_pair { |k,v| _must << (v.is_a?(Array) ? {terms: Hash[k,v]} : {term: Hash[k,v]}) } if arg.class == Hash
203
- _must << {term: Hash[[arg.split(/:/).collect(&:strip)]]} if arg.class == String
204
- _must << arg.first if arg.class == Array
203
+ case arg
204
+ when Hash
205
+ arg = keyword_transformer.transform(arg)
206
+ arg.each_pair do |k,v|
207
+ # If v is an array, we build a terms query otherwise a term query
208
+ _must << (v.is_a?(Array) ? {terms: Hash[k,v]} : {term: Hash[k,v]})
209
+ end
210
+ when String
211
+ _must << {term: Hash[[arg.split(/:/).collect(&:strip)]]}
212
+ when Array
213
+ _must << arg.first
214
+ end
205
215
  end
206
216
  _must.length == 1 ? _must.first : _must
207
217
  end
208
218
 
219
+
209
220
  def as_query_string(q)
210
221
  _and = []
211
222
 
@@ -260,6 +271,9 @@ module Stretchy
260
271
  end
261
272
  end
262
273
 
274
+ def keyword_transformer
275
+ @keyword_transformer ||= Stretchy::Attributes::Transformers::KeywordTransformer.new(@attribute_types)
276
+ end
263
277
  end
264
278
  end
265
279
  end
@@ -15,7 +15,7 @@ module Stretchy
15
15
  :query_string,
16
16
  :aggregation,
17
17
  :search_option,
18
- :filter,
18
+ :filter_query,
19
19
  :or_filter,
20
20
  :extending,
21
21
  :skip_callbacks
@@ -308,7 +308,7 @@ module Stretchy
308
308
 
309
309
 
310
310
 
311
- # @deprecated in elasticsearch 7.x+ use {#filter} instead
311
+ # @deprecated in elasticsearch 7.x+ use {#filter_query} instead
312
312
  def or_filter(name, options = {}, &block)
313
313
  spawn.or_filter!(name, options, &block)
314
314
  end
@@ -322,53 +322,27 @@ module Stretchy
322
322
  #
323
323
  # This method supports all filters supported by Elasticsearch.
324
324
  #
325
- # @overload filter(type, opts)
325
+ # @overload filter_query(type, opts)
326
326
  # @param type [Symbol] the type of filter to add (:range, :term, etc.)
327
327
  # @param opts [Hash] a hash containing the attribute and value to filter by
328
328
  #
329
329
  # @example
330
- # Model.filter(:range, age: {gte: 30})
331
- # Model.filter(:term, color: :blue)
330
+ # Model.filter_query(:range, age: {gte: 30})
331
+ # Model.filter_query(:term, color: :blue)
332
332
  #
333
333
  # @return [Stretchy::Relation] a new relation, which reflects the filter
334
- def filter(name, options = {}, &block)
335
- spawn.filter!(name, options, &block)
334
+ def filter_query(name, options = {}, &block)
335
+ spawn.filter_query!(name, options, &block)
336
336
  end
337
337
 
338
- def filter!(name, options = {}, &block) # :nodoc:
339
- self.filter_values += [{name: name, args: options}]
338
+ def filter_query!(name, options = {}, &block) # :nodoc:
339
+ self.filter_query_values += [{name: name, args: options}]
340
340
  self
341
341
  end
342
342
 
343
343
 
344
344
 
345
- # Adds an aggregation to the query.
346
- #
347
- # @param name [Symbol, String] the name of the aggregation
348
- # @param options [Hash] a hash of options for the aggregation
349
- # @param block [Proc] an optional block to further configure the aggregation
350
- #
351
- # @example
352
- # Model.aggregation(:avg_price, field: :price)
353
- # Model.aggregation(:price_ranges) do
354
- # range field: :price, ranges: [{to: 100}, {from: 100, to: 200}, {from: 200}]
355
- # end
356
- #
357
- # Aggregation results are available in the `aggregations` method of the results under name provided in the aggregation.
358
- #
359
- # @example
360
- # results = Model.where(color: :blue).aggregation(:avg_price, field: :price)
361
- # results.aggregations.avg_price
362
- #
363
- # @return [Stretchy::Relation] a new relation
364
- def aggregation(name, options = {}, &block)
365
- spawn.aggregation!(name, options, &block)
366
- end
367
345
 
368
- def aggregation!(name, options = {}, &block) # :nodoc:
369
- self.aggregation_values += [{name: name, args: assume_keyword_field(options)}]
370
- self
371
- end
372
346
 
373
347
 
374
348
 
@@ -430,7 +404,7 @@ module Stretchy
430
404
  #
431
405
  # @return [ActiveRecord::Relation] a new relation, which reflects the exists filter
432
406
  def has_field(field)
433
- spawn.filter(:exists, {field: field})
407
+ spawn.filter_query(:exists, {field: field})
434
408
  end
435
409
 
436
410
 
@@ -2,6 +2,27 @@ module Stretchy
2
2
  module Utils
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ concerning :ConsoleMethods do
6
+ def reload!
7
+ Stretchy.loader.reload
8
+ end
9
+
10
+ def banner
11
+ banner = <<~BANNER
12
+
13
+ d8b
14
+ d8P d8P ?88
15
+ d888888P d888888P 88b
16
+ .d888b, ?88' 88bd88b d8888b ?88' d8888b 888888b ?88 d8P
17
+ ?8b, 88P 88P' `d8b_,dP 88P d8P' `P 88P `?8bd88 88
18
+ `?8b 88b d88 88b 88b 88b d88 88P?8( d88
19
+ `?888P' `?8b d88' `?888P' `?8b `?888P'd88' 88b`?88P'?8b
20
+ )88
21
+ ,d8P
22
+ `?888P'
23
+ BANNER
24
+ end
25
+ end
5
26
 
6
27
  def self.to_curl(klass, arguments = {}, end_point = "_search")
7
28
  host = klass.gateway.client.transport.transport.hosts&.first || klass.gateway.client.transport.transport.options[:url]
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Stretchy
4
- VERSION = "0.2.0"
2
+ VERSION = '0.4.0'
5
3
  end
data/lib/stretchy.rb CHANGED
@@ -10,13 +10,28 @@ require 'active_support/all'
10
10
  require 'active_model/type/array'
11
11
  require 'active_model/type/hash'
12
12
 
13
- ActiveModel::Type.register(:array, ActiveModel::Type::Array)
14
- ActiveModel::Type.register(:hash, ActiveModel::Type::Hash)
15
-
16
13
  require_relative "stretchy/version"
17
14
  require_relative "rails/instrumentation/railtie" if defined?(Rails)
18
15
 
16
+
17
+
19
18
  module Stretchy
19
+
20
+ def self.loader
21
+ @loader ||= begin
22
+ loader = Zeitwerk::Loader.new
23
+ loader.tag = File.basename(__FILE__, ".rb")
24
+ loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
25
+ loader.push_dir(__dir__)
26
+ loader
27
+ end
28
+ end
29
+
30
+ def self.boot!
31
+ loader.setup
32
+ Stretchy::Attributes.register!
33
+ end
34
+
20
35
  module Errors
21
36
  class QueryOptionMissing < StandardError; end
22
37
  end
@@ -24,9 +39,25 @@ module Stretchy
24
39
  class Configuration
25
40
 
26
41
  attr_accessor :client
42
+ attr_accessor :opensearch
27
43
 
28
44
  def initialize
29
45
  @client = Elasticsearch::Client.new url: 'http://localhost:9200'
46
+ @opensearch = false
47
+ end
48
+
49
+ def client=(client)
50
+ @client = client
51
+ self.opensearch = true if @client.class.name =~ /OpenSearch/
52
+ end
53
+
54
+ def opensearch=(bool)
55
+ @opensearch = bool
56
+ OpenSearchCompatibility.opensearch_patch! if bool
57
+ end
58
+
59
+ def opensearch?
60
+ @opensearch
30
61
  end
31
62
 
32
63
  end
@@ -48,8 +79,6 @@ module Stretchy
48
79
 
49
80
  end
50
81
 
51
- loader = Zeitwerk::Loader.new
52
- loader.tag = File.basename(__FILE__, ".rb")
53
- loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
54
- loader.push_dir(__dir__)
55
- loader.setup
82
+ Stretchy.loader.enable_reloading if defined?(Rails) && Rails.env.development? || ENV['RACK_ENV'] == 'development'
83
+ Stretchy.boot!
84
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stretchy-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spencer Markowski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-05 00:00:00.000000000 Z
11
+ date: 2024-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -164,6 +164,48 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 0.9.36
167
+ - !ruby/object:Gem::Dependency
168
+ name: opensearch-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '3.0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '3.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: octokit
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '4.20'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '4.20'
195
+ - !ruby/object:Gem::Dependency
196
+ name: versionomy
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 0.5.0
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 0.5.0
167
209
  description: Provides a familiar ActiveRecord-like interface for working with Elasticsearch
168
210
  email:
169
211
  - spencer.markowski@theablefew.com
@@ -190,6 +232,9 @@ files:
190
232
  - lib/stretchy/associations.rb
191
233
  - lib/stretchy/associations/associated_validator.rb
192
234
  - lib/stretchy/associations/elastic_relation.rb
235
+ - lib/stretchy/attributes.rb
236
+ - lib/stretchy/attributes/transformers/keyword_transformer.rb
237
+ - lib/stretchy/attributes/type/keyword.rb
193
238
  - lib/stretchy/common.rb
194
239
  - lib/stretchy/delegation/delegate_cache.rb
195
240
  - lib/stretchy/delegation/gateway_delegation.rb
@@ -197,11 +242,13 @@ files:
197
242
  - lib/stretchy/model/callbacks.rb
198
243
  - lib/stretchy/model/serialization.rb
199
244
  - lib/stretchy/null_relation.rb
245
+ - lib/stretchy/open_search_compatibility.rb
200
246
  - lib/stretchy/persistence.rb
201
247
  - lib/stretchy/querying.rb
202
248
  - lib/stretchy/record.rb
203
249
  - lib/stretchy/refreshable.rb
204
250
  - lib/stretchy/relation.rb
251
+ - lib/stretchy/relations/aggregation_methods.rb
205
252
  - lib/stretchy/relations/finder_methods.rb
206
253
  - lib/stretchy/relations/merger.rb
207
254
  - lib/stretchy/relations/query_builder.rb
@@ -242,7 +289,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
289
  - !ruby/object:Gem::Version
243
290
  version: '0'
244
291
  requirements: []
245
- rubygems_version: 3.3.7
292
+ rubygems_version: 3.5.3
246
293
  signing_key:
247
294
  specification_version: 4
248
295
  summary: Rails ORM for Elasticsearch