search-engine-for-typesense 30.1.0 → 30.1.1

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 (57) 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/cli/doctor.rb +6 -6
  14. data/lib/search_engine/client/request_builder.rb +2 -2
  15. data/lib/search_engine/client.rb +19 -19
  16. data/lib/search_engine/collection_resolver.rb +7 -2
  17. data/lib/search_engine/config/presets.rb +7 -7
  18. data/lib/search_engine/config.rb +5 -5
  19. data/lib/search_engine/console_helpers.rb +4 -4
  20. data/lib/search_engine/dispatcher.rb +2 -2
  21. data/lib/search_engine/dsl/parser.rb +9 -9
  22. data/lib/search_engine/errors.rb +6 -6
  23. data/lib/search_engine/filters/sanitizer.rb +24 -44
  24. data/lib/search_engine/hydration/materializers.rb +13 -7
  25. data/lib/search_engine/indexer/batch_planner.rb +3 -8
  26. data/lib/search_engine/indexer/import_dispatcher.rb +1 -1
  27. data/lib/search_engine/indexer/retry_policy.rb +9 -6
  28. data/lib/search_engine/indexer.rb +3 -176
  29. data/lib/search_engine/instrumentation.rb +1 -1
  30. data/lib/search_engine/joins/guard.rb +4 -4
  31. data/lib/search_engine/joins/resolver.rb +2 -2
  32. data/lib/search_engine/logging_subscriber.rb +4 -4
  33. data/lib/search_engine/mapper.rb +13 -21
  34. data/lib/search_engine/multi.rb +2 -2
  35. data/lib/search_engine/multi_result.rb +1 -1
  36. data/lib/search_engine/notifications/compact_logger.rb +3 -3
  37. data/lib/search_engine/otel.rb +5 -5
  38. data/lib/search_engine/partitioner.rb +5 -5
  39. data/lib/search_engine/ranking_plan.rb +4 -4
  40. data/lib/search_engine/relation/compiler.rb +11 -6
  41. data/lib/search_engine/relation/dsl/filters.rb +9 -9
  42. data/lib/search_engine/relation/dsl/selection.rb +3 -3
  43. data/lib/search_engine/relation/dsl.rb +17 -17
  44. data/lib/search_engine/relation/dx.rb +4 -4
  45. data/lib/search_engine/relation/materializers.rb +1 -1
  46. data/lib/search_engine/relation/options.rb +1 -1
  47. data/lib/search_engine/relation.rb +1 -1
  48. data/lib/search_engine/result.rb +1 -1
  49. data/lib/search_engine/schema.rb +4 -4
  50. data/lib/search_engine/sources/active_record_source.rb +0 -1
  51. data/lib/search_engine/sources/lambda_source.rb +1 -1
  52. data/lib/search_engine/sources/sql_source.rb +1 -1
  53. data/lib/search_engine/sources.rb +3 -3
  54. data/lib/search_engine/test/stub_client.rb +8 -8
  55. data/lib/search_engine/test.rb +2 -2
  56. data/lib/search_engine/version.rb +1 -1
  57. metadata +2 -2
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module SearchEngine
4
6
  class Relation
5
7
  # Compile immutable relation state and options into Typesense body params.
@@ -11,7 +13,7 @@ module SearchEngine
11
13
  # redaction-aware instrumentation events for DX surfaces.
12
14
  #
13
15
  # @return [SearchEngine::CompiledParams] deterministic, deeply frozen params
14
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl`
16
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl`
15
17
  # @see `https://typesense.org/docs/latest/api/documents.html#search-document`
16
18
  def to_typesense_params
17
19
  cfg = SearchEngine.config
@@ -80,7 +82,7 @@ module SearchEngine
80
82
  # Compile filter_by string from AST nodes or legacy fragments.
81
83
  # @param ast_nodes [Array<SearchEngine::AST::Node>]
82
84
  # @return [String, nil] a Typesense filter string or nil when absent
83
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/compiler`
85
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/compiler`
84
86
  def compiled_filter_by(ast_nodes)
85
87
  unless ast_nodes.empty?
86
88
  compiled = SearchEngine::Compiler.compile(ast_nodes, klass: @klass)
@@ -184,8 +186,9 @@ module SearchEngine
184
186
  applied = Array(@state[:joins])
185
187
  return {} if applied.empty?
186
188
 
189
+ seen = Set.new
187
190
  assocs = []
188
- applied.each { |a| assocs << a unless assocs.include?(a) }
191
+ applied.each { |a| assocs << a if seen.add?(a) }
189
192
 
190
193
  nested_map = @state[:select_nested] || {}
191
194
  nested_order = Array(@state[:select_nested_order])
@@ -219,6 +222,7 @@ module SearchEngine
219
222
  list = Array(nodes).flatten.compact
220
223
  return [] if list.empty?
221
224
 
225
+ seen_set = Set.new
222
226
  seen = []
223
227
  walker = lambda do |node|
224
228
  return unless node.is_a?(SearchEngine::AST::Node)
@@ -229,7 +233,7 @@ module SearchEngine
229
233
  m = field.match(/^\$(\w+)\./)
230
234
  if m
231
235
  name = m[1].to_sym
232
- seen << name unless seen.include?(name)
236
+ seen << name if seen_set.add?(name)
233
237
  end
234
238
  end
235
239
  end
@@ -248,6 +252,7 @@ module SearchEngine
248
252
  list = Array(orders).flatten.compact
249
253
  return [] if list.empty?
250
254
 
255
+ seen_set = Set.new
251
256
  seen = []
252
257
  list.each do |entry|
253
258
  field, _dir = entry.to_s.split(':', 2)
@@ -257,7 +262,7 @@ module SearchEngine
257
262
  next unless m
258
263
 
259
264
  name = m[1].to_sym
260
- seen << name unless seen.include?(name)
265
+ seen << name if seen_set.add?(name)
261
266
  end
262
267
  seen
263
268
  end
@@ -600,7 +605,7 @@ module SearchEngine
600
605
  rescue StandardError => error
601
606
  raise SearchEngine::Errors::InvalidOption.new(
602
607
  "InvalidOption: ranking options could not be compiled (#{error.class}: #{error.message})",
603
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
608
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
604
609
  )
605
610
  end
606
611
 
@@ -169,7 +169,7 @@ module SearchEngine
169
169
  raise SearchEngine::Errors::InvalidParams.new(
170
170
  "merge: cannot infer association for #{other.klass}",
171
171
  hint: 'Declare a collection on the joined model or pass assoc: :name',
172
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/joins#troubleshooting'
172
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/joins#troubleshooting'
173
173
  )
174
174
  end
175
175
 
@@ -180,7 +180,7 @@ module SearchEngine
180
180
  raise SearchEngine::Errors::InvalidParams.new(
181
181
  "merge: no join association for #{collection_name} on #{klass_name_for_inspect}",
182
182
  hint: "#{hint} Pass assoc: :name to disambiguate.",
183
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/joins#troubleshooting',
183
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/joins#troubleshooting',
184
184
  details: { target_collection: collection_name, available: cfgs.keys }
185
185
  )
186
186
  end
@@ -190,7 +190,7 @@ module SearchEngine
190
190
  raise SearchEngine::Errors::InvalidParams.new(
191
191
  "merge: ambiguous association for #{collection_name} on #{klass_name_for_inspect}",
192
192
  hint: "Pass assoc: :#{names.first} (available: #{names.map(&:inspect).join(', ')})",
193
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/joins#troubleshooting',
193
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/joins#troubleshooting',
194
194
  details: { target_collection: collection_name, matches: names }
195
195
  )
196
196
  end
@@ -207,7 +207,7 @@ module SearchEngine
207
207
  raise SearchEngine::Errors::InvalidParams.new(
208
208
  "merge: expected a join scope Symbol or Array<Symbol> for #{assoc.inspect}",
209
209
  hint: 'Use merge(assoc: :scope) or merge(assoc: [:scope1, :scope2])',
210
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#join-scope',
210
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#join-scope',
211
211
  details: { assoc: assoc, value: scope_value }
212
212
  )
213
213
  end
@@ -350,7 +350,7 @@ module SearchEngine
350
350
  unless target_klass.respond_to?(sym)
351
351
  raise SearchEngine::Errors::InvalidParams.new(
352
352
  %(Unknown join-scope :#{sym} on association :#{assoc} for #{target_klass}),
353
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#join-scope'
353
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#join-scope'
354
354
  )
355
355
  end
356
356
 
@@ -358,7 +358,7 @@ module SearchEngine
358
358
  unless rel.is_a?(SearchEngine::Relation)
359
359
  raise SearchEngine::Errors::InvalidParams.new(
360
360
  %(join-scope :#{sym} on :#{assoc} must return a SearchEngine::Relation (got #{rel.class})),
361
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#join-scope'
361
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#join-scope'
362
362
  )
363
363
  end
364
364
 
@@ -397,7 +397,7 @@ module SearchEngine
397
397
  if fragment.include?('$')
398
398
  raise SearchEngine::Errors::InvalidParams.new(
399
399
  'join-scope raw fragments must use base fields only (no nested join paths)',
400
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#join-scope',
400
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#join-scope',
401
401
  details: { fragment: fragment, assoc: assoc_sym }
402
402
  )
403
403
  end
@@ -416,7 +416,7 @@ module SearchEngine
416
416
  if lhs.start_with?('$') || lhs.include?('.')
417
417
  raise SearchEngine::Errors::InvalidParams.new(
418
418
  %(join-scope cannot reference nested join field #{lhs.inspect}; use base fields only),
419
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#join-scope',
419
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#join-scope',
420
420
  details: { field: lhs, assoc: assoc_sym }
421
421
  )
422
422
  end
@@ -553,7 +553,7 @@ module SearchEngine
553
553
  def raise_empty_array_type!(field_sym)
554
554
  raise SearchEngine::Errors::InvalidType.new(
555
555
  %(expected #{field_sym.inspect} to be a non-empty Array),
556
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/query-dsl#troubleshooting',
556
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/query-dsl#troubleshooting',
557
557
  details: { field: field_sym }
558
558
  )
559
559
  end
@@ -9,7 +9,7 @@ module SearchEngine
9
9
  # Select a subset of fields for Typesense `include_fields`.
10
10
  # @param fields [Array<Symbol,String,Hash,Array>]
11
11
  # @return [SearchEngine::Relation]
12
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection`
12
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection`
13
13
  def select(*fields)
14
14
  normalized = normalize_select_input(fields)
15
15
  spawn do |s|
@@ -46,7 +46,7 @@ module SearchEngine
46
46
  # Exclude a subset of fields from the final selection.
47
47
  # @param fields [Array<Symbol,String,Hash,Array>]
48
48
  # @return [SearchEngine::Relation]
49
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection`
49
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection`
50
50
  #
51
51
  # When excluding, you may also pass a joined association name (after calling
52
52
  # `joins(:assoc)`) to exclude the entire joined payload from the response.
@@ -88,7 +88,7 @@ module SearchEngine
88
88
  # Replace the selected fields list (Typesense `include_fields`).
89
89
  # @param fields [Array<#to_sym,#to_s>]
90
90
  # @return [SearchEngine::Relation]
91
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection`
91
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection`
92
92
  def reselect(*fields)
93
93
  normalized = normalize_select_input(fields)
94
94
 
@@ -98,7 +98,7 @@ module SearchEngine
98
98
  raise SearchEngine::Errors::InvalidOption.new(
99
99
  "InvalidOption: unknown prefix mode #{mode.inspect}",
100
100
  hint: 'Use :disabled, :fallback, or :always',
101
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#prefix',
101
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#prefix',
102
102
  details: { provided: mode, allowed: valid.keys }
103
103
  )
104
104
  end
@@ -571,7 +571,7 @@ module SearchEngine
571
571
  if field_str.start_with?('$') || field_str.include?('.')
572
572
  raise SearchEngine::Errors::UnsupportedGroupField.new(
573
573
  %(UnsupportedGroupField: grouping supports base fields only (got #{field_str.inspect})),
574
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/grouping#troubleshooting',
574
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/grouping#troubleshooting',
575
575
  details: { field: field_str }
576
576
  )
577
577
  end
@@ -583,7 +583,7 @@ module SearchEngine
583
583
  msg = build_invalid_group_unknown_field_message(sym)
584
584
  raise SearchEngine::Errors::InvalidGroup.new(
585
585
  msg,
586
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/grouping#troubleshooting',
586
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/grouping#troubleshooting',
587
587
  details: { field: sym }
588
588
  )
589
589
  end
@@ -593,7 +593,7 @@ module SearchEngine
593
593
  got = limit.nil? ? 'nil' : limit.inspect
594
594
  raise SearchEngine::Errors::InvalidGroup.new(
595
595
  "InvalidGroup: limit must be a positive integer (got #{got})",
596
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/grouping#troubleshooting',
596
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/grouping#troubleshooting',
597
597
  details: { limit: limit }
598
598
  )
599
599
  end
@@ -601,7 +601,7 @@ module SearchEngine
601
601
  unless [true, false].include?(missing_values)
602
602
  raise SearchEngine::Errors::InvalidGroup.new(
603
603
  "InvalidGroup: missing_values must be boolean (got #{missing_values.inspect})",
604
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/grouping#troubleshooting',
604
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/grouping#troubleshooting',
605
605
  details: { missing_values: missing_values }
606
606
  )
607
607
  end
@@ -702,7 +702,7 @@ module SearchEngine
702
702
  'InvalidOption: ranking expects a Hash of options',
703
703
  hint: 'Use ranking(num_typos: 1, drop_tokens_threshold: 0.2,'\
704
704
  'prioritize_exact_match: true, query_by_weights: { name: 2 })',
705
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
705
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
706
706
  )
707
707
  end
708
708
 
@@ -729,14 +729,14 @@ module SearchEngine
729
729
  unless [0, 1, 2].include?(iv)
730
730
  raise SearchEngine::Errors::InvalidOption.new(
731
731
  "InvalidOption: num_typos must be 0, 1, or 2 (got #{raw.inspect})",
732
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
732
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
733
733
  )
734
734
  end
735
735
  out[:num_typos] = iv
736
736
  rescue ArgumentError, TypeError
737
737
  raise SearchEngine::Errors::InvalidOption.new(
738
738
  "InvalidOption: num_typos must be an Integer in {0,1,2} (got #{raw.inspect})",
739
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
739
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
740
740
  )
741
741
  end
742
742
  end
@@ -752,14 +752,14 @@ module SearchEngine
752
752
  unless fv >= 0.0 && fv <= 1.0 && fv.finite?
753
753
  raise SearchEngine::Errors::InvalidOption.new(
754
754
  "InvalidOption: drop_tokens_threshold must be a float between 0.0 and 1.0 (got #{raw.inspect})",
755
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
755
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
756
756
  )
757
757
  end
758
758
  out[:drop_tokens_threshold] = fv
759
759
  rescue ArgumentError, TypeError
760
760
  raise SearchEngine::Errors::InvalidOption.new(
761
761
  "InvalidOption: drop_tokens_threshold must be a float between 0.0 and 1.0 (got #{raw.inspect})",
762
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#options'
762
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#options'
763
763
  )
764
764
  end
765
765
  end
@@ -780,7 +780,7 @@ module SearchEngine
780
780
  unless raw.is_a?(Hash)
781
781
  raise SearchEngine::Errors::InvalidOption.new(
782
782
  'InvalidOption: query_by_weights must be a Hash of { field => Integer }',
783
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#weights'
783
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#weights'
784
784
  )
785
785
  end
786
786
  normalized = {}
@@ -793,14 +793,14 @@ module SearchEngine
793
793
  rescue ArgumentError, TypeError
794
794
  raise SearchEngine::Errors::InvalidOption.new(
795
795
  "InvalidOption: weight for #{k.inspect} must be an Integer >= 0",
796
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#weights',
796
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#weights',
797
797
  details: { field: k, weight: v }
798
798
  )
799
799
  end
800
800
  if w.negative?
801
801
  raise SearchEngine::Errors::InvalidOption.new(
802
802
  "InvalidOption: weight for #{k.inspect} must be >= 0",
803
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/ranking#weights',
803
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/ranking#weights',
804
804
  details: { field: k, weight: v }
805
805
  )
806
806
  end
@@ -844,7 +844,7 @@ module SearchEngine
844
844
 
845
845
  raise SearchEngine::Errors::InvalidParams.new(
846
846
  %(#{context}: supports base fields only (got #{name.inspect})),
847
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/faceting#supported-options',
847
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/faceting#supported-options',
848
848
  details: { field: name }
849
849
  )
850
850
  end
@@ -873,7 +873,7 @@ module SearchEngine
873
873
  raise SearchEngine::Errors::InvalidParams.new(
874
874
  "facet_by: option :sort is not supported by Typesense facets (got #{sort.inspect})",
875
875
  hint: 'Supported: default count-desc only at present.',
876
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/faceting#supported-options',
876
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/faceting#supported-options',
877
877
  details: { sort: sort }
878
878
  )
879
879
  end
@@ -883,7 +883,7 @@ module SearchEngine
883
883
 
884
884
  raise SearchEngine::Errors::InvalidParams.new(
885
885
  'facet_by: option :stats is not supported at present',
886
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/faceting#supported-options',
886
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/faceting#supported-options',
887
887
  details: { stats: stats }
888
888
  )
889
889
  end
@@ -905,7 +905,7 @@ module SearchEngine
905
905
  raise SearchEngine::Errors::InvalidParams.new(
906
906
  %(facet_query: invalid range syntax #{expr.inspect} (unbalanced brackets)),
907
907
  hint: 'Use shapes like "[0..9]", "[10..19]"',
908
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/faceting#facet-query-expressions',
908
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/faceting#facet-query-expressions',
909
909
  details: { expr: expr }
910
910
  )
911
911
  end
@@ -15,7 +15,7 @@ module SearchEngine
15
15
  # @param pretty [Boolean] pretty-print with stable key ordering when true
16
16
  # @return [String]
17
17
  # @since M8
18
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
18
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
19
19
  def to_params_json(pretty: true)
20
20
  params = SearchEngine::CompiledParams.from(to_typesense_params)
21
21
  redacted = SearchEngine::Relation::Dx::DryRun.redact_params(params.to_h)
@@ -25,7 +25,7 @@ module SearchEngine
25
25
  # Return a single-line curl command with redacted API key and JSON body.
26
26
  # @return [String]
27
27
  # @since M8
28
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
28
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
29
29
  def to_curl
30
30
  url = compiled_url
31
31
  params = SearchEngine::CompiledParams.from(to_typesense_params).to_h
@@ -37,7 +37,7 @@ module SearchEngine
37
37
  # @return [Hash] { url:, body:, url_opts: }
38
38
  # @raise [SearchEngine::Errors::*] same validation errors as runtime path
39
39
  # @since M8
40
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx
40
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
41
41
  def dry_run!
42
42
  params = SearchEngine::CompiledParams.from(to_typesense_params).to_h
43
43
  SearchEngine::Relation::Dx::DryRun.payload(url: compiled_url, params: params, url_opts: compiled_url_opts)
@@ -48,7 +48,7 @@ module SearchEngine
48
48
  # @param to [Symbol, nil]
49
49
  # @return [String]
50
50
  # @since M8
51
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/dx#helpers--examples
51
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx#helpers--examples
52
52
  def explain(to: nil)
53
53
  params = SearchEngine::CompiledParams.from(to_typesense_params)
54
54
  lines = []
@@ -76,7 +76,7 @@ module SearchEngine
76
76
  # Returns nil when no records match.
77
77
  # @param fields [Array<#to_sym,#to_s>]
78
78
  # @return [Object, Array<Object>, nil]
79
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/materializers#pick
79
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/materializers#pick
80
80
  def pick(*fields)
81
81
  SearchEngine::Hydration::Materializers.pick(self, *fields)
82
82
  end
@@ -44,7 +44,7 @@ module SearchEngine
44
44
  unless value.is_a?(Hash)
45
45
  raise SearchEngine::Errors::InvalidOption.new(
46
46
  'InvalidOption: hit_limits expects a Hash of options',
47
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/hit-limits'
47
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/hit-limits'
48
48
  )
49
49
  end
50
50
 
@@ -591,7 +591,7 @@ module SearchEngine
591
591
  raise SearchEngine::Errors::HitLimitExceeded.new(
592
592
  msg,
593
593
  hint: 'Increase `validate_hits!(max:)` or narrow filters. Prefer `limit_hits(n)` to avoid work when supported.',
594
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/hit-limits#validation',
594
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/hit-limits#validation',
595
595
  details: { total_hits: th, max: max, collection: coll, relation_summary: inspect }
596
596
  )
597
597
  end
@@ -500,7 +500,7 @@ module SearchEngine
500
500
  raise SearchEngine::Errors::MissingField.new(
501
501
  msg,
502
502
  hint: 'Adjust select/exclude or disable strict_missing to avoid raising.',
503
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/field-selection#strict-vs-lenient-selection',
503
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection#strict-vs-lenient-selection',
504
504
  details: { requested: requested, present_keys: present_keys }
505
505
  )
506
506
  end
@@ -71,7 +71,7 @@ module SearchEngine
71
71
  # @param klass [Class] model class inheriting from {SearchEngine::Base}
72
72
  # @param client [SearchEngine::Client] optional client wrapper (for tests)
73
73
  # @return [Hash] { diff: Hash, pretty: String }
74
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema-indexer-e2e`
74
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema-indexer-e2e`
75
75
  # @see `https://typesense.org/docs/latest/api/collections.html`
76
76
  def diff(klass, client: nil)
77
77
  client ||= SearchEngine.client
@@ -154,7 +154,7 @@ module SearchEngine
154
154
  # @yieldparam physical_name [String] the newly created physical collection name
155
155
  # @return [Hash] { logical: String, new_physical: String, previous_physical: String, alias_target: String, dropped_physicals: Array<String> }
156
156
  # @raise [SearchEngine::Errors::Api, ArgumentError]
157
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#lifecycle`
157
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#lifecycle`
158
158
  # @see `https://typesense.org/docs/latest/api/collections.html`
159
159
  def apply!(klass, client: nil, force_rebuild: false)
160
160
  client ||= SearchEngine.client
@@ -245,7 +245,7 @@ module SearchEngine
245
245
  # @param client [SearchEngine::Client]
246
246
  # @return [Hash] { logical: String, new_target: String, previous_target: String }
247
247
  # @raise [ArgumentError] when no previous physical exists
248
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/schema#retention`
248
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/schema#retention`
249
249
  def rollback(klass, client: nil)
250
250
  client ||= SearchEngine.client
251
251
  compiled = compile(klass)
@@ -477,7 +477,7 @@ module SearchEngine
477
477
  "(got #{type_descriptor.inspect}).",
478
478
  hint: "Declare attribute :#{attribute_name}, :string in #{klass.name} to match " \
479
479
  'Typesense reference requirements.',
480
- doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/joins#declaring-references',
480
+ doc: 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/joins#declaring-references',
481
481
  details: {
482
482
  field: attribute_name.to_s,
483
483
  declared_type: type_descriptor,
@@ -81,7 +81,6 @@ module SearchEngine
81
81
  end
82
82
  elsif relation.respond_to?(:find_in_batches)
83
83
  relation.find_in_batches(batch_size: @batch_size) do |rows|
84
- rows = rows.map { |r| r }
85
84
  duration = monotonic_ms - started
86
85
  instrument_batch_fetched(source: 'active_record', batch_index: idx, rows_count: rows.size,
87
86
  duration_ms: duration, partition: partition, cursor: cursor,
@@ -11,7 +11,7 @@ module SearchEngine
11
11
  # src = SearchEngine::Sources::LambdaSource.new(->(cursor:, partition:) { [[row1, row2]] })
12
12
  # src.each_batch { |rows| ... }
13
13
  #
14
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer`
14
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer`
15
15
  class LambdaSource
16
16
  include Base
17
17
 
@@ -12,7 +12,7 @@ module SearchEngine
12
12
  # src.each_batch { |rows| ... }
13
13
  #
14
14
  # @note Emits "search_engine.source.batch_fetched" and "search_engine.source.error".
15
- # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer`
15
+ # @see `https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer`
16
16
  class SqlSource
17
17
  include Base
18
18
 
@@ -28,7 +28,7 @@ module SearchEngine
28
28
  unless model.is_a?(Class)
29
29
  raise SearchEngine::Errors::InvalidParams,
30
30
  'active_record source requires :model (ActiveRecord class). See ' \
31
- 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer.'
31
+ 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer.'
32
32
  end
33
33
 
34
34
  scope = options[:scope]
@@ -43,7 +43,7 @@ module SearchEngine
43
43
  unless sql.is_a?(String) && !sql.strip.empty?
44
44
  raise SearchEngine::Errors::InvalidParams,
45
45
  'sql source requires :sql (String). See ' \
46
- 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer.'
46
+ 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer.'
47
47
  end
48
48
 
49
49
  binds = options[:binds]
@@ -58,7 +58,7 @@ module SearchEngine
58
58
  unless callable
59
59
  raise SearchEngine::Errors::InvalidParams,
60
60
  'lambda source requires a block or :callable. See ' \
61
- 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/indexer.'
61
+ 'https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/indexer.'
62
62
  end
63
63
 
64
64
  LambdaSource.new(callable)
@@ -20,7 +20,7 @@ module SearchEngine
20
20
  # or Procs that receive the captured request and return a response.
21
21
  #
22
22
  # @since M8
23
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
23
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
24
24
  class StubClient
25
25
  Call = Struct.new(
26
26
  :timestamp,
@@ -45,7 +45,7 @@ module SearchEngine
45
45
  # @param value [Hash, Exception, Proc]
46
46
  # @return [void]
47
47
  # @since M8
48
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing#quick-start
48
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing#quick-start
49
49
  def enqueue_response(method, value)
50
50
  @lock.synchronize do
51
51
  queue_for(method) << value
@@ -54,7 +54,7 @@ module SearchEngine
54
54
 
55
55
  # Reset all internal state (queues and captures).
56
56
  # @since M8
57
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing#parallel-test-safety
57
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing#parallel-test-safety
58
58
  def reset!
59
59
  @lock.synchronize do
60
60
  @queues.each_value(&:clear)
@@ -65,7 +65,7 @@ module SearchEngine
65
65
  # Return captured calls for search.
66
66
  # @return [Array<Call>]
67
67
  # @since M8
68
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
68
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
69
69
  def search_calls
70
70
  @lock.synchronize { @calls[:search].dup }
71
71
  end
@@ -73,7 +73,7 @@ module SearchEngine
73
73
  # Return captured calls for multi_search.
74
74
  # @return [Array<Call>]
75
75
  # @since M8
76
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
76
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
77
77
  def multi_search_calls
78
78
  @lock.synchronize { @calls[:multi_search].dup }
79
79
  end
@@ -81,7 +81,7 @@ module SearchEngine
81
81
  # All calls in chronological order.
82
82
  # @return [Array<Call>]
83
83
  # @since M8
84
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
84
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
85
85
  def all_calls
86
86
  @lock.synchronize { (@calls[:search] + @calls[:multi_search]).sort_by(&:timestamp) }
87
87
  end
@@ -91,7 +91,7 @@ module SearchEngine
91
91
  # @param params [Hash]
92
92
  # @param url_opts [Hash]
93
93
  # @since M8
94
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
94
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
95
95
  def search(collection:, params:, url_opts: {})
96
96
  unless collection.is_a?(String) && !collection.strip.empty?
97
97
  raise ArgumentError, 'collection must be a non-empty String'
@@ -109,7 +109,7 @@ module SearchEngine
109
109
  # @param searches [Array<Hash>]
110
110
  # @param url_opts [Hash]
111
111
  # @since M8
112
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
112
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
113
113
  def multi_search(searches:, url_opts: {})
114
114
  unless searches.is_a?(Array) && searches.all? { |h| h.is_a?(Hash) }
115
115
  raise ArgumentError, 'searches must be an Array of Hashes'
@@ -12,7 +12,7 @@ module SearchEngine
12
12
  # These helpers are allocation-light, thread-safe, and never perform network I/O.
13
13
  #
14
14
  # @since M8
15
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing
15
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing
16
16
  module Test
17
17
  class << self
18
18
  # Subscribe to `search_engine.*` for the duration of the block and return captured events.
@@ -22,7 +22,7 @@ module SearchEngine
22
22
  # @yield block within which events are captured
23
23
  # @return [Array<Hash>]
24
24
  # @since M8
25
- # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/testing#event-assertions
25
+ # @see https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/testing#event-assertions
26
26
  def capture_events(name = nil)
27
27
  require 'active_support/notifications'
28
28
  pattern = name.is_a?(Regexp) ? name : /^search_engine\./
@@ -3,5 +3,5 @@
3
3
  module SearchEngine
4
4
  # Current gem version.
5
5
  # @return [String]
6
- VERSION = '30.1.0'
6
+ VERSION = '30.1.1'
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search-engine-for-typesense
3
3
  version: !ruby/object:Gem::Version
4
- version: 30.1.0
4
+ version: 30.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shkoda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-05 00:00:00.000000000 Z
11
+ date: 2026-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby