search-engine-for-typesense 30.1.6.0 → 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 +4 -4
- data/README.md +0 -3
- data/lib/generators/search_engine/install/templates/initializer.rb.tt +26 -0
- data/lib/generators/search_engine/model/templates/model.rb.tt +9 -0
- data/lib/search_engine/base/model_dsl.rb +3 -1
- data/lib/search_engine/mapper.rb +1 -1
- data/lib/search_engine/schema.rb +36 -8
- data/lib/search_engine/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea3e7da2359ce1d9b4990147c6627e00a726cfa4e75cc340649ae561b32e42fe
|
|
4
|
+
data.tar.gz: 6b14b6b9e39cab91210e16ecced55f19948831d9f949cb2969f0be5731229cf1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c2252a8d88ebe47b54bb7ac7ed2d65f2e0ed82b3e011ffeb3f5fb3756bff75de417c055a1de8c71fa513e51a6cfcb70b1025e6463164ff5de7018b1fcec1df8
|
|
7
|
+
data.tar.gz: 5519c44a440ed906df1673baf2682b1630171c0a2efb103feeb3300700d2d2653d521432fa332d2bc344c2cf717ba0ae7aa4d3feaaa6dcc7c82cfc0ba1486a4f
|
data/README.md
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# Search Engine for Typesense [![CI][ci-badge]][ci-url] [![Gem][gem-badge]][gem-url] [![Docs][docs-badge]][docs-url]
|
|
2
2
|
[](https://typesense.org) [](https://github.com/typesense/typesense-ruby)
|
|
3
3
|
|
|
4
|
-
> [!WARNING]
|
|
5
|
-
> **⚠️ This project is under maintenance – work in progress. APIs and docs may change. ⚠️**
|
|
6
|
-
|
|
7
4
|
Mountless Rails::Engine for [Typesense](https://typesense.org). Expressive Relation/DSL with JOINs, grouping, presets/curation — with strong DX and observability.
|
|
8
5
|
|
|
9
6
|
> [!NOTE]
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# - CLI (Doctor): https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/cli
|
|
16
16
|
# - DX helpers: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/dx
|
|
17
17
|
# - Configuration: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/configuration
|
|
18
|
+
# - Vector search: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/vector-search
|
|
18
19
|
SearchEngine.configure do |c|
|
|
19
20
|
c.host = ENV.fetch('TYPESENSE_HOST', 'localhost')
|
|
20
21
|
c.port = Integer(ENV.fetch('TYPESENSE_PORT', 8108))
|
|
@@ -82,6 +83,26 @@ SearchEngine.configure do |c|
|
|
|
82
83
|
# Override the logger used by the engine (defaults to Rails.logger). Example:
|
|
83
84
|
# c.logger = Rails.logger
|
|
84
85
|
|
|
86
|
+
# --- Embedding / Vector search -------------------------------------------
|
|
87
|
+
# See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/vector-search
|
|
88
|
+
|
|
89
|
+
# Default embedding model used by the `embedding` DSL macro when no
|
|
90
|
+
# per-field model: override is provided. Typesense ships built-in models
|
|
91
|
+
# (e.g. "ts/all-MiniLM-L12-v2"); remote providers like OpenAI are also
|
|
92
|
+
# supported — set api_key below or per-field.
|
|
93
|
+
# c.embedding.model = 'ts/all-MiniLM-L12-v2'
|
|
94
|
+
|
|
95
|
+
# API key for remote embedding providers (e.g. OpenAI). Keep in ENV.
|
|
96
|
+
# c.embedding.api_key = ENV['EMBEDDING_API_KEY']
|
|
97
|
+
|
|
98
|
+
# Extra model_config merged into every Typesense embed block.
|
|
99
|
+
# c.embedding.model_config = {}
|
|
100
|
+
|
|
101
|
+
# Tolerance for vector_search weights sum validation (weights must sum
|
|
102
|
+
# to ~1.0 within this tolerance). Raise when many small weights cause
|
|
103
|
+
# floating-point drift. Default: 0.01
|
|
104
|
+
# c.embedding.weights_sum_tolerance = 0.01
|
|
105
|
+
|
|
85
106
|
# --- Presets -------------------------------------------------------------
|
|
86
107
|
# See: https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/presets
|
|
87
108
|
|
|
@@ -125,6 +146,11 @@ SearchEngine.configure do |c|
|
|
|
125
146
|
# c.observability.include_error_messages = false
|
|
126
147
|
# c.observability.emit_legacy_event_aliases = true
|
|
127
148
|
|
|
149
|
+
# Redact raw float arrays inside vector_query strings in logs/telemetry.
|
|
150
|
+
# When true (default), vectors are replaced with [<N dims>].
|
|
151
|
+
# Set to false to see raw vectors for debugging.
|
|
152
|
+
# c.observability.redact_vectors = true
|
|
153
|
+
|
|
128
154
|
# OpenTelemetry integration (only when the SDK is present). Defaults shown.
|
|
129
155
|
# c.opentelemetry = { enabled: false, service_name: 'search_engine' }
|
|
130
156
|
|
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
# Docs:
|
|
5
5
|
# - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/quickstart
|
|
6
6
|
# - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/field-selection#guardrails--errors
|
|
7
|
+
# - https://nikita-shkoda.mintlify.app/projects/search-engine-for-typesense/v30.1/vector-search
|
|
7
8
|
class SearchEngine::<%= class_name %> < SearchEngine::Base
|
|
8
9
|
collection "<%= @collection_name %>"
|
|
9
10
|
<% Array(@attributes).each do |(name, type)| -%>
|
|
10
11
|
attribute :<%= name %>, :<%= type %>
|
|
11
12
|
<% end -%>
|
|
13
|
+
|
|
14
|
+
# --- Vector search (optional) ---
|
|
15
|
+
# Auto-embedding: Typesense generates vectors from the listed text fields.
|
|
16
|
+
# Requires config.embedding.model or a per-field model: override.
|
|
17
|
+
# embedding from: %i[name description]
|
|
18
|
+
#
|
|
19
|
+
# External embedding: bring your own vectors (num_dim required).
|
|
20
|
+
# embedding :my_vector, num_dim: 384, suffix: false
|
|
12
21
|
end
|
|
@@ -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
|
|
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]
|
data/lib/search_engine/mapper.rb
CHANGED
|
@@ -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
|
data/lib/search_engine/schema.rb
CHANGED
|
@@ -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
|
|
48
|
-
#
|
|
49
|
-
#
|
|
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
|
-
|
|
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
|