stretchy-model 0.3.0 → 0.4.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.
@@ -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.3.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
@@ -61,12 +76,9 @@ module Stretchy
61
76
  end
62
77
  end
63
78
 
64
- end
65
79
 
80
+ end
66
81
 
82
+ Stretchy.loader.enable_reloading if defined?(Rails) && Rails.env.development? || ENV['RACK_ENV'] == 'development'
83
+ Stretchy.boot!
67
84
 
68
- loader = Zeitwerk::Loader.new
69
- loader.tag = File.basename(__FILE__, ".rb")
70
- loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
71
- loader.push_dir(__dir__)
72
- loader.setup
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.3.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-06 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
@@ -178,6 +178,34 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
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
181
209
  description: Provides a familiar ActiveRecord-like interface for working with Elasticsearch
182
210
  email:
183
211
  - spencer.markowski@theablefew.com
@@ -204,6 +232,9 @@ files:
204
232
  - lib/stretchy/associations.rb
205
233
  - lib/stretchy/associations/associated_validator.rb
206
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
207
238
  - lib/stretchy/common.rb
208
239
  - lib/stretchy/delegation/delegate_cache.rb
209
240
  - lib/stretchy/delegation/gateway_delegation.rb
@@ -217,6 +248,7 @@ files:
217
248
  - lib/stretchy/record.rb
218
249
  - lib/stretchy/refreshable.rb
219
250
  - lib/stretchy/relation.rb
251
+ - lib/stretchy/relations/aggregation_methods.rb
220
252
  - lib/stretchy/relations/finder_methods.rb
221
253
  - lib/stretchy/relations/merger.rb
222
254
  - lib/stretchy/relations/query_builder.rb
@@ -257,7 +289,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
257
289
  - !ruby/object:Gem::Version
258
290
  version: '0'
259
291
  requirements: []
260
- rubygems_version: 3.3.7
292
+ rubygems_version: 3.5.3
261
293
  signing_key:
262
294
  specification_version: 4
263
295
  summary: Rails ORM for Elasticsearch