stretchy-model 0.4.0 → 0.6.0

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