mongodb_meilisearch 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f57c18174244b2885536a990a2d602ce833e949dcf205112c78f30567a6eee6a
4
- data.tar.gz: bb5bdd6c8cb90a6e1334c4606063a56b71515510f0f45c3e1ae2d817b4497163
3
+ metadata.gz: 8dcda2174ac28dc1b3e134d90d2fc92b2d43e6815cab31dfc8a2c8954939d658
4
+ data.tar.gz: 45b658a38a761d8c02bc1ee4d21f82d7762a60e749ba14b8ec53e987284bdc4f
5
5
  SHA512:
6
- metadata.gz: cb3e4f69dad6365682470020e3acc864492c45d79de54fb65fbab6fb9eaacb0811d8af02414f4df324a9d0cf64278646e0eac3062e7aff1a9873aa2f264d0d64
7
- data.tar.gz: 54aa4eea1cfe0b378e9ad93c719eb31a5c35c96c08ef842d975efb8391cf1b55b0ca53e9ca3d8daf98626a02c30ea817e9623c78de0b6ce14ade726473767304
6
+ metadata.gz: 425ebd155ffec9c67e96cbb8b028be16a27948566d8cd5e64bfb4226a9a3d89551a449cad2bc9c8ecb6dd2261cab702fdf426c603c82a5402d7660de90596149
7
+ data.tar.gz: 322b9b928ea51c65df41906e872c9497af27b8cda2b2e04e0f1d360348c6ed9d4e7ebfad731d6e97956fa4d3cf0eb3b7b686db8352fcd2285be984690138eade
data/CHANGELOG.md CHANGED
@@ -1,3 +1,52 @@
1
+ ## [1.2.0] - 2024-06-19
2
+
3
+ ### Features
4
+ - The value of `SEARCHABLE_ATTRIBUTES` can now optionally
5
+ include method names, not just field names.
6
+
7
+ This makes for easier incorporation of dynamically generated index values,
8
+ because it eliminates many cases where you would have had to define
9
+ a `search_indexable_hash` method. If you already have one, it'll
10
+ continue to work.
11
+
12
+ ## [1.1.1] 2023-11-13
13
+ bug fix
14
+
15
+ ### Fixes
16
+
17
+ Issue #7 - adding the first document from a new model didn't
18
+ configure the filterable attributes or searchable attributes.
19
+ The lack of the former would cause basic searches to fail
20
+ because of our expectation that "object_class" will always
21
+ be a filterable field unless you've specifically designated
22
+ your model as unfilterable.
23
+
24
+
25
+ ## [1.1.0] - 2023-09-16
26
+ Sorting & Filtering
27
+
28
+ ## Additions
29
+ - adds the ability to specify sortable attributes
30
+ - these default to match the filterable attributes
31
+ - filterable attributes still default to match
32
+ searchable attributes
33
+ - `set_filterable_attributes`
34
+ - `set_sortable_attributes`
35
+ - `set_sortable_attributes!`
36
+ - `reindex` and `reindex!` now set searchable attributes
37
+
38
+ ## Fixes
39
+ - `set_filterable_attributes!` is now synchronous as the name implies
40
+ - `reindex_core` correctly honors the user's async request when
41
+ setting filterable attributes (previously it was always async)
42
+
43
+ ## [1.0.1] - 2023-09-2
44
+
45
+ ids from `belongs_to` relations are no longer
46
+ indexed by default. You can explicitly specify
47
+ their inclusion if you want.
48
+
49
+
1
50
  ## [1.0.0] - 2023-07-22
2
51
 
3
52
  - Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mongodb_meilisearch (1.1.0)
4
+ mongodb_meilisearch (1.2.1)
5
5
  meilisearch
6
6
  mongoid (~> 7.0)
7
7
  rails
data/README.md CHANGED
@@ -169,16 +169,19 @@ unless you intend to allow users to sort by when a record was created,
169
169
  there's no point in recording it's `created_at` in the search index.
170
170
  It'll just waste bandwidth, memory, and disk space.
171
171
 
172
- Define a `SEARCHABLE_ATTRIBUTES` constant with an array of strings to limit things.
173
- By default these will _also_ be the fields you can filter on. Note that
174
- Meilisearch requires there to be an `id` field and it must be a string.
172
+ Define a `SEARCHABLE_ATTRIBUTES` constant with an array of strings to limit things.
173
+ These are the field names, and/or names of methods you wish to have indexed.
174
+
175
+ By default these will _also_ be the fields you can filter on.
176
+
177
+ Note that Meilisearch requires there to be an `id` field and it must be a string.
175
178
  If you don't define one it will use string version of the `_id` your
176
179
  document's `BSON::ObjectId`.
177
180
 
178
181
  ```ruby
179
182
  # explicitly define the fields you want to be searchable
180
183
  # this should be an array of symbols
181
- SEARCHABLE_ATTRIBUTES = %i[title body]
184
+ SEARCHABLE_ATTRIBUTES = %w[title body]
182
185
  # OR explicitly define the fields you DON'T want searchable
183
186
  SEARCHABLE_ATTRIBUTES = searchable_attributes - [:created_at]
184
187
  ```
@@ -194,9 +197,16 @@ exclude any fields that are `Mongoid::Fields::ForeignKey` objects.
194
197
 
195
198
  #### Getting Extra Specific
196
199
  If your searchable data needs to by dynamically generated instead of
197
- just taken directly from the `Mongoid::Document`'s attributes you can
198
- define a `search_indexable_hash` method on your class. This method
199
- must return a hash, and that hash must include the following keys:
200
+ just taken directly from the `Mongoid::Document`'s attributes or
201
+ existing methods you can define a `search_indexable_hash` method on your class.
202
+
203
+ Before you do, please note that as of v1.1 your `SEARCHABLE_ATTRIBUTES`
204
+ constant can contain fields and method names in its array of values. Making
205
+ a method for each thing dynamically generated thing you want in the search
206
+ and then including it in SEARCHABLE_ATTRIBUTES is going to be
207
+ the easiest way of accomplishing this.
208
+
209
+ Your `search_indexable_hash` must return a hash, and that hash must include the following keys:
200
210
  - `"id"` - a string that uniquely identifies the record
201
211
  - `"object_class"` the name of the class that this record corresponds to.
202
212
 
@@ -323,6 +333,16 @@ Be careful to not add documents that are already in the index.
323
333
  WARNING: if you think you should use this, you're probably
324
334
  mistaken.
325
335
 
336
+ #### Indexes
337
+ By default every model gets its own search index. This means that
338
+ `Foo.search("some text")` will only search `Foo` objects. To have a
339
+ search cross objects you'll need to use a "Shared Index" (see below).
340
+
341
+
342
+ The name of the index isn't important when not using shared indexes.
343
+ By default a model's index is the snake cased form of the class name.
344
+ For example, data for `MyWidget` models will be stored in the `my_widget` index.
345
+
326
346
  #### Shared indexes
327
347
  Imagine you have a `Note` and a `Comment` model, sharing an index so that
328
348
  you can perform a single search and have search results for both models
@@ -442,10 +462,14 @@ To invoke any of Meilisearch's custom search options (see [their documentation](
442
462
 
443
463
  `MyModel.search("search term", options: <my custom options>)`
444
464
 
445
- The Meilisearch-ruby gem should be able to convert keys from snake case to
465
+ Currently the Meilisearch-ruby gem can convert keys from snake case to
446
466
  camel case. For example `hits_per_page` will become `hitsPerPage`.
447
- Meilisearch ultimately wants camel case. Follow their documentation
448
- to see what's available and what type of options to pass it. Note that your
467
+ Meilisearch ultimately wants camel case (`camelCase`) parameter keys,
468
+ _but_ `meilisearch-ruby` wants snake case (`snake_case`).
469
+
470
+ Follow Meilisearch's documentation
471
+ to see what's available and what type of options to pass it, but convert
472
+ them to snake case first. Note that your
449
473
  options keys and values must all be simple JSON values.
450
474
 
451
475
  If for some reason that still isn't enough, you can work with the
@@ -5,5 +5,5 @@ module MongodbMeilisearch
5
5
  # @note This library will adhere to strict semantic versioning.
6
6
  # See https://semver.org/
7
7
  #
8
- VERSION = "1.1.0"
8
+ VERSION = "1.2.1"
9
9
  end
@@ -109,6 +109,7 @@ module Search
109
109
  # @param filtered_by_class [Boolean] - defaults to filtering results by the class
110
110
  # your searching from. Ex. Foo.search("something") it will
111
111
  # have Meilisearch filter on records where `object_class` == `"Foo"`
112
+ # This simplifies working with shared indexes.
112
113
  # @return [Hash[String, [Array[String | Document]] a hash with keys corresponding
113
114
  # to the classes of objects returned, and a value of an array of ids or
114
115
  # Mongoid::Document objects sorted by match strength. It will ALSO have a key named
@@ -135,13 +136,13 @@ module Search
135
136
 
136
137
  # TODO: break this if/else out into a separate function
137
138
  if ids_only
138
- options.merge!({attributesToRetrieve: [pk.to_s]})
139
+ options.merge!({attributes_to_retrieve: [pk.to_s]})
139
140
  else
140
141
  # Don't care what you add, but we need the primary key and object_class
141
- options[:attributesToRetrieve] = [] unless options.has_key?(:attributesToRetrieve)
142
+ options[:attributes_to_retrieve] = [] unless options.has_key?(:attributes_to_retrieve)
142
143
  [pk.to_s, "object_class"].each do |attr|
143
- unless options[:attributesToRetrieve].include?(attr)
144
- options[:attributesToRetrieve] << attr
144
+ unless options[:attributes_to_retrieve].include?(attr)
145
+ options[:attributes_to_retrieve] << attr
145
146
  end
146
147
  end
147
148
  end
@@ -260,6 +261,7 @@ module Search
260
261
  # - each document hash is presumed to have been created by way of
261
262
  # search_indexable_hash
262
263
  def add_documents(new_documents, async: true)
264
+ configure_attributes_and_index_if_needed!
263
265
  if async
264
266
  search_index.add_documents(new_documents, primary_search_key)
265
267
  else
@@ -393,17 +395,38 @@ module Search
393
395
  # @return [Array] - an array of attributes configured as sortable
394
396
  # in the index.
395
397
  def meilisearch_sortable_attributes
396
- # Search::Client.instance.http_get(
397
- search_index.http_get(
398
- "/indexes/#{search_index}/settings/sortable-attributes"
399
- )
398
+ @_meilisearch_sortable_attributes ||= search_index.get_sortable_attributes
400
399
  end
401
400
 
402
401
  def meilisearch_filterable_attributes
403
- # Search::Client.instance.http_get(
404
- search_index.http_get(
405
- "/indexes/#{search_index}/settings/filterable-attributes"
406
- )
402
+ @_meilisearch_filterable_attributes ||= search_index.get_filterable_attributes
403
+ end
404
+
405
+ def reset_cached_data!
406
+ @_meilisearch_filterable_attributes = nil
407
+ @_filterable_attributes = nil
408
+ @_meilisearch_sortable_attributes = nil
409
+ @_sortable_attributes = nil
410
+ end
411
+
412
+ # Guarantees that the filterable attributes have been configured
413
+ # This should be called before
414
+ def configure_attributes_and_index_if_needed!
415
+ return if unfilterable?
416
+ indexes_filterable_attributes = []
417
+
418
+ begin
419
+ indexes_filterable_attributes = meilisearch_filterable_attributes
420
+ rescue ::MeiliSearch::ApiError => e
421
+ # this is expected to happen the first time an instance
422
+ # of a new model is saved.
423
+ raise unless e.message.match?(/Index `\S+` not found\./)
424
+ Search::Client.instance.create_index(search_index_name)
425
+ end
426
+
427
+ return if indexes_filterable_attributes.include?("object_class")
428
+ set_filterable_attributes
429
+ set_sortable_attributes
407
430
  end
408
431
 
409
432
  # Updates the filterable attributes in the search index.
@@ -579,7 +602,7 @@ module Search
579
602
  def reindex_core(async: true)
580
603
  # no point in continuing if this fails...
581
604
  delete_all_documents!
582
-
605
+ reset_cached_data!
583
606
  # this conveniently lines up with the batch size of 100
584
607
  # that Mongoid gives us
585
608
  documents = []
data/lib/search/client.rb CHANGED
@@ -30,7 +30,7 @@ module Search
30
30
  if @client.respond_to? m.to_sym
31
31
  @client.send(m, *args, &block)
32
32
  else
33
- raise ArgumentError.new("Method `#{m}` doesn't exist.")
33
+ raise ArgumentError.new("Method `#{m}` doesn't exist in #{@client.inspect}.")
34
34
  end
35
35
  end
36
36
 
@@ -2,6 +2,7 @@ module Search
2
2
  module InstanceMethods
3
3
  # Adds this record to the search index asynchronously
4
4
  def add_to_search
5
+ self.class.configure_attributes_and_index_if_needed!
5
6
  search_index.add_documents(
6
7
  [search_indexable_hash],
7
8
  primary_search_key.to_s
@@ -10,6 +11,7 @@ module Search
10
11
 
11
12
  # Adds this record to the search index synchronously
12
13
  def add_to_search!
14
+ self.class.configure_attributes_and_index_if_needed!
13
15
  index = search_index
14
16
  documents = [search_indexable_hash]
15
17
  pk = primary_search_key.to_s
@@ -42,6 +44,10 @@ module Search
42
44
  search_index.delete_document!(send(primary_search_key).to_s)
43
45
  end
44
46
 
47
+ def searchable_attributes
48
+ self.class.searchable_attributes
49
+ end
50
+
45
51
  # returns a hash of all the attributes
46
52
  # if searchable_attributes method is defined
47
53
  # it is assumed to return a list of symbols
@@ -55,9 +61,10 @@ module Search
55
61
  # in returned results
56
62
  def search_indexable_hash
57
63
  klass = self.class
58
- hash = attributes
59
- .to_h
60
- .slice(* klass.searchable_attributes.map { |a| a.to_s })
64
+ # the to_s & to_sym is just safety in case someone
65
+ # defined searchable_attributes as an array of strings
66
+ hash = {}
67
+ searchable_attributes.each { |a| hash[a.to_s] = send(a.to_sym) }
61
68
 
62
69
  # Meilisearch doesn't like a primary key of _id
63
70
  # but Mongoid ids are _id
@@ -67,7 +74,7 @@ module Search
67
74
  id = hash.delete("_id").to_s
68
75
  new_id = (!klass.has_class_prefixed_search_ids?) ? id : "#{self.class.name}_#{id}"
69
76
  hash["id"] = new_id
70
- elsif hash.has_key?("id") && !hash[id].is_a?(String)
77
+ elsif hash.has_key?("id") && !hash["id"].is_a?(String)
71
78
  # this is mostly in case it's a BSON::ObjectId
72
79
  hash["id"] = hash["id"].to_s
73
80
  elsif !hash.has_key?("id")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongodb_meilisearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - masukomi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-16 00:00:00.000000000 Z
11
+ date: 2024-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -187,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
187
187
  - !ruby/object:Gem::Version
188
188
  version: '0'
189
189
  requirements: []
190
- rubygems_version: 3.4.17
190
+ rubygems_version: 3.4.20
191
191
  signing_key:
192
192
  specification_version: 4
193
193
  summary: MeiliSearch integration for MongoDB