Skip to content

Commit 8079226

Browse files
committed
feat(stemming): add support for stemming dictionaries API
- Add `Stemming`, `StemmingDictionaries` and `StemmingDictionary` classes - Add `stemming` attribute to `Client` class - Add integration tests for dictionary operations - Update `collections_spec.rb` with stem dictionary field
1 parent 00f6fd3 commit 8079226

7 files changed

Lines changed: 130 additions & 1 deletion

File tree

lib/typesense.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ module Typesense
3232
require_relative 'typesense/stats'
3333
require_relative 'typesense/operations'
3434
require_relative 'typesense/error'
35+
require_relative 'typesense/stemming'
36+
require_relative 'typesense/stemming_dictionaries'
37+
require_relative 'typesense/stemming_dictionary'

lib/typesense/client.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Typesense
44
class Client
55
attr_reader :configuration, :collections, :aliases, :keys, :debug, :health, :metrics, :stats, :operations,
6-
:multi_search, :analytics, :presets
6+
:multi_search, :analytics, :presets, :stemming
77

88
def initialize(options = {})
99
@configuration = Configuration.new(options)
@@ -18,6 +18,7 @@ def initialize(options = {})
1818
@stats = Stats.new(@api_call)
1919
@operations = Operations.new(@api_call)
2020
@analytics = Analytics.new(@api_call)
21+
@stemming = Stemming.new(@api_call)
2122
@presets = Presets.new(@api_call)
2223
end
2324
end

lib/typesense/stemming.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
module Typesense
3+
class Stemming
4+
RESOURCE_PATH = "/stemming"
5+
6+
def initialize(api_call)
7+
@api_call = api_call
8+
end
9+
10+
def dictionaries
11+
@dictionaries ||= StemmingDictionaries.new(@api_call)
12+
end
13+
end
14+
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module Typesense
4+
class StemmingDictionaries
5+
RESOURCE_PATH = "/stemming/dictionaries"
6+
7+
def initialize(api_call)
8+
@api_call = api_call
9+
@dictionaries = {}
10+
end
11+
12+
def upsert(dict_id, words_and_roots_combinations)
13+
words_and_roots_combinations_in_jsonl = if words_and_roots_combinations.is_a?(Array)
14+
words_and_roots_combinations.map { |combo| Oj.dump(combo, mode: :compat) }.join("\n")
15+
else
16+
words_and_roots_combinations
17+
end
18+
19+
result_in_jsonl = @api_call.perform_request(
20+
"post",
21+
endpoint_path("import"),
22+
query_parameters: { id: dict_id },
23+
body_parameters: words_and_roots_combinations_in_jsonl,
24+
additional_headers: { "Content-Type" => "text/plain" },
25+
)
26+
27+
if words_and_roots_combinations.is_a?(Array)
28+
result_in_jsonl.split("\n").map { |r| Oj.load(r) }
29+
else
30+
result_in_jsonl
31+
end
32+
end
33+
34+
def retrieve
35+
@api_call.get(endpoint_path)
36+
end
37+
38+
def [](dict_id)
39+
@dictionaries[dict_id] ||= StemmingDictionary.new(dict_id, @api_call)
40+
end
41+
42+
private
43+
44+
def endpoint_path(operation = nil)
45+
"#{StemmingDictionaries::RESOURCE_PATH}#{operation.nil? ? "" : "/#{URI.encode_www_form_component(operation)}"}"
46+
end
47+
end
48+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# frozen_string_literal: true
2+
3+
module Typesense
4+
class StemmingDictionary
5+
def initialize(id, api_call)
6+
@dict_id = id
7+
@api_call = api_call
8+
end
9+
10+
def retrieve
11+
@api_call.get(endpoint_path)
12+
end
13+
14+
private
15+
16+
def endpoint_path
17+
"#{StemmingDictionaries::RESOURCE_PATH}/#{URI.encode_www_form_component(@dict_id)}"
18+
end
19+
end
20+
end

spec/typesense/collections_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
'optional' => false,
101101
'sort' => false,
102102
'stem' => false,
103+
'stem_dictionary' => '',
103104
'store' => true
104105
},
105106
{
@@ -112,6 +113,7 @@
112113
'optional' => false,
113114
'sort' => true,
114115
'stem' => false,
116+
'stem_dictionary' => '',
115117
'store' => true
116118
},
117119
{
@@ -124,6 +126,7 @@
124126
'optional' => false,
125127
'sort' => false,
126128
'stem' => false,
129+
'stem_dictionary' => '',
127130
'store' => true
128131
}
129132
]

spec/typesense/stemming_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
require_relative "../spec_helper"
3+
4+
describe "Stemming Dictionaries" do
5+
let(:client) do
6+
Typesense::Client.new(
7+
nodes: [{ host: "localhost", port: "8108", protocol: "http" }],
8+
api_key: "xyz",
9+
connection_timeout_seconds: 10,
10+
)
11+
end
12+
13+
let(:dictionary_id) { "test_dictionary" }
14+
let(:dictionary) do
15+
[
16+
{ "root" => "exampleRoot1", "word" => "exampleWord1" },
17+
{ "root" => "exampleRoot2", "word" => "exampleWord2" },
18+
]
19+
end
20+
21+
before { WebMock.disable! }
22+
after { WebMock.enable! }
23+
24+
it "can upsert a dictionary" do
25+
response = client.stemming.dictionaries.upsert(dictionary_id, dictionary)
26+
expect(response).to eq(dictionary)
27+
end
28+
29+
it "can retrieve a dictionary" do
30+
response = client.stemming.dictionaries[dictionary_id].retrieve
31+
expect(response["id"]).to eq(dictionary_id)
32+
expect(response["words"]).to eq(dictionary)
33+
end
34+
35+
it "can retrieve all dictionaries" do
36+
response = client.stemming.dictionaries.retrieve
37+
expect(response["dictionaries"].length).to eq(1)
38+
expect(response["dictionaries"][0]).to eq(dictionary_id)
39+
end
40+
end

0 commit comments

Comments
 (0)