search-engine-for-typesense 30.1.6.1 → 30.1.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2107cc318bf6916c602aa10cd0804afc83f8148eac15b66572526644ffd10fd5
4
- data.tar.gz: ef5327d92e2140487b6a407bba9b140aa5e70636f063aaa146e9345f6723eb87
3
+ metadata.gz: ea3e7da2359ce1d9b4990147c6627e00a726cfa4e75cc340649ae561b32e42fe
4
+ data.tar.gz: 6b14b6b9e39cab91210e16ecced55f19948831d9f949cb2969f0be5731229cf1
5
5
  SHA512:
6
- metadata.gz: eef349f62ec8a29de651d8511f0d678bf874fff636ce566709c247464df5861da8257d1f95cfa7446a9d8f9ac6e35dd04ea6df13cd33c1511638dbf27f46801f
7
- data.tar.gz: a5a72915fc94ee24751ac255bb5920d85d4f94da9dab0dbe53382456b017363de1d025bba46c6ac871e02d80bbbecc4f8c2d35a131430397d187716b21eb2c8e
6
+ metadata.gz: 6c2252a8d88ebe47b54bb7ac7ed2d65f2e0ed82b3e011ffeb3f5fb3756bff75de417c055a1de8c71fa513e51a6cfcb70b1025e6463164ff5de7018b1fcec1df8
7
+ data.tar.gz: 5519c44a440ed906df1673baf2682b1630171c0a2efb103feeb3300700d2d2653d521432fa332d2bc344c2cf717ba0ae7aa4d3feaaa6dcc7c82cfc0ba1486a4f
@@ -96,7 +96,9 @@ module SearchEngine
96
96
  #
97
97
  # @param name [#to_sym]
98
98
  # @param type [Object] type descriptor (e.g., :string, :integer)
99
- # @param index [Boolean, nil] when false, omit from compiled Typesense schema (still hydrated/displayed)
99
+ # @param index [Boolean, nil] when false, omit from compiled Typesense schema (still
100
+ # hydrated/displayed). Fields referenced by an embedding's `from:` are kept in the
101
+ # schema with Typesense-native `"index": false` instead of being omitted entirely.
100
102
  # @param locale [String, nil]
101
103
  # @param optional [Boolean, nil]
102
104
  # @param sort [Boolean, nil]
@@ -566,7 +566,7 @@ module SearchEngine
566
566
  base_fields.reject! { |fname| fname.to_s.include?('.') }
567
567
  required = base_fields.to_set
568
568
  opts.each do |fname, o|
569
- next unless o.is_a?(Hash) && o[:optional]
569
+ next unless o.is_a?(Hash) && (o[:optional] || o[:index] == false)
570
570
 
571
571
  required.delete(fname.to_sym)
572
572
  end
@@ -35,7 +35,7 @@ module SearchEngine
35
35
  }.freeze
36
36
 
37
37
  FIELD_COMPARE_KEYS = %i[
38
- type reference async_reference locale sort optional infix facet
38
+ type reference async_reference locale sort optional index infix facet
39
39
  embed num_dim hnsw_params
40
40
  ].freeze
41
41
  PHYSICAL_SUFFIX_RE = /_\d{8}_\d{6}_\d{3}\z/
@@ -44,9 +44,14 @@ module SearchEngine
44
44
  # Build a Typesense-compatible schema hash from a model class DSL.
45
45
  #
46
46
  # The output includes only keys that are supported and declared via the DSL.
47
- # Fields explicitly marked with `index: false` are intentionally omitted
48
- # from the compiled schema (they can still be sent in documents and will
49
- # be hydrated/displayed, but are not indexed in memory).
47
+ # Fields explicitly marked with `index: false` are generally omitted from
48
+ # the compiled schema (they can still be sent in documents and will be
49
+ # hydrated/displayed, but are not indexed in memory).
50
+ #
51
+ # **Exception:** fields with `index: false` that are referenced by an
52
+ # embedding's `from:` list are included with Typesense-native
53
+ # `"index": false` and `"optional": true` so the embedding model can
54
+ # read their values without keyword-indexing them.
50
55
  #
51
56
  # @param klass [Class] model class inheriting from {SearchEngine::Base}
52
57
  # @return [Hash] frozen schema hash with symbol keys
@@ -462,6 +467,7 @@ module SearchEngine
462
467
  attribute_options = klass.respond_to?(:attribute_options) ? (klass.attribute_options || {}) : {}
463
468
  references_by_local_key = build_references_by_local_key(klass, client: client)
464
469
  async_reference_by_local_key = build_async_reference_by_local_key(klass)
470
+ embed_source_fields = collect_embed_source_fields(attribute_options)
465
471
 
466
472
  fields_array = []
467
473
  needs_nested_fields = false
@@ -470,14 +476,16 @@ module SearchEngine
470
476
  validate_attribute_type!(attribute_name, type_descriptor)
471
477
 
472
478
  opts = attribute_options[attribute_name.to_sym] || {}
473
- # Skip non-indexed attributes and any nested fields under a non-indexed base
479
+ # Skip non-indexed attributes and any nested fields under a non-indexed base,
480
+ # unless the field is referenced by an embedding's from: list.
474
481
  base_index_false = false
475
482
  if attribute_name.to_s.include?('.')
476
483
  base_sym = attribute_name.to_s.split('.', 2).first.to_sym
477
484
  base_opts = attribute_options[base_sym] || {}
478
485
  base_index_false = (base_opts[:index] == false)
479
486
  end
480
- next if opts[:index] == false || base_index_false
487
+ effectively_unindexed = opts[:index] == false || base_index_false
488
+ next if effectively_unindexed && !embed_source_fields.include?(attribute_name.to_sym)
481
489
 
482
490
  ts_type = typesense_type_for(type_descriptor)
483
491
 
@@ -536,14 +544,34 @@ module SearchEngine
536
544
  %w[object object[]].include?(ts_type)
537
545
  end
538
546
 
547
+ # Collect field names referenced by any embedding's from: list.
548
+ # @param attribute_options [Hash] model attribute options
549
+ # @return [Set<Symbol>] field names used as embedding sources
550
+ def collect_embed_source_fields(attribute_options)
551
+ sources = Set.new
552
+ attribute_options.each_value do |opts|
553
+ next unless opts.is_a?(Hash)
554
+
555
+ embed = opts[:embed]
556
+ next unless embed.is_a?(Hash)
557
+
558
+ from = embed[:from] || embed['from']
559
+ Array(from).each { |f| sources << f.to_sym }
560
+ end
561
+ sources
562
+ end
563
+ private :collect_embed_source_fields
564
+
539
565
  def build_field_entry(attribute_name, ts_type, references_by_local_key, async_reference_by_local_key, opts)
566
+ unindexed = opts[:index] == false
540
567
  {
541
568
  name: attribute_name.to_s,
542
569
  type: ts_type,
543
570
  **{
571
+ index: unindexed ? false : nil,
544
572
  locale: opts[:locale],
545
573
  sort: opts[:sort],
546
- optional: opts[:optional],
574
+ optional: unindexed ? true : opts[:optional],
547
575
  infix: opts[:infix],
548
576
  facet: opts[:facet],
549
577
  reference: references_by_local_key[attribute_name.to_sym],
@@ -621,7 +649,7 @@ module SearchEngine
621
649
  entry = { name: fname, type: normalize_type(ftype) }
622
650
  entry[:reference] = fref.to_s unless fref.nil? || fref.to_s.strip.empty?
623
651
 
624
- %i[locale sort optional infix facet async_reference].each do |k|
652
+ %i[locale sort optional index infix facet async_reference].each do |k|
625
653
  val = field[k] || field[k.to_s]
626
654
  entry[k] = val unless val.nil?
627
655
  end
@@ -3,5 +3,5 @@
3
3
  module SearchEngine
4
4
  # Current gem version.
5
5
  # @return [String]
6
- VERSION = '30.1.6.1'
6
+ VERSION = '30.1.6.2'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search-engine-for-typesense
3
3
  version: !ruby/object:Gem::Version
4
- version: 30.1.6.1
4
+ version: 30.1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shkoda