stretchy-model 0.4.0 → 0.6.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/README.md +19 -84
  4. data/lib/rails/instrumentation/railtie.rb +2 -0
  5. data/lib/stretchy/associations.rb +155 -15
  6. data/lib/stretchy/attributes/type/array.rb +20 -0
  7. data/lib/stretchy/attributes/type/base.rb +42 -0
  8. data/lib/stretchy/attributes/type/binary.rb +45 -0
  9. data/lib/stretchy/attributes/type/boolean.rb +48 -0
  10. data/lib/stretchy/attributes/type/completion.rb +25 -0
  11. data/lib/stretchy/attributes/type/constant_keyword.rb +38 -0
  12. data/lib/stretchy/attributes/type/date_time.rb +35 -0
  13. data/lib/stretchy/attributes/type/dense_vector.rb +59 -0
  14. data/lib/stretchy/attributes/type/flattened.rb +31 -0
  15. data/lib/stretchy/attributes/type/geo_point.rb +27 -0
  16. data/lib/stretchy/attributes/type/geo_shape.rb +27 -0
  17. data/lib/stretchy/attributes/type/hash.rb +40 -0
  18. data/lib/stretchy/attributes/type/histogram.rb +7 -0
  19. data/lib/stretchy/attributes/type/ip.rb +29 -0
  20. data/lib/stretchy/attributes/type/join.rb +22 -0
  21. data/lib/stretchy/attributes/type/keyword.rb +36 -10
  22. data/lib/stretchy/attributes/type/match_only_text.rb +8 -0
  23. data/lib/stretchy/attributes/type/nested.rb +25 -0
  24. data/lib/stretchy/attributes/type/numeric/base.rb +32 -0
  25. data/lib/stretchy/attributes/type/numeric/byte.rb +7 -0
  26. data/lib/stretchy/attributes/type/numeric/double.rb +7 -0
  27. data/lib/stretchy/attributes/type/numeric/float.rb +7 -0
  28. data/lib/stretchy/attributes/type/numeric/half_float.rb +7 -0
  29. data/lib/stretchy/attributes/type/numeric/integer.rb +7 -0
  30. data/lib/stretchy/attributes/type/numeric/long.rb +7 -0
  31. data/lib/stretchy/attributes/type/numeric/scaled_float.rb +23 -0
  32. data/lib/stretchy/attributes/type/numeric/short.rb +7 -0
  33. data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +7 -0
  34. data/lib/stretchy/attributes/type/percolator.rb +23 -0
  35. data/lib/stretchy/attributes/type/point.rb +24 -0
  36. data/lib/stretchy/attributes/type/range/base.rb +9 -0
  37. data/lib/stretchy/attributes/type/range/date_range.rb +17 -0
  38. data/lib/stretchy/attributes/type/range/double_range.rb +17 -0
  39. data/lib/stretchy/attributes/type/range/float_range.rb +16 -0
  40. data/lib/stretchy/attributes/type/range/integer_range.rb +16 -0
  41. data/lib/stretchy/attributes/type/range/ip_range.rb +16 -0
  42. data/lib/stretchy/attributes/type/range/long_range.rb +16 -0
  43. data/lib/stretchy/attributes/type/rank_feature.rb +21 -0
  44. data/lib/stretchy/attributes/type/rank_features.rb +24 -0
  45. data/lib/stretchy/attributes/type/search_as_you_type.rb +30 -0
  46. data/lib/stretchy/attributes/type/shape.rb +24 -0
  47. data/lib/stretchy/attributes/type/sparse_vector.rb +37 -0
  48. data/lib/stretchy/attributes/type/string.rb +7 -0
  49. data/lib/stretchy/attributes/type/text.rb +51 -0
  50. data/lib/stretchy/attributes/type/token_count.rb +26 -0
  51. data/lib/stretchy/attributes/type/version.rb +21 -0
  52. data/lib/stretchy/attributes/type/wildcard.rb +35 -0
  53. data/lib/stretchy/attributes.rb +68 -2
  54. data/lib/stretchy/common.rb +5 -5
  55. data/lib/stretchy/delegation/gateway_delegation.rb +9 -5
  56. data/lib/stretchy/model/serialization.rb +1 -0
  57. data/lib/stretchy/querying.rb +4 -4
  58. data/lib/stretchy/record.rb +9 -10
  59. data/lib/stretchy/relation.rb +11 -17
  60. data/lib/stretchy/relations/finder_methods.rb +19 -2
  61. data/lib/stretchy/relations/merger.rb +5 -1
  62. data/lib/stretchy/relations/query_builder.rb +32 -3
  63. data/lib/stretchy/relations/query_methods.rb +66 -2
  64. data/lib/stretchy/scoping/named.rb +1 -1
  65. data/lib/stretchy/shared_scopes.rb +1 -1
  66. data/lib/stretchy/version.rb +1 -1
  67. data/lib/stretchy.rb +5 -3
  68. data/lib/stretchy_model.rb +9 -0
  69. metadata +49 -4
  70. data/lib/active_model/type/array.rb +0 -13
  71. data/lib/active_model/type/hash.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7dff610329ad21128c58429c6f9d08b62138a7fad9adb0f610984bd744db1cf
4
- data.tar.gz: 915256c1b413dd34d4777097b878f0c2c7886342b5d8dcfedc2159a405b3bdf2
3
+ metadata.gz: f23580fe0c8761ced02da75cbc44f16ab7893a559d30d9a5e0de23aa96ed790a
4
+ data.tar.gz: 821381d3e9e92822d71aac637d98f8b04a76736a80e421ca7d2c4fd05fd108d8
5
5
  SHA512:
6
- metadata.gz: 66f98b878a8e78d9d79b53f05edf89a8d4a61d4fd3a5b98d93ef349491c7c87f36d4cadb857b8be5e9e992308609c22d1abc45703eb39b95e78240d87c6ae081
7
- data.tar.gz: 0eabc3b84d0aecb0f8051bf53238ec6da5d431c7ff5d0f7c3be20888a20252504e3324c7b558fe455d90c2cf0db827b1d0440e6d03d211569ed8ed7d60dade01
6
+ metadata.gz: 99dc36ee6cce021e869a8e1f3e2f005f546b1c50bf973434c624574c1dd601757baf4a1d39562f7ddd05bf4a1e640512111b821d8a70b8d5416bf73701b38f03
7
+ data.tar.gz: 4a58307bd7ae5a5781e7462df9c95438f66eecab5d0ae43238878d228e2ca6939a8a7643b70ac3c2586dcae537935206bfb15339415f6b3c3a70ef2518e401b4
data/.rspec CHANGED
@@ -1 +1 @@
1
- --require spec_helper
1
+ --require spec_helper
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  stretchy-model
2
2
  ===
3
-
4
3
  <p>
5
4
  <a href="https://stretchy.io/" target="_blank"><img src="./stretchy.logo.png" alt="Gum Image" width="450" /></a>
6
5
  <br><br>
@@ -9,104 +8,42 @@ stretchy-model
9
8
 
10
9
  </p>
11
10
 
12
- Stretchy provides Elasticsearch models in a Rails environment with an integrated ActiveRecord-like interface and features.
13
11
 
14
12
  ## Features
15
13
  Stretchy simplifies the process of querying, aggregating, and managing Elasticsearch-backed models, allowing Rails developers to work with search indices as comfortably as they would with traditional Rails models.
16
14
 
17
- ## Attributes
18
-
19
- ```ruby
20
- class Post < Stretchy::Record
21
-
22
- attribute :title, :string
23
- attribute :body, :string
24
- attribute :flagged, :boolean, default: false
25
- attribute :author, :hash
26
- attribute :tags, :array, default: []
27
-
28
- end
29
- ```
30
- >[!NOTE]
31
- >`created_at`, `:updated_at` and `:id` are automatically added to all `Stretchy::Records`
32
-
33
-
34
- ## Query
35
- ```ruby
36
- Post.where('author.name': "Jadzia", flagged: true).first
37
- #=> <Post id: aW02w3092, title: "Fun Cats", body: "...", flagged: true,
38
- # author: {name: "Jadzia", age: 20}, tags: ["cat", "amusing"]>
39
- ```
40
-
41
- ## Aggregations
42
- ```ruby
43
-
44
- result = Post.filter(:range, 'author.age': {gte: 18})
45
- .aggregation(:post_frequency, date_histogram: {
46
- field: :created_at,
47
- calender_interval: :month
48
- })
49
-
50
- result.aggregations.post_frequency
51
- #=> {buckets: [{key_as_string: "2024-01-01", doc_count: 20}, ...]}
52
- ```
53
-
54
- ## Scopes
55
-
56
- ```ruby
57
- class Post < Stretchy::Record
58
- # ...attributes
59
-
60
- # Scopes
61
- scope :flagged, -> { where(flagged: true) }
62
- scope :top_links, lambda do |size=10, url=".com"|
63
- aggregation(:links,
64
- terms: {
65
- field: :links,
66
- size: size,
67
- include: ".*#{url}.*"
68
- })
69
- end
70
- end
15
+ * Model fully back by Elasticsearch/Opensearch
16
+ * Chain queries, scopes and aggregations
17
+ * Reduce Elasticsearch query complexity
18
+ * Support for time-based indices and aliases
19
+ * Associations to both ActiveRecord models and Stretchy::Record
20
+ * Bulk Operations made easy
21
+ * Validations, custom attributes, and more...
71
22
 
72
- # Returns flagged posts and includes the top 10 'youtube.com'
73
- # links in results.aggregations.links
74
- result = Post.flagged.top_links(10, "youtube.com")
23
+ Follow the guides to learn more about:
75
24
 
76
- ```
25
+ * [Models](https://theablefew.github.io/stretchy/#/guides/models?id=models)
26
+ * [Querying](https://theablefew.github.io/stretchy/#/guides/querying?id=querying)
27
+ * [Aggregations](https://theablefew.github.io/stretchy/#/guides/aggregations?id=aggregations)
28
+ * [Scopes](https://theablefew.github.io/stretchy/#/guides/scopes?id=scopes)
77
29
 
78
- ## Bulk Operations
79
30
 
31
+ [Read the Documentation](https://theablefew.github.io/stretchy/#/) or walk through of a simple [Data Analysis](https://theablefew.github.io/stretchy/#/examples/data_analysis?id=data-analysis) example.
80
32
 
81
- ```ruby
82
- Model.bulk(records_as_bulk_operations)
83
- ```
84
33
 
85
- #### Bulk helper
86
- Generates structure for the bulk operation
87
- ```ruby
88
- record.to_bulk # default to_bulk(:index)
89
- record.to_bulk(:delete)
90
- record.to_bulk(:update)
91
- ```
92
-
93
- #### In batches
94
- Run bulk operations in batches specified by `size`
95
- ```ruby
96
- Model.bulk_in_batches(records, size: 100) do |batch|
97
- batch.map! { |record| Model.new(record).to_bulk }
98
- end
99
- ```
100
34
 
101
35
  ## Installation
102
36
 
103
37
  Install the gem and add to the application's Gemfile by executing:
104
38
 
105
- $ bundle add stretchy-model
39
+ ```sh
40
+ bundle add stretchy-model
41
+ ```
106
42
 
107
43
  If bundler is not being used to manage dependencies, install the gem by executing:
108
-
109
- $ gem install stretchy-model
44
+ ```sh
45
+ gem install stretchy-model
46
+ ```
110
47
 
111
48
  <details>
112
49
  <summary>Rails Configuration</summary>
@@ -145,8 +82,6 @@ end
145
82
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
146
83
 
147
84
  >[!TIP]
148
- >This library is built on top of the excellent [elasticsearch-persistence](https://github.com/elastic/elasticsearch-rails/tree/main/elasticsearch-persistence) gem.
149
- >
150
85
  > Full documentation on [Elasticsearch Query DSL and Aggregation options](https://github.com/elastic/elasticsearch-rails/tree/main/elasticsearch-persistence)
151
86
 
152
87
  ## Testing
@@ -4,6 +4,8 @@ module Stretchy
4
4
  class Railtie < ::Rails::Railtie
5
5
 
6
6
  require 'rails/instrumentation/publishers'
7
+ Stretchy.instrument!
8
+
7
9
  ActiveSupport::Notifications.subscribe 'search.stretchy' do |name, start, finish, id, payload|
8
10
  message = [
9
11
  Rainbow(" #{payload[:klass]}").bright,
@@ -3,7 +3,11 @@ module Stretchy
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  def save!
6
+ if valid?
6
7
  self.save
8
+ else
9
+ raise "Record is invalid"
10
+ end
7
11
  end
8
12
 
9
13
  # Required for Elasticsearch < 7
@@ -34,7 +38,7 @@ module Stretchy
34
38
  end
35
39
 
36
40
  def association_reflection(association)
37
- ElasticRelation.new @@_associations[association], (dirty[association.to_sym] || [])
41
+ Stretchy::Relation.new @@_associations[association], (dirty[association.to_sym] || {})
38
42
  end
39
43
 
40
44
  def _destroy=(bool)
@@ -48,6 +52,7 @@ module Stretchy
48
52
  def save_associations
49
53
  @_after_save_objects.each_pair do |association, collection|
50
54
  collection.each do |instance|
55
+ # TODO: bulk update
51
56
  instance.send("#{@@_association_options[association.to_sym][:foreign_key]}=", self.id)
52
57
  instance.save
53
58
  end
@@ -59,59 +64,194 @@ module Stretchy
59
64
  @@_associations ||= {}
60
65
  @@_association_options ||= {}
61
66
 
67
+ # The belongs_to method is used to set up a one-to-one connection with another model.
68
+ # This indicates that this model has exactly one instance of another model.
69
+ # For example, if your application includes authors and books, and each book can be assigned exactly one author,
70
+ # you'd declare the book model to belong to the author model.
71
+ #
72
+ # association:: [Symbol] the name of the association
73
+ # options:: [Hash] a hash to set up options for the association
74
+ # :foreign_key - the foreign key used for the association. Defaults to "#{association}_id"
75
+ # :primary_key - the primary key used for the association. Defaults to "id"
76
+ # :class_name - the name of the associated object's class. Defaults to the name of the association
77
+ #
78
+ # Example:
79
+ # belongs_to :author
80
+ #
81
+ # This creates a book.author method that returns the author of the book.
82
+ # It also creates an author= method that allows you to assign the author of the book.
83
+ #
62
84
  def belongs_to(association, options = {})
63
85
  @@_association_options[association] = {
64
86
  foreign_key: "#{association}_id",
65
87
  primary_key: "id",
66
88
  class_name: association
67
- }.reverse_merge(options)
89
+ }.merge(options)
68
90
 
69
91
  klass = @@_association_options[association][:class_name].to_s.singularize.classify.constantize
70
92
  @@_associations[association] = klass
71
93
 
72
94
  define_method(association.to_sym) do
73
- klass.where(_id: self.send(@@_association_options[association][:foreign_key].to_sym)).first
95
+ instance_variable_get("@#{association}") ||
96
+ klass.where(_id: self.send(@@_association_options[association][:foreign_key].to_sym)).first
74
97
  end
75
98
 
76
99
  define_method("#{association}=".to_sym) do |val|
77
100
  options = @@_association_options[association]
78
- instance_variable_set("@#{options[:foreign_key]}", val.send(options[:primary_key]))
101
+ self.send("#{options[:foreign_key]}=", val.send(options[:primary_key]))
102
+ instance_variable_set("@#{association}", val)
103
+ end
104
+
105
+ define_method("build_#{association}") do |*args|
106
+ associated_object = klass.new(*args)
107
+ instance_variable_set("@#{association}", associated_object)
108
+ associated_object
109
+ end
110
+
111
+ before_save do
112
+ associated_object = instance_variable_get("@#{association}")
113
+ if associated_object && associated_object.new_record?
114
+ if associated_object.save!
115
+ self.send("#{@@_association_options[association][:foreign_key]}=", associated_object.id)
116
+ end
117
+ end
79
118
  end
80
119
  end
81
120
 
82
- def has_one(association, class_name: nil, foreign_key: nil, dependent: :destroy)
83
121
 
84
- klass = association.to_s.singularize.classify.constantize unless class_name.present?
85
- foreign_key = "#{self.name.downcase}_id" unless foreign_key.present?
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+ # The has_one method is used to set up a one-to-one connection with another model.
131
+ # This indicates that this model contains the foreign key.
132
+ #
133
+ # association:: [Symbol] The name of the association.
134
+ # options:: [Hash] A hash to set up options for the association.
135
+ # :class_name - The name of the associated model. If not provided, it's derived from +association+.
136
+ # :foreign_key - The name of the foreign key on the associated model. If not provided, it's derived from the name of this model.
137
+ # :dependent - If set to +:destroy+, the associated object will be destroyed when this object is destroyed. This is the default behavior.
138
+ # :primary_key - The name of the primary key on the associated model. If not provided, it's assumed to be +id+.
139
+ #
140
+ #
141
+ # Example:
142
+ # has_one :profile
143
+ #
144
+ # This creates a user.profile method that returns the profile of the user.
145
+ # It also creates a profile= method that allows you to assign the profile of the user.
146
+ #
147
+ def has_one(association, options = {})
148
+
149
+ @@_association_options[association] = {
150
+ foreign_key: "#{self.name.underscore}_id",
151
+ primary_key: "id",
152
+ class_name: association
153
+ }.merge(options)
154
+
155
+ klass = @@_association_options[association][:class_name].to_s.singularize.classify.constantize
86
156
  @@_associations[association] = klass
87
157
 
158
+ foreign_key = @@_association_options[association][:foreign_key]
159
+
88
160
  define_method(association.to_sym) do
89
- klass.where("#{foreign_key}": self.id).first
161
+ instance_variable_get("@#{association}") ||
162
+ klass.where("#{foreign_key}": self.id).first
163
+ end
164
+
165
+ define_method("#{association}=".to_sym) do |val|
166
+ instance_variable_set("@#{association}", val)
167
+ save!
168
+ end
169
+
170
+ before_save do
171
+ associated_object = instance_variable_get("@#{association}")
172
+ if associated_object
173
+ associated_object.send("#{foreign_key}=", self.id)
174
+ associated_object.save!
175
+ end
90
176
  end
91
177
  end
92
178
 
93
- def has_many(association, klass, options = {})
94
- @@_associations[association] = klass
95
179
 
96
- opt_fk = options.delete(:foreign_key)
97
- foreign_key = opt_fk ? opt_fk : "#{self.name.split("::").last.tableize.singularize}_id"
98
180
 
99
- @@_association_options[association] = { foreign_key: foreign_key }
181
+
182
+
183
+
184
+
185
+
186
+
187
+ # The has_many method is used to set up a one-to-many connection with another model.
188
+ # This indicates that this model can be matched with zero or more instances of another model.
189
+ # For example, if your application includes authors and books, and each author can have many books,
190
+ # you'd declare the author model to have many books.
191
+ #
192
+ # association:: [Symbol] the name of the association
193
+ # options:: [Hash] a hash to set up options for the association
194
+ # :foreign_key - the foreign key used for the association. Defaults to "#{self.name.downcase}_id"
195
+ # :primary_key - the primary key used for the association. Defaults to "id"
196
+ # :class_name - the name of the associated object's class. Defaults to the name of the association
197
+ # :dependent - if set to :destroy, the associated object will be destroyed when this object is destroyed. This is the default behavior.
198
+ #
199
+ #
200
+ # Example:
201
+ # has_many :books
202
+ #
203
+ # This creates an author.books method that returns a collection of books for the author.
204
+ # It also creates a books= method that allows you to assign the books for the author.
205
+ #
206
+ def has_many(association, options = {})
207
+ @@_association_options[association] = {
208
+ foreign_key: "#{self.name.underscore}_id",
209
+ primary_key: "id",
210
+ class_name: association.to_s.singularize.to_sym
211
+ }.merge(options)
212
+
213
+ klass = @@_association_options[association][:class_name].to_s.classify.constantize
214
+ foreign_key = @@_association_options[association][:foreign_key]
215
+ primary_key = @@_association_options[association][:primary_key]
216
+ @@_associations[association] = klass
100
217
 
101
218
  define_method(association.to_sym) do
102
219
  args = {}
103
- args[foreign_key] = self.id
220
+ args["_#{primary_key}"] = self.send("#{association.to_s.singularize}_ids")
104
221
  self.new_record? ? association_reflection(association) : klass.where(args)
105
222
  end
106
223
 
224
+ define_method("#{association.to_s.singularize}_ids") do
225
+ instance_variable_get("@#{association.to_s.singularize}_ids".to_sym)
226
+ end
227
+
228
+ define_method("#{association.to_s.singularize}_ids=") do |val|
229
+ instance_variable_set("@#{association.to_s.singularize}_ids".to_sym, val)
230
+ end
231
+
232
+ define_method("#{association}=".to_sym) do |val|
233
+ val.each { |v| after_save_objects(v.attributes, association)}
234
+ self.send("#{association.to_s.singularize}_ids=", val.map(&:id))
235
+ dirty
236
+ end
237
+
107
238
  define_method("build_#{association}".to_sym) do |*args|
108
239
  opts = {}
109
240
  opts[foreign_key] = self.id
110
241
  args.first.merge! opts
111
242
  klass.new *args
112
243
  end
244
+
245
+ after_save do
246
+ save_associations
247
+ end
113
248
  end
114
249
 
250
+
251
+
252
+
253
+
254
+
115
255
  def validates_associated(*attr_names)
116
256
  validates_with AssociatedValidator, _merge_attributes(attr_names)
117
257
  end
@@ -131,7 +271,7 @@ module Stretchy
131
271
  end
132
272
 
133
273
  def reflect_on_association(association)
134
- ElasticRelation.new @@_associations[association]
274
+ Stretchy::Relation.new @@_associations[association]
135
275
  end
136
276
 
137
277
  def update_all(records, **attributes)
@@ -0,0 +1,20 @@
1
+ module Stretchy::Attributes::Type
2
+ class Array < Stretchy::Attributes::Type::Base # :nodoc:
3
+ OPTIONS = [:data_type, :fields]
4
+ def type
5
+ :array
6
+ end
7
+
8
+ def type_for_database
9
+ data_type || :text
10
+ end
11
+
12
+ def mappings(name)
13
+ options = {type: type_for_database}
14
+ self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
15
+ options.delete(:fields) if fields == false
16
+ options[:fields] = {keyword: {type: :keyword, ignore_above: 256}} if type_for_database == :text && fields.nil?
17
+ { name => options }.as_json
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ module Stretchy
2
+ module Attributes
3
+ module Type
4
+ class Base < ActiveModel::Type::Value
5
+
6
+ OPTIONS = []
7
+
8
+
9
+ def initialize(**args)
10
+
11
+ define_option_methods!
12
+
13
+ args.each do |k, v|
14
+ if self.class::OPTIONS.include?(k)
15
+ instance_variable_set("@#{k}", v)
16
+ args.delete(k)
17
+ end
18
+ end
19
+ super
20
+ end
21
+
22
+ def mappings(name)
23
+ options = {type: type_for_database}
24
+ self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
25
+ { name => options }.as_json
26
+ end
27
+
28
+ def type_for_database
29
+ type
30
+ end
31
+
32
+ private
33
+
34
+ def define_option_methods!
35
+ self.class::OPTIONS.each do |option|
36
+ define_singleton_method(option.to_sym) { instance_variable_get("@#{option}") }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ # Public: Defines a binary attribute for the model.
2
+ #
3
+ # name - The Symbol name of the attribute.
4
+ # opts - The Hash options used to refine the attribute (default: {}):
5
+ # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
+ # This allows it to be used later for sorting, aggregations, or scripting. Defaults to false.
7
+ # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
8
+ #
9
+ # Examples
10
+ #
11
+ # class MyModel
12
+ # include StretchyModel
13
+ # attribute :name, :binary, doc_values: true, store: true
14
+ # end
15
+ #
16
+ # Returns nothing.
17
+ module Stretchy
18
+ module Attributes
19
+ module Type
20
+ class Binary < ActiveModel::Type::Value
21
+ OPTIONS = [:doc_values, :store]
22
+ attr_reader *OPTIONS
23
+
24
+ def initialize(**args)
25
+ args.each do |k, v|
26
+ instance_variable_set("@#{k}", v) if OPTIONS.include?(k)
27
+ args.delete(k)
28
+ end
29
+ super
30
+ end
31
+
32
+ def type
33
+ :binary
34
+ end
35
+
36
+
37
+ def mappings(name)
38
+ options = {type: type}
39
+ OPTIONS.each { |_| options[_] = self.send(_) }
40
+ { name => options }.as_json
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ # Public: Defines a boolean attribute for the model.
2
+ #
3
+ # name - The Symbol name of the attribute.
4
+ # opts - The Hash options used to refine the attribute (default: {}):
5
+ # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
+ # This allows it to be used later for sorting, aggregations, or scripting. Defaults to true.
7
+ # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
8
+ # :ignore_malformed - The Boolean indicating if exceptions thrown when trying to index the wrong data type into a field should be ignored. Defaults to false.
9
+ # :null_value - The Boolean value to be substituted for any explicit null values. Defaults to null.
10
+ # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
11
+ # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
12
+ # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
13
+ # :meta - The Hash metadata about the field.
14
+ #
15
+ # Examples
16
+ #
17
+ # class MyModel
18
+ # include StretchyModel
19
+ # attribute :name, :boolean, doc_values: true, store: true
20
+ # end
21
+ #
22
+ # Returns nothing.
23
+ module Stretchy
24
+ module Attributes
25
+ module Type
26
+ class Boolean < Stretchy::Attributes::Type::Base
27
+
28
+ OPTIONS = [:doc_values, :index, :ignore_malformed, :null_value, :on_script_error, :script, :store, :meta]
29
+ attr_reader *OPTIONS
30
+
31
+ def initialize(**args)
32
+ args.each do |k, v|
33
+ instance_variable_set("@#{k}", v) if OPTIONS.include?(k)
34
+ args.delete(k)
35
+ end
36
+ super
37
+ end
38
+
39
+ def type
40
+ :boolean
41
+ end
42
+
43
+
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module Stretchy::Attributes::Type
2
+ # Public: Defines a completion attribute for the model.
3
+ #
4
+ # opts - The Hash options used to refine the attribute (default: {}):
5
+ # :analyzer - The String index analyzer to use. Defaults to 'simple'.
6
+ # :search_analyzer - The String search analyzer to use. Defaults to the value of :analyzer.
7
+ # :preserve_separators - The Boolean indicating if separators should be preserved. Defaults to true.
8
+ # :preserve_position_increments - The Boolean indicating if position increments should be enabled. Defaults to true.
9
+ # :max_input_length - The Integer limit for the length of a single input. Defaults to 50.
10
+ #
11
+ # Examples
12
+ #
13
+ # class MyModel < Stretchy::Record
14
+ # attribute :name, :completion, analyzer: 'simple', max_input_length: 100
15
+ # end
16
+ #
17
+ # Returns nothing.
18
+ class Completion < Stretchy::Attributes::Type::Base
19
+ OPTIONS = [:analyzer, :search_analyzer, :preserve_separators, :preserve_position_increments, :max_input_length]
20
+ attr_reader *OPTIONS
21
+ def type
22
+ :completion
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module Stretchy::Attributes::Type
2
+ # Public: Defines a constant_keyword attribute for the model. This field type is a specialization of the keyword field, but it only accepts a single value.
3
+ #
4
+ # opts - The Hash options used to refine the attribute (default: {}):
5
+ # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion. Defaults to true.
6
+ # :eager_global_ordinals - The Boolean indicating if global ordinals should be loaded eagerly on refresh. Defaults to false.
7
+ # :fields - The Hash of multi-fields for the same string value to be indexed in multiple ways.
8
+ # :ignore_above - The Integer limit for the length of the string. Strings longer than this limit will not be indexed. Defaults to 2147483647.
9
+ # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
10
+ # :index_options - The String indicating what information should be stored in the index for scoring purposes. Defaults to 'docs'.
11
+ # :meta - The Hash metadata about the field.
12
+ # :norms - The Boolean indicating if field-length should be taken into account when scoring queries. Defaults to false.
13
+ # :null_value - The String value to be substituted for any explicit null values. Defaults to null.
14
+ # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
15
+ # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
16
+ # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
17
+ # :similarity - The String scoring algorithm or similarity to be used. Defaults to 'BM25'.
18
+ # :normalizer - The String pre-processor for the keyword prior to indexing. Defaults to null.
19
+ # :split_queries_on_whitespace - The Boolean indicating if full text queries should split the input on whitespace. Defaults to false.
20
+ # :time_series_dimension - The Boolean indicating if the field is a time series dimension. Defaults to false.
21
+ # :value - The String value to associate with all documents in the index.
22
+ #
23
+ # Examples
24
+ #
25
+ # class MyModel
26
+ # include StretchyModel
27
+ # attribute :status, :constant_keyword, value: 'active'
28
+ # end
29
+ #
30
+ # Returns nothing.
31
+ class ConstantKeyword < Stretchy::Attributes::Type::Keyword
32
+ OPTIONS = OPTIONS + [:value]
33
+ attr_reader *OPTIONS
34
+ def type
35
+ :constant_keyword
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Stretchy::Attributes::Type
2
+ # Public: Defines a datetime attribute for the model.
3
+ #
4
+ # opts - The Hash options used to refine the attribute (default: {}):
5
+ # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
+ # This allows it to be used later for sorting, aggregations, or scripting. Defaults to true.
7
+ # :format - The String date format(s) that can be parsed. Defaults to 'strict_date_optional_time||epoch_millis'.
8
+ # :locale - The String locale to use when parsing dates. Defaults to the ROOT locale.
9
+ # :ignore_malformed - The Boolean indicating if malformed numbers should be ignored. Defaults to false.
10
+ # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
11
+ # :null_value - The Date value to be substituted for any explicit null values. Defaults to null.
12
+ # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
13
+ # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
14
+ # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
15
+ # :meta - The Hash metadata about the field.
16
+ #
17
+ # Examples
18
+ #
19
+ # class MyModel < Stretchy::Record
20
+ # attribute :created_at, :datetime, format: 'strict_date_optional_time||epoch_millis', locale: 'en'
21
+ # end
22
+ #
23
+ # Returns nothing.
24
+ class DateTime < Stretchy::Attributes::Type::Base
25
+ OPTIONS = [:doc_values, :format, :locale, :ignore_malformed, :index, :null_value, :on_script_error, :script, :store, :meta]
26
+ attr_reader *OPTIONS
27
+ def type
28
+ :datetime
29
+ end
30
+
31
+ def type_for_database
32
+ :date
33
+ end
34
+ end
35
+ end