search-engine-for-typesense 30.1.0 → 30.1.2

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/search_engine/search_engine/index_partition_job.rb +7 -26
  4. data/lib/generators/search_engine/install/install_generator.rb +1 -1
  5. data/lib/generators/search_engine/install/templates/initializer.rb.tt +11 -11
  6. data/lib/generators/search_engine/model/model_generator.rb +2 -2
  7. data/lib/generators/search_engine/model/templates/model.rb.tt +2 -2
  8. data/lib/search_engine/admin/stopwords.rb +1 -1
  9. data/lib/search_engine/admin/synonyms.rb +1 -1
  10. data/lib/search_engine/ast/node.rb +1 -1
  11. data/lib/search_engine/ast.rb +1 -1
  12. data/lib/search_engine/base/creation.rb +4 -4
  13. data/lib/search_engine/base/deletion.rb +2 -0
  14. data/lib/search_engine/base/model_dsl.rb +2 -0
  15. data/lib/search_engine/base/relation_delegation.rb +1 -1
  16. data/lib/search_engine/cli/doctor.rb +6 -6
  17. data/lib/search_engine/client/request_builder.rb +2 -2
  18. data/lib/search_engine/client.rb +19 -19
  19. data/lib/search_engine/collection_resolver.rb +7 -2
  20. data/lib/search_engine/config/presets.rb +7 -7
  21. data/lib/search_engine/config.rb +5 -5
  22. data/lib/search_engine/console_helpers.rb +4 -4
  23. data/lib/search_engine/dispatcher.rb +2 -2
  24. data/lib/search_engine/dsl/parser.rb +9 -9
  25. data/lib/search_engine/errors.rb +6 -6
  26. data/lib/search_engine/filters/sanitizer.rb +24 -44
  27. data/lib/search_engine/hydration/materializers.rb +13 -7
  28. data/lib/search_engine/indexer/batch_planner.rb +3 -8
  29. data/lib/search_engine/indexer/import_dispatcher.rb +1 -1
  30. data/lib/search_engine/indexer/retry_policy.rb +9 -6
  31. data/lib/search_engine/indexer.rb +3 -176
  32. data/lib/search_engine/instrumentation.rb +1 -1
  33. data/lib/search_engine/joins/guard.rb +4 -4
  34. data/lib/search_engine/joins/resolver.rb +2 -2
  35. data/lib/search_engine/logging_subscriber.rb +4 -4
  36. data/lib/search_engine/mapper.rb +13 -21
  37. data/lib/search_engine/multi.rb +2 -2
  38. data/lib/search_engine/multi_result.rb +1 -1
  39. data/lib/search_engine/notifications/compact_logger.rb +3 -3
  40. data/lib/search_engine/otel.rb +5 -5
  41. data/lib/search_engine/partitioner.rb +5 -5
  42. data/lib/search_engine/ranking_plan.rb +4 -4
  43. data/lib/search_engine/relation/compiler.rb +11 -6
  44. data/lib/search_engine/relation/deletion.rb +2 -0
  45. data/lib/search_engine/relation/dsl/filters.rb +9 -9
  46. data/lib/search_engine/relation/dsl/selection.rb +3 -3
  47. data/lib/search_engine/relation/dsl.rb +17 -17
  48. data/lib/search_engine/relation/dx.rb +4 -4
  49. data/lib/search_engine/relation/materializers.rb +1 -1
  50. data/lib/search_engine/relation/options.rb +1 -1
  51. data/lib/search_engine/relation.rb +1 -1
  52. data/lib/search_engine/result.rb +1 -1
  53. data/lib/search_engine/schema.rb +4 -4
  54. data/lib/search_engine/sources/active_record_source.rb +0 -1
  55. data/lib/search_engine/sources/lambda_source.rb +1 -1
  56. data/lib/search_engine/sources/sql_source.rb +1 -1
  57. data/lib/search_engine/sources.rb +3 -3
  58. data/lib/search_engine/test/stub_client.rb +8 -8
  59. data/lib/search_engine/test.rb +2 -2
  60. data/lib/search_engine/version.rb +1 -1
  61. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edc1b2db8a930d829b052ce9e414921cbd78c333111596c754fabe9ce443e26c
4
- data.tar.gz: bcd25036e9eb8202bc813decea0c7c8904f9b7565e5cfb254a2d7fb5760dd473
3
+ metadata.gz: 724f7861a8f221fce45175c0fae05471107eb97a3cd95711357e70ffd59d32bc
4
+ data.tar.gz: 0a89defd92643f410d13f3e62a694fb0ae976cb6c81e2ab05e2c010f29a134e4
5
5
  SHA512:
6
- metadata.gz: 275bdb5de35a2c8fb31f12a187231713756f5bf381b32d1bf255d6830b0feadc521b8f1aacdb2f2069b519e3ce5f6e5b793bb1a6ee9b79ceaded017afce4259e
7
- data.tar.gz: 3401b22b7440aaa90040004128cc5dea5ff76355505471f1752d9b91909acdbff120842904295fd014a349d3f785eb18c774f8e94583cd4887b651078fac6143
6
+ metadata.gz: 28e5692bbf6947e1ba6b9cac829b05e59d20d0bc5423e7c99f3e4dcd18910b86392fc6b66f72d7af974898c3a3366eb86aa130f87a9283d7313862fd2f201b46
7
+ data.tar.gz: 42284e73cb4d0017d286bcf887909edd993522ad7e38b548d7bdcfc8db57d5e13256a8aaea6e80468994e9261177fc80f215174409543c19b9ac29ca4748f332
data/README.md CHANGED
@@ -45,7 +45,7 @@ end
45
45
  SearchEngine::Product.where(name: "milk").select(:id, :name).limit(5).to_a
46
46
  ```
47
47
 
48
- See [Quickstart](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/quickstart).
48
+ See [Quickstart](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/quickstart).
49
49
 
50
50
  ### Host app SearchEngine models
51
51
 
@@ -123,7 +123,7 @@ SearchEngine::Product.upsert_bulk(data: [mapped])
123
123
 
124
124
  ## Documentation
125
125
 
126
- See the [Docs](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/index)
126
+ See the [Docs](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/index)
127
127
 
128
128
  ## Test/offline mode
129
129
 
@@ -139,11 +139,11 @@ If you set `SearchEngine.configure { |c| c.client = ... }`, the custom client is
139
139
 
140
140
  ## Example app
141
141
 
142
- See `examples/demo_shop` — demonstrates single/multi search, JOINs, grouping, presets/curation, and DX/observability. Supports offline mode via the stub client (see [Testing](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing)).
142
+ See `examples/demo_shop` — demonstrates single/multi search, JOINs, grouping, presets/curation, and DX/observability. Supports offline mode via the stub client (see [Testing](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing)).
143
143
 
144
144
  ## Contributing
145
145
 
146
- See [Docs Style Guide](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/docs-style-guide). Follow YARDoc for public APIs, add backlinks on docs landing pages, and redact secrets in examples.
146
+ See [Docs Style Guide](https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/docs-style-guide). Follow YARDoc for public APIs, add backlinks on docs landing pages, and redact secrets in examples.
147
147
 
148
148
  <!-- Badge references (placeholders) -->
149
149
  [ci-badge]: https://img.shields.io/github/actions/workflow/status/lstpsche/search-engine-for-typesense/ci.yml?branch=main
@@ -151,4 +151,4 @@ See [Docs Style Guide](https://nikita-shkoda.mintlify.app/projects/search-engine
151
151
  [gem-badge]: https://badge.fury.io/rb/search-engine-for-typesense.svg?icon=si%3Arubygems
152
152
  [gem-url]: https://rubygems.org/gems/search-engine-for-typesense
153
153
  [docs-badge]: https://img.shields.io/badge/docs-index-blue
154
- [docs-url]: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/index
154
+ [docs-url]: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/index
@@ -18,7 +18,7 @@ module SearchEngine
18
18
  rescue_from(SearchEngine::Errors::Timeout) { |error| retry_if_possible(error) }
19
19
  rescue_from(SearchEngine::Errors::Connection) { |error| retry_if_possible(error) }
20
20
  rescue_from(SearchEngine::Errors::Api) do |error|
21
- if transient_status?(error.status.to_i)
21
+ if SearchEngine::Indexer::RetryPolicy.transient_status?(error.status.to_i)
22
22
  retry_if_possible(error)
23
23
  else
24
24
  instrument_error(error)
@@ -82,14 +82,14 @@ module SearchEngine
82
82
  end
83
83
 
84
84
  def retry_if_possible(error)
85
- attempts, base, max, jitter = retry_settings
86
- attempt_no = executions.to_i # number of times we've run so far (1-based)
87
- if attempt_no >= attempts
85
+ policy = retry_policy
86
+ attempt_no = executions.to_i
87
+ if attempt_no >= policy.attempts
88
88
  instrument_error(error)
89
89
  raise
90
90
  end
91
91
 
92
- wait_seconds = backoff_seconds(attempt_no + 1, base: base, max: max, jitter_fraction: jitter)
92
+ wait_seconds = policy.next_delay(attempt_no + 1, error)
93
93
  instrument(
94
94
  'search_engine.dispatcher.job_error',
95
95
  error_payload(error).merge(queue: queue_name, job_id: job_id, retry_after_s: wait_seconds)
@@ -124,28 +124,9 @@ module SearchEngine
124
124
  SearchEngine::Instrumentation.instrument(event, payload) {}
125
125
  end
126
126
 
127
- def retry_settings
127
+ def retry_policy
128
128
  cfg = SearchEngine.config.indexer
129
- attempts = cfg&.retries && cfg.retries[:attempts].to_i.positive? ? cfg.retries[:attempts].to_i : 3
130
- base = cfg&.retries && cfg.retries[:base].to_f.positive? ? cfg.retries[:base].to_f : 0.5
131
- max = cfg&.retries && cfg.retries[:max].to_f.positive? ? cfg.retries[:max].to_f : 5.0
132
- jitter = cfg&.retries && cfg.retries[:jitter_fraction].to_f >= 0 ? cfg.retries[:jitter_fraction].to_f : 0.2
133
- [attempts, base, max, jitter]
134
- end
135
-
136
- def backoff_seconds(attempt, base:, max:, jitter_fraction:)
137
- exp = [base * (2 ** (attempt - 1)), max].min
138
- jitter = exp * jitter_fraction
139
- delta = rand(-jitter..jitter)
140
- sleep_time = exp + delta
141
- sleep_time.positive? ? sleep_time : 0.0
142
- end
143
-
144
- def transient_status?(code)
145
- return true if code == 429
146
- return true if code >= 500 && code <= 599
147
-
148
- false
129
+ SearchEngine::Indexer::RetryPolicy.from_config(cfg&.retries)
149
130
  end
150
131
 
151
132
  def monotonic_ms
@@ -8,7 +8,7 @@ module SearchEngine
8
8
  #
9
9
  # @example
10
10
  # rails g search_engine:install
11
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
11
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
12
12
  class InstallGenerator < Rails::Generators::Base
13
13
  source_root File.expand_path('templates', __dir__)
14
14
 
@@ -11,10 +11,10 @@
11
11
  # - Explore queries in `rails console` using SE helpers: `SE.q("milk")`
12
12
  #
13
13
  # Docs:
14
- # - Quickstart: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/quickstart
15
- # - CLI (Doctor): https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/cli
16
- # - DX helpers: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
17
- # - Configuration: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/configuration
14
+ # - Quickstart: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/quickstart
15
+ # - CLI (Doctor): https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/cli
16
+ # - DX helpers: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
17
+ # - Configuration: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/configuration
18
18
  SearchEngine.configure do |c|
19
19
  c.host = ENV.fetch('TYPESENSE_HOST', 'localhost')
20
20
  c.port = Integer(ENV.fetch('TYPESENSE_PORT', 8108))
@@ -83,7 +83,7 @@ SearchEngine.configure do |c|
83
83
  # c.logger = Rails.logger
84
84
 
85
85
  # --- Presets -------------------------------------------------------------
86
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/presets
86
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/presets
87
87
 
88
88
  # Enable preset namespacing and resolution. Default: true
89
89
  # c.presets.enabled = true
@@ -95,7 +95,7 @@ SearchEngine.configure do |c|
95
95
  # c.presets.locked_domains = %i[filter_by sort_by include_fields exclude_fields]
96
96
 
97
97
  # --- Selection & Grouping ------------------------------------------------
98
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection
98
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection
99
99
 
100
100
  # Raise when requested fields are missing during hydration. Default: false
101
101
  # c.selection.strict_missing = false
@@ -104,7 +104,7 @@ SearchEngine.configure do |c|
104
104
  # c.grouping.warn_on_ambiguous = true
105
105
 
106
106
  # --- Observability & Logging --------------------------------------------
107
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/observability
107
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/observability
108
108
 
109
109
  # Quiet by default: structured instrumentation logs ([se.*]) are OFF unless
110
110
  # explicitly enabled below.
@@ -129,13 +129,13 @@ SearchEngine.configure do |c|
129
129
  # c.opentelemetry = { enabled: false, service_name: 'search_engine' }
130
130
 
131
131
  # --- Schema lifecycle ----------------------------------------------------
132
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema
132
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema
133
133
 
134
134
  # Retention: how many previous physical collections to keep after swap. Default: 0
135
135
  # c.schema.retention.keep_last = 0
136
136
 
137
137
  # --- Indexer -------------------------------------------------------------
138
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer
138
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer
139
139
 
140
140
  # Default batch size for imports when not provided. Default: 2000
141
141
  # c.indexer.batch_size = 2000
@@ -202,7 +202,7 @@ SearchEngine.configure do |c|
202
202
  # c.partitioning.max_error_samples = 5
203
203
 
204
204
  # --- Stale deletes -------------------------------------------------------
205
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/deletion
205
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/deletion
206
206
 
207
207
  # Global kill switch for stale delete workflows. Default: true
208
208
  # c.stale_deletes.enabled = true
@@ -217,7 +217,7 @@ SearchEngine.configure do |c|
217
217
  # c.stale_deletes.estimation_enabled = false
218
218
 
219
219
  # --- Curation ------------------------------------------------------------
220
- # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/curation
220
+ # See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/curation
221
221
 
222
222
  # Maximum number of pinned IDs allowed. Default: 50
223
223
  # c.curation.max_pins = 50
@@ -16,7 +16,7 @@ module SearchEngine
16
16
  #
17
17
  # @example
18
18
  # rails g search_engine:model Product --collection products --attrs id:integer name:string
19
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
19
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
20
20
  class ModelGenerator < Rails::Generators::NamedBase
21
21
  source_root File.expand_path('templates', __dir__)
22
22
 
@@ -31,7 +31,7 @@ module SearchEngine
31
31
 
32
32
  raise Thor::Error,
33
33
  '--collection is required. See ' \
34
- 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx#generators--console-helpers'
34
+ 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx#generators--console-helpers'
35
35
  end
36
36
 
37
37
  def create_model
@@ -2,8 +2,8 @@
2
2
 
3
3
  # Minimal SearchEngine model generated by `search_engine:model`.
4
4
  # Docs:
5
- # - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/quickstart
6
- # - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection#guardrails--errors
5
+ # - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/quickstart
6
+ # - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection#guardrails--errors
7
7
  class SearchEngine::<%= class_name %> < SearchEngine::Base
8
8
  collection "<%= @collection_name %>"
9
9
  <% Array(@attributes).each do |(name, type)| -%>
@@ -5,7 +5,7 @@ module SearchEngine
5
5
  # Manage stopword sets for a collection.
6
6
  #
7
7
  # @since 0.1.0
8
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords#management
8
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords#management
9
9
  # @see `https://typesense.org/docs/latest/api/stopwords.html`
10
10
  module Stopwords
11
11
  class << self
@@ -5,7 +5,7 @@ module SearchEngine
5
5
  # Manage synonym sets for a collection.
6
6
  #
7
7
  # @since 0.1.0
8
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords#management
8
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords#management
9
9
  # @see `https://typesense.org/docs/latest/api/synonyms.html`
10
10
  module Synonyms
11
11
  class << self
@@ -8,7 +8,7 @@ module SearchEngine
8
8
  # #children and #each_child, and compact debug output.
9
9
  # Subclasses must implement #type and override readers as needed.
10
10
  #
11
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl`
11
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl`
12
12
  class Node
13
13
  # Maximum preview length for inspect payloads
14
14
  INSPECT_PREVIEW_LIMIT = 80
@@ -23,7 +23,7 @@ module SearchEngine
23
23
  #
24
24
  # Exposes ergonomic builders as module functions, returning immutable nodes.
25
25
  #
26
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl`
26
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl`
27
27
  module AST
28
28
  module_function
29
29
 
@@ -234,7 +234,7 @@ module SearchEngine
234
234
  elsif !valid
235
235
  raise SearchEngine::Errors::InvalidParams.new(
236
236
  err,
237
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer#troubleshooting',
237
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer#troubleshooting',
238
238
  details: { field: key.to_s, expected: expected, got: value.class.name }
239
239
  )
240
240
  end
@@ -338,7 +338,7 @@ module SearchEngine
338
338
  msg = "Missing required fields: #{missing.to_a.sort.inspect} for #{klass.name}."
339
339
  raise SearchEngine::Errors::InvalidParams.new(
340
340
  msg,
341
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer#troubleshooting',
341
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer#troubleshooting',
342
342
  details: { missing_required: missing.to_a.sort }
343
343
  )
344
344
  end
@@ -352,7 +352,7 @@ module SearchEngine
352
352
  ].join(' ')
353
353
  raise SearchEngine::Errors::InvalidField.new(
354
354
  msg,
355
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer#troubleshooting',
355
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer#troubleshooting',
356
356
  details: { extras: extras.to_a.sort }
357
357
  )
358
358
  end
@@ -523,7 +523,7 @@ module SearchEngine
523
523
  msg = "#{msg} (e.g., #{sample})" if sample
524
524
  raise SearchEngine::Errors::InvalidParams.new(
525
525
  msg,
526
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer#troubleshooting',
526
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer#troubleshooting',
527
527
  details: result
528
528
  )
529
529
  end
@@ -57,6 +57,8 @@ module SearchEngine
57
57
  resp.nil? ? 0 : 1
58
58
  end
59
59
 
60
+ alias_method :destroy, :delete
61
+
60
62
  private
61
63
 
62
64
  # Determine the effective document id for deletion, preferring hydrated
@@ -52,6 +52,8 @@ module SearchEngine
52
52
  timeout_ms: timeout_ms
53
53
  )
54
54
  end
55
+
56
+ alias_method :destroy_by, :delete_by
55
57
  end
56
58
 
57
59
  class_methods do
@@ -25,7 +25,7 @@ module SearchEngine
25
25
  select include_fields exclude reselect
26
26
  limit_hits validate_hits!
27
27
  first last take pluck pick exists? count find_by all!
28
- delete_all update_all
28
+ delete_all destroy_all update_all
29
29
  raw
30
30
  ].each { |method| delegate method, to: :all }
31
31
 
@@ -12,9 +12,9 @@ module SearchEngine
12
12
  # Supports FORMAT env var (table/json) and redaction-aware details.
13
13
  #
14
14
  # @since M8
15
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/cli#doctor
15
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/cli#doctor
16
16
  module Doctor
17
- DOCS_BASE = 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/'
17
+ DOCS_BASE = 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/'
18
18
 
19
19
  module_function
20
20
 
@@ -30,7 +30,7 @@ module SearchEngine
30
30
  # Returns exit code (0 success, 1 failure).
31
31
  # @return [Integer]
32
32
  # @since M8
33
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/cli#doctor
33
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/cli#doctor
34
34
  def run
35
35
  puts 'Executing doctor checks...'
36
36
 
@@ -136,7 +136,7 @@ module SearchEngine
136
136
  ok = missing.empty?
137
137
  hint = unless ok
138
138
  'Set TYPESENSE_* envs or configure in an initializer. ' \
139
- 'See https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/installation#configuration'
139
+ 'See https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/installation#configuration'
140
140
  end
141
141
 
142
142
  details = cfg.to_h_redacted
@@ -158,7 +158,7 @@ module SearchEngine
158
158
  started,
159
159
  error,
160
160
  hint: 'Unexpected error reading configuration',
161
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/configuration'
161
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/configuration'
162
162
  )
163
163
  end
164
164
 
@@ -275,7 +275,7 @@ module SearchEngine
275
275
  nil
276
276
  else
277
277
  'Run schema lifecycle to create physical collections and swap alias. ' \
278
- 'See https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#lifecycle'
278
+ 'See https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#lifecycle'
279
279
  end
280
280
  details = { resolved: resolved, missing: missing }
281
281
  Builder.result_for_check(
@@ -10,7 +10,7 @@ module SearchEngine
10
10
  # Sanitizes internal-only keys before encoding to ensure the payload is
11
11
  # compatible with the Typesense API.
12
12
  #
13
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/client`
13
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/client`
14
14
  # @see `https://typesense.org/docs/latest/api/`
15
15
  class RequestBuilder
16
16
  # Normalized request container used by the transport adapter.
@@ -39,7 +39,7 @@ module SearchEngine
39
39
  DEFAULT_HEADERS_JSON = { 'Content-Type' => CONTENT_TYPE_JSON }.freeze
40
40
 
41
41
  # Centralized doc links used by client error mapping
42
- DOC_CLIENT_ERRORS = 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/client#errors'
42
+ DOC_CLIENT_ERRORS = 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/client#errors'
43
43
 
44
44
  INTERNAL_ONLY_KEYS = %i[
45
45
  _join
@@ -31,7 +31,7 @@ module SearchEngine
31
31
  # @param url_opts [Hash] URL/common knobs (use_cache, cache_ttl)
32
32
  # @return [SearchEngine::Result] Wrapped response with hydrated hits
33
33
  # @raise [SearchEngine::Errors::InvalidParams, SearchEngine::Errors::*]
34
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/client`
34
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/client`
35
35
  # @see `https://typesense.org/docs/latest/api/documents.html#search-document`
36
36
  def search(collection:, params:, url_opts: {})
37
37
  services.fetch(:search).call(collection: collection, params: params, url_opts: url_opts)
@@ -43,7 +43,7 @@ module SearchEngine
43
43
  # @param timeout_ms [Integer, nil] optional read-timeout override in ms
44
44
  # @return [String, nil] physical collection name when alias exists; nil when alias not found
45
45
  # @raise [SearchEngine::Errors::*] on network or API errors other than 404
46
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema`
46
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema`
47
47
  # @see `https://typesense.org/docs/latest/api/aliases.html`
48
48
  def resolve_alias(logical_name, timeout_ms: nil)
49
49
  services.fetch(:collections).resolve_alias(logical_name, timeout_ms: timeout_ms)
@@ -55,7 +55,7 @@ module SearchEngine
55
55
  # @param timeout_ms [Integer, nil] optional read-timeout override in ms
56
56
  # @return [Hash, nil] schema hash when found; nil when collection not found (404)
57
57
  # @raise [SearchEngine::Errors::*] on other network or API errors
58
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema`
58
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema`
59
59
  # @see `https://typesense.org/docs/latest/api/collections.html`
60
60
  def retrieve_collection_schema(collection_name, timeout_ms: nil)
61
61
  services.fetch(:collections).retrieve_schema(collection_name, timeout_ms: timeout_ms)
@@ -65,7 +65,7 @@ module SearchEngine
65
65
  # @param alias_name [String]
66
66
  # @param physical_name [String]
67
67
  # @return [Hash]
68
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#lifecycle`
68
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#lifecycle`
69
69
  # @see `https://typesense.org/docs/latest/api/aliases.html#upsert-an-alias`
70
70
  def upsert_alias(alias_name, physical_name)
71
71
  services.fetch(:collections).upsert_alias(alias_name, physical_name)
@@ -74,7 +74,7 @@ module SearchEngine
74
74
  # Create a new physical collection with the given schema.
75
75
  # @param schema [Hash] Typesense schema body
76
76
  # @return [Hash] created collection schema
77
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#lifecycle`
77
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#lifecycle`
78
78
  # @see `https://typesense.org/docs/latest/api/collections.html#create-a-collection`
79
79
  def create_collection(schema)
80
80
  services.fetch(:collections).create(schema)
@@ -95,7 +95,7 @@ module SearchEngine
95
95
  # default suitable for destructive operations is used (prefers indexer timeout, with
96
96
  # a minimum of 30s).
97
97
  # @return [Hash] Typesense delete response
98
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#lifecycle`
98
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#lifecycle`
99
99
  # @see `https://typesense.org/docs/latest/api/collections.html#delete-a-collection`
100
100
  def delete_collection(name, timeout_ms: nil)
101
101
  effective_timeout_ms = begin
@@ -120,7 +120,7 @@ module SearchEngine
120
120
  # List all collections.
121
121
  # @param timeout_ms [Integer, nil] optional read-timeout override in ms
122
122
  # @return [Array<Hash>] list of collection metadata
123
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema`
123
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema`
124
124
  # @see `https://typesense.org/docs/latest/api/collections.html#list-all-collections`
125
125
  def list_collections(timeout_ms: nil)
126
126
  services.fetch(:collections).list(timeout_ms: timeout_ms)
@@ -128,7 +128,7 @@ module SearchEngine
128
128
 
129
129
  # Perform a server health check.
130
130
  # @return [Hash] Typesense health response (symbolized where applicable)
131
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/troubleshooting`
131
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/troubleshooting`
132
132
  # @see `https://typesense.org/docs/latest/api/cluster-operations.html#health`
133
133
  def health
134
134
  services.fetch(:operations).health
@@ -173,7 +173,7 @@ module SearchEngine
173
173
  # @param id [String]
174
174
  # @param terms [Array<String>]
175
175
  # @return [Hash]
176
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
176
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
177
177
  # @see `https://typesense.org/docs/latest/api/synonyms.html#upsert-a-synonym`
178
178
  def synonyms_upsert(collection:, id:, terms:)
179
179
  set = synonym_set_name_for_collection(collection)
@@ -186,7 +186,7 @@ module SearchEngine
186
186
  end
187
187
 
188
188
  # @return [Array<Hash>]
189
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
189
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
190
190
  # @see `https://typesense.org/docs/latest/api/synonyms.html#list-all-synonyms-in-a-synonym-set`
191
191
  def synonyms_list(collection:)
192
192
  set = synonym_set_name_for_collection(collection)
@@ -198,7 +198,7 @@ module SearchEngine
198
198
  end
199
199
 
200
200
  # @return [Hash, nil]
201
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
201
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
202
202
  # @see `https://typesense.org/docs/latest/api/synonyms.html#retrieve-a-synonym`
203
203
  def synonyms_get(collection:, id:)
204
204
  set = synonym_set_name_for_collection(collection)
@@ -211,7 +211,7 @@ module SearchEngine
211
211
  end
212
212
 
213
213
  # @return [Hash]
214
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
214
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
215
215
  # @see `https://typesense.org/docs/latest/api/synonyms.html#delete-a-synonym`
216
216
  def synonyms_delete(collection:, id:)
217
217
  set = synonym_set_name_for_collection(collection)
@@ -228,7 +228,7 @@ module SearchEngine
228
228
  # @param id [String]
229
229
  # @param terms [Array<String>]
230
230
  # @return [Hash]
231
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
231
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
232
232
  # @see `https://typesense.org/docs/latest/api/stopwords.html#upsert-a-stopwords`
233
233
  def stopwords_upsert(collection:, id:, terms:)
234
234
  admin_resource_request(
@@ -241,7 +241,7 @@ module SearchEngine
241
241
  end
242
242
 
243
243
  # @return [Array<Hash>]
244
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
244
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
245
245
  # @see `https://typesense.org/docs/latest/api/stopwords.html#list-all-stopwords-of-a-collection`
246
246
  def stopwords_list(collection:)
247
247
  admin_resource_request(
@@ -252,7 +252,7 @@ module SearchEngine
252
252
  end
253
253
 
254
254
  # @return [Hash, nil]
255
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
255
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
256
256
  # @see `https://typesense.org/docs/latest/api/stopwords.html#retrieve-a-stopword`
257
257
  def stopwords_get(collection:, id:)
258
258
  admin_resource_request(
@@ -265,7 +265,7 @@ module SearchEngine
265
265
  end
266
266
 
267
267
  # @return [Hash]
268
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/synonyms-stopwords`
268
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/synonyms-stopwords`
269
269
  # @see `https://typesense.org/docs/latest/api/stopwords.html#delete-a-stopword`
270
270
  def stopwords_delete(collection:, id:)
271
271
  admin_resource_request(
@@ -285,7 +285,7 @@ module SearchEngine
285
285
  # @param action [Symbol, String] one of :upsert, :create, :update (default: :upsert)
286
286
  # @return [Object] upstream return (String of JSONL statuses or Array of Hashes depending on gem version)
287
287
  # @raise [SearchEngine::Errors::*]
288
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer`
288
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer`
289
289
  # @see `https://typesense.org/docs/latest/api/documents.html#import-documents`
290
290
  def import_documents(collection:, jsonl:, action: :upsert)
291
291
  services.fetch(:documents).import(collection: collection, jsonl: jsonl, action: action)
@@ -296,7 +296,7 @@ module SearchEngine
296
296
  # @param filter_by [String] Typesense filter string
297
297
  # @param timeout_ms [Integer, nil] optional read timeout override in ms
298
298
  # @return [Hash] response from Typesense client (symbolized)
299
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer#stale-deletes`
299
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer#stale-deletes`
300
300
  # @see `https://typesense.org/docs/latest/api/documents.html#delete-documents-by-query`
301
301
  def delete_documents_by_filter(collection:, filter_by:, timeout_ms: nil)
302
302
  services.fetch(:documents).delete_by_filter(collection: collection, filter_by: filter_by, timeout_ms: timeout_ms)
@@ -366,7 +366,7 @@ module SearchEngine
366
366
  # @param url_opts [Hash] URL/common knobs (use_cache, cache_ttl)
367
367
  # @return [Hash] Raw Typesense multi-search response with key 'results'
368
368
  # @raise [SearchEngine::Errors::InvalidParams, SearchEngine::Errors::*]
369
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/multi-search-guide`
369
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/multi-search-guide`
370
370
  # @see `https://typesense.org/docs/latest/api/#multi-search`
371
371
  def multi_search(searches:, url_opts: {})
372
372
  services.fetch(:search).multi(searches: searches, url_opts: url_opts)
@@ -14,13 +14,16 @@ module SearchEngine
14
14
  class << self
15
15
  # Build a map of logical names => model classes by merging registry with
16
16
  # a scan of the SearchEngine namespace for subclasses of Base.
17
+ # Cached and invalidated automatically when Registry.mapping changes
18
+ # (copy-on-write replacement produces a new object identity).
17
19
  # @return [Hash{String=>Class}]
18
20
  def models_map
19
- map = {}
20
21
  reg = SearchEngine::Registry.mapping
22
+ return @__models_map_cache if @__models_map_cache && @__models_map_reg_id == reg.object_id
23
+
24
+ map = {}
21
25
  reg.each { |k, v| map[k.to_s] = v } if reg && !reg.empty?
22
26
 
23
- # Walk the SearchEngine namespace to find Base descendants
24
27
  begin
25
28
  SearchEngine.constants.each do |c|
26
29
  const = SearchEngine.const_get(c)
@@ -39,6 +42,8 @@ module SearchEngine
39
42
  # best-effort; namespace may not be fully loaded
40
43
  end
41
44
 
45
+ @__models_map_cache = map
46
+ @__models_map_reg_id = reg.object_id
42
47
  map
43
48
  end
44
49