Skip to content

Commit 0b689b2

Browse files
authored
Merge pull request #19 from tharropoulos/v30
feat: add v30 support
2 parents ab7308e + 45beadd commit 0b689b2

9 files changed

Lines changed: 372 additions & 12 deletions

File tree

.github/workflows/tests.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ permissions:
77

88
jobs:
99
test:
10+
name: test (typesense ${{ matrix.typesense-version }})
1011
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
typesense-version: ['28.0', '30.1']
1116

1217
env:
1318
BUNDLE_JOBS: 4
@@ -16,7 +21,7 @@ jobs:
1621

1722
services:
1823
typesense:
19-
image: typesense/typesense:28.0
24+
image: typesense/typesense:${{ matrix.typesense-version }}
2025
ports:
2126
- 8108:8108
2227
volumes:

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
source "http://rubygems.org"
22

33
gem 'json', '>= 1.5.1'
4-
gem "typesense", "~> 0.13.0"
4+
gem "typesense", ">= 5.0.0", "< 6.0.0"
55

66

77
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Special thanks to the Algolia team for their original implementation, which prov
1818
- Environment-specific indexing
1919
- Support for faceted search and filtering
2020
- Customizable schema with predefined fields
21-
- Multi-way and one-way synonyms support
21+
- Multi-way and one-way synonyms support across Typesense v29 and v30+
22+
- Direct attachment of v30+ synonym sets and curation sets
2223
- Rake tasks for index management
2324

2425
## Installation
@@ -91,6 +92,10 @@ class Product < ApplicationRecord
9192
}
9293
]
9394

95+
# Attach existing global resources on Typesense v30+
96+
synonym_sets ["catalog-synonyms"]
97+
curation_sets ["catalog-curations"]
98+
9499
# Symbols to index
95100
symbols_to_index ["-", "_"]
96101

@@ -103,6 +108,9 @@ class Product < ApplicationRecord
103108
end
104109
```
105110

111+
For Typesense v29, `multi_way_synonyms` and `one_way_synonyms` continue to use the legacy collection-level synonym APIs.
112+
For Typesense v30 and newer, this gem stores those DSL synonyms in a collection-specific synonym set and attaches it automatically, while also supporting explicit `synonym_sets` / `curation_sets`.
113+
106114
### Working with Relationships
107115

108116
```ruby

lib/typesense-rails.rb

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class IndexSettings
8080
OPTIONS = [
8181
:multi_way_synonyms,
8282
:one_way_synonyms,
83+
:synonym_sets,
84+
:curation_sets,
8385
:predefined_fields,
8486
:fields,
8587
:default_sorting_field,
@@ -271,11 +273,13 @@ def collection_name_with_timestamp(options)
271273
"#{typesense_collection_name(options)}_#{Time.now.to_i}"
272274
end
273275

274-
def typesense_create_collection(collection_name, settings = nil)
276+
def typesense_create_collection(collection_name, settings = nil, existing_collection: nil)
275277
fields = settings.get_setting(:predefined_fields) || settings.get_setting(:fields)
276278
default_sorting_field = settings.get_setting(:default_sorting_field)
277279
multi_way_synonyms = settings.get_setting(:multi_way_synonyms)
278280
one_way_synonyms = settings.get_setting(:one_way_synonyms)
281+
synonym_sets = settings.get_setting(:synonym_sets)
282+
curation_sets = settings.get_setting(:curation_sets)
279283
symbols_to_index = settings.get_setting(:symbols_to_index)
280284
token_separators = settings.get_setting(:token_separators)
281285
enable_nested_fields = settings.get_setting(:enable_nested_fields)
@@ -300,9 +304,14 @@ def typesense_create_collection(collection_name, settings = nil)
300304
)
301305
Typesense.log(:debug, "Collection '#{collection_name}' created!")
302306

303-
typesense_multi_way_synonyms(collection_name, multi_way_synonyms) if multi_way_synonyms
304-
305-
typesense_one_way_synonyms(collection_name, one_way_synonyms) if one_way_synonyms
307+
apply_typesense_collection_resources(
308+
collection_name,
309+
multi_way_synonyms: multi_way_synonyms,
310+
one_way_synonyms: one_way_synonyms,
311+
synonym_sets: synonym_sets,
312+
curation_sets: curation_sets,
313+
existing_collection: existing_collection
314+
)
306315
end
307316

308317
def typesense_upsert_alias(collection_name, alias_name)
@@ -385,6 +394,103 @@ def typesense_one_way_synonyms(collection, synonyms)
385394
end
386395
end
387396

397+
def apply_typesense_collection_resources(collection_name, multi_way_synonyms: nil, one_way_synonyms: nil, synonym_sets: nil, curation_sets: nil, existing_collection: nil)
398+
if typesense_server_major_version >= 30
399+
apply_v30_collection_resources(
400+
collection_name,
401+
multi_way_synonyms: multi_way_synonyms,
402+
one_way_synonyms: one_way_synonyms,
403+
synonym_sets: synonym_sets,
404+
curation_sets: curation_sets,
405+
existing_collection: existing_collection
406+
)
407+
else
408+
ensure_v30_resource_support!(synonym_sets, curation_sets)
409+
typesense_multi_way_synonyms(collection_name, multi_way_synonyms) if multi_way_synonyms
410+
typesense_one_way_synonyms(collection_name, one_way_synonyms) if one_way_synonyms
411+
end
412+
end
413+
414+
def apply_v30_collection_resources(collection_name, multi_way_synonyms: nil, one_way_synonyms: nil, synonym_sets: nil, curation_sets: nil, existing_collection: nil)
415+
inline_synonyms_present = multi_way_synonyms || one_way_synonyms
416+
attached_synonym_sets = Array(existing_collection && existing_collection["synonym_sets"]) + Array(synonym_sets)
417+
attached_curation_sets = Array(existing_collection && existing_collection["curation_sets"]) + Array(curation_sets)
418+
419+
if inline_synonyms_present
420+
synonym_set_name = default_synonym_set_name(collection_name)
421+
ensure_synonym_set_exists(synonym_set_name)
422+
upsert_synonym_set_items(synonym_set_name, multi_way_synonyms, one_way_synonyms)
423+
attached_synonym_sets << synonym_set_name
424+
end
425+
426+
collection_patch = {}
427+
collection_patch["synonym_sets"] = attached_synonym_sets.uniq if attached_synonym_sets.any?
428+
collection_patch["curation_sets"] = attached_curation_sets.uniq if attached_curation_sets.any?
429+
430+
return if collection_patch.empty?
431+
432+
typesense_client.collections[collection_name].update(collection_patch)
433+
end
434+
435+
def ensure_v30_resource_support!(synonym_sets, curation_sets)
436+
unsupported = []
437+
unsupported << "synonym_sets" if synonym_sets
438+
unsupported << "curation_sets" if curation_sets
439+
return if unsupported.empty?
440+
441+
raise Typesense::BadConfiguration, "#{unsupported.join(' and ')} require Typesense v30.0 or newer"
442+
end
443+
444+
def default_synonym_set_name(collection_name)
445+
"#{collection_name}_synonyms_index"
446+
end
447+
448+
def ensure_synonym_set_exists(synonym_set_name)
449+
typesense_client.synonym_sets.upsert(synonym_set_name, { "items" => [] })
450+
end
451+
452+
def upsert_synonym_set_items(synonym_set_name, multi_way_synonyms, one_way_synonyms)
453+
items = []
454+
455+
Array(multi_way_synonyms).each do |synonym_hash|
456+
synonym_hash.each do |synonym_name, synonym|
457+
items << { "id" => synonym_name, "synonyms" => synonym }
458+
end
459+
end
460+
461+
Array(one_way_synonyms).each do |synonym_hash|
462+
synonym_hash.each do |synonym_name, synonym|
463+
items << synonym.merge("id" => synonym_name)
464+
end
465+
end
466+
467+
typesense_client.synonym_sets.upsert(synonym_set_name, { "items" => items })
468+
end
469+
470+
def typesense_server_major_version
471+
Typesense.server_major_version
472+
end
473+
474+
def reset_typesense_server_major_version!
475+
Typesense.reset_server_version_cache!
476+
end
477+
478+
def typesense_debug_info
479+
Typesense.debug_info
480+
end
481+
482+
def typesense_collection_resources(collection_name)
483+
return {} if typesense_server_major_version < 30
484+
485+
collection = typesense_get_collection(collection_name)
486+
return {} unless collection
487+
488+
{
489+
"synonym_sets" => Array(collection["synonym_sets"]),
490+
"curation_sets" => Array(collection["curation_sets"])
491+
}
492+
end
493+
388494
def typesense(options = {}, &block)
389495
self.typesense_settings = IndexSettings.new(options, &block)
390496
self.typesense_options = { type: typesense_full_const_get(model_name.to_s) }.merge(options) # :per_page => typesense_settings.get_setting(:hitsPerPage) || 10, :page => 1
@@ -528,8 +634,10 @@ def typesense_reindex(batch_size = Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
528634
typesense_configurations.each do |options, settings|
529635
next if typesense_indexing_disabled?(options)
530636

637+
existing_collection_resources = {}
531638
begin
532639
master_index = typesense_ensure_init(options, settings, false)
640+
existing_collection_resources = typesense_collection_resources(master_index[:alias_name])
533641
delete_collection(master_index[:alias_name])
534642
rescue ArgumentError
535643
@typesense_indexes[settings] = { collection_name: "", alias_name: typesense_collection_name(options) }
@@ -542,7 +650,7 @@ def typesense_reindex(batch_size = Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
542650
tmp_options.delete(:per_environment) # already included in the temporary index_name
543651
tmp_settings = settings.dup
544652

545-
create_collection(src_index_name, settings)
653+
create_collection(src_index_name, settings, existing_collection: existing_collection_resources)
546654

547655
typesense_find_in_batches(batch_size) do |group|
548656
if typesense_conditional_index?(options)

lib/typesense/config.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def configuration=(configuration)
1616
@@pagination_backend = configuration[:pagination_backend] if configuration.key?(:pagination_backend)
1717
@@log_level = configuration[:log_level] if configuration.key?(:log_level)
1818
@@configuration = configuration
19+
@client = nil
20+
reset_server_version_cache!
1921
end
2022

2123
def pagination_backend
@@ -66,5 +68,21 @@ def client
6668
def setup_client
6769
@client = Typesense::Client.new(@@configuration)
6870
end
71+
72+
def server_major_version
73+
@server_major_version ||= begin
74+
version = debug_info.fetch("version", "")
75+
version == "nightly" ? 30 : version.split(".").first.to_i
76+
end
77+
end
78+
79+
def debug_info
80+
@debug_info ||= client.debug.retrieve
81+
end
82+
83+
def reset_server_version_cache!
84+
@server_major_version = nil
85+
@debug_info = nil
86+
end
6987
end
7088
end

lib/typesense/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Typesense
2-
GEM_VERSION = "1.0.0.rc5"
2+
GEM_VERSION = "1.0.0.rc6"
33
end

0 commit comments

Comments
 (0)