Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/typesense.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module Typesense
require_relative 'typesense/override'
require_relative 'typesense/synonyms'
require_relative 'typesense/synonym'
require_relative 'typesense/synonym_sets'
require_relative 'typesense/synonym_set'
require_relative 'typesense/aliases'
require_relative 'typesense/alias'
require_relative 'typesense/keys'
Expand Down
3 changes: 2 additions & 1 deletion lib/typesense/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Typesense
class Client
attr_reader :configuration, :collections, :aliases, :keys, :debug, :health, :metrics, :stats, :operations,
:multi_search, :analytics, :presets, :stemming, :nl_search_models
:multi_search, :analytics, :presets, :stemming, :nl_search_models, :synonym_sets

def initialize(options = {})
@configuration = Configuration.new(options)
Expand All @@ -21,6 +21,7 @@ def initialize(options = {})
@stemming = Stemming.new(@api_call)
@presets = Presets.new(@api_call)
@nl_search_models = NlSearchModels.new(@api_call)
@synonym_sets = SynonymSets.new(@api_call)
end
end
end
24 changes: 24 additions & 0 deletions lib/typesense/synonym_set.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Typesense
class SynonymSet
def initialize(synonym_set_name, api_call)
@synonym_set_name = synonym_set_name
@api_call = api_call
end

def retrieve
@api_call.get(endpoint_path)
end

def delete
@api_call.delete(endpoint_path)
end

private

def endpoint_path
"#{SynonymSets::RESOURCE_PATH}/#{URI.encode_www_form_component(@synonym_set_name)}"
end
end
end
30 changes: 30 additions & 0 deletions lib/typesense/synonym_sets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module Typesense
class SynonymSets
RESOURCE_PATH = '/synonym_sets'

def initialize(api_call)
@api_call = api_call
@synonym_sets = {}
end

def upsert(synonym_set_name, params)
@api_call.put(endpoint_path(synonym_set_name), params)
end

def retrieve
@api_call.get(endpoint_path)
end

def [](synonym_set_name)
@synonym_sets[synonym_set_name] ||= SynonymSet.new(synonym_set_name, @api_call)
end

private

def endpoint_path(operation = nil)
"#{RESOURCE_PATH}#{"/#{URI.encode_www_form_component(operation)}" unless operation.nil?}"
end
end
end
32 changes: 31 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,37 @@ def typesense_healthy?(host = 'localhost', port = 8108)
false
end

def typesense_version
WebMock.allow_net_connect!
conn = Faraday.new('http://localhost:8108')
response = conn.get('/debug') do |req|
req.headers['X-TYPESENSE-API-KEY'] = 'xyz'
end

if response.status == 200 && !response.body.empty?
debug_info = JSON.parse(response.body)
debug_info['version']
end
rescue StandardError
nil
ensure
WebMock.disable_net_connect!(allow_localhost: true)
end

def typesense_v30_or_above?
version = typesense_version
return false unless version

return true if version == 'nightly'

if version.match(/^v(\d+)/)
major_version = Regexp.last_match(1).to_i
return major_version >= 30
end

false
end

def ensure_typesense_running
if typesense_healthy?
puts '✅ Typesense is already running and healthy, ready for use in integration tests'
Expand Down Expand Up @@ -96,7 +127,6 @@ def stop_typesense_if_started

config.before(:suite) do
ensure_typesense_running
WebMock.disable_net_connect!
end

config.after(:suite) do
Expand Down
4 changes: 4 additions & 0 deletions spec/typesense/analytics_rule_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
}
end

before do
skip('Analytics is deprecated in Typesense v30+') if typesense_v30_or_above?
end

describe '#retrieve' do
it 'returns the specified analytics rule' do
stub_request(:get, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/analytics/rules/search_suggestions', typesense.configuration.nodes[0]))
Expand Down
4 changes: 4 additions & 0 deletions spec/typesense/analytics_rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
}
end

before do
skip('Analytics is deprecated in Typesense v30+') if typesense_v30_or_above?
end

describe '#upsert' do
it 'creates a rule and returns it' do
stub_request(:put, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/analytics/rules/search_suggestions', typesense.configuration.nodes[0]))
Expand Down
71 changes: 71 additions & 0 deletions spec/typesense/synonym_set_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

require_relative '../spec_helper'

describe Typesense::SynonymSet do
subject(:synonym_set) { typesense.synonym_sets['test-synonym-set'] }

let(:typesense) do
Typesense::Client.new(
api_key: 'xyz',
nodes: [
{
host: 'localhost',
port: 8108,
protocol: 'http'
}
],
connection_timeout_seconds: 10,
retry_interval_seconds: 0.01
)
end

let(:synonym_set_data) do
{
'synonyms' => [
{
'id' => 'dummy',
'synonyms' => %w[foo bar baz],
'root' => ''
}
]
}
end

before do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

# Create a test synonym set
typesense.synonym_sets.upsert('test-synonym-set', synonym_set_data)
end

after do
# Clean up the test synonym set
typesense.synonym_sets['test-synonym-set'].delete
rescue StandardError
# Ignore errors if already deleted
end

describe '#retrieve' do
it 'returns the specified synonym set' do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

result = synonym_set.retrieve

expect(result['synonyms']).to eq(synonym_set_data['synonyms'])
end
end

describe '#delete' do
it 'deletes the specified synonym set' do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

result = synonym_set.delete

expect(result['name']).to eq('test-synonym-set')

# Verify it's deleted by trying to retrieve it
expect { synonym_set.retrieve }.to raise_error(Typesense::Error::ObjectNotFound)
end
end
end
90 changes: 90 additions & 0 deletions spec/typesense/synonym_sets_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true

require_relative '../spec_helper'

describe Typesense::SynonymSets do
subject(:synonym_sets) { typesense.synonym_sets }

let(:typesense) do
Typesense::Client.new(
api_key: 'xyz',
nodes: [
{
host: 'localhost',
port: 8108,
protocol: 'http'
}
],
connection_timeout_seconds: 10,
retry_interval_seconds: 0.01
)
end

let(:synonym_set_data) do
{
'synonyms' => [
{
'id' => 'dummy',
'synonyms' => %w[foo bar baz],
'root' => ''
}
]
}
end

before do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?
end

after do
next unless typesense_v30_or_above?

# Clean up any created synonym sets
existing_sets = synonym_sets.retrieve
existing_sets.each do |set|
synonym_sets[set['name']].delete
end
rescue StandardError
# Ignore errors if no synonym sets exist
end

describe '#upsert' do
it 'creates a synonym set and returns it' do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

result = synonym_sets.upsert('test-synonym-set', synonym_set_data)

expect(result['synonyms']).to eq(synonym_set_data['synonyms'])
end
end

describe '#retrieve' do
it 'retrieves all synonym sets' do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

# Create a synonym set first
synonym_sets.upsert('test-synonym-set', synonym_set_data)

result = synonym_sets.retrieve

expect(result).to be_an(Array)
expect(result.length).to be >= 1

# Find our test synonym set
test_set = result.find { |set| set['name'] == 'test-synonym-set' }
expect(test_set).not_to be_nil
expect(test_set['synonyms']).to eq(synonym_set_data['synonyms'])
end
end

describe '#[]' do
it 'creates a synonym set object and returns it' do
skip('SynonymSets is only supported in Typesense v30+') unless typesense_v30_or_above?

result = synonym_sets['test-synonym-set']

expect(result).to be_a(Typesense::SynonymSet)
expect(result.instance_variable_get(:@synonym_set_name)).to eq('test-synonym-set')
end
end
end
4 changes: 4 additions & 0 deletions spec/typesense/synonym_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
}
end

before do
skip('Synonyms is deprecated in Typesense v30+, use SynonymSets instead') if typesense_v30_or_above?
end

describe '#retrieve' do
it 'returns the specified synonym' do
stub_request(:get, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/collections/companies/synonyms/synonym-set-1', typesense.configuration.nodes[0]))
Expand Down
4 changes: 4 additions & 0 deletions spec/typesense/synonyms_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
}
end

before do
skip('Synonyms is deprecated in Typesense v30+, use SynonymSets instead') if typesense_v30_or_above?
end

describe '#upsert' do
it 'creates an synonym rule and returns it' do
stub_request(:put, Typesense::ApiCall.new(typesense.configuration).send(:uri_for, '/collections/companies/synonyms/synonym-set-1', typesense.configuration.nodes[0]))
Expand Down