Skip to content

Commit 3b3ffea

Browse files
authored
v28: Add Stemming API (#83)
* feat(api): add stemming dictionary management support - Add `Stemming` class to manage dictionary operations - Add `StemmingDictionaries` to handle bulk dictionary operations - Add `StemmingDictionary` to manage individual dictionaries - Add `StemmingDictionariesRetrieveSchema` for API responses - Add tests for dictionary creation and retrieval * ci: update typesense server version to 28.0.rc36
1 parent 0571a01 commit 3b3ffea

File tree

8 files changed

+202
-1
lines changed

8 files changed

+202
-1
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
java: ['8', '11', '17']
1717
services:
1818
typesense:
19-
image: typesense/typesense:27.0
19+
image: typesense/typesense:28.0.rc36
2020
ports:
2121
- 8108:8108/tcp
2222
volumes:

src/main/java/org/typesense/api/Client.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class Client {
2121

2222
private Analytics analytics;
2323

24+
private Stemming stemming;
25+
2426
private Stopwords stopwords;
2527
private Map<String, StopwordsSet> individualStopwordsSets;
2628

@@ -45,6 +47,7 @@ public Client(Configuration configuration){
4547
this.debug = new Debug(this.apiCall);
4648
this.multiSearch = new MultiSearch(this.apiCall);
4749
this.analytics = new Analytics(this.apiCall);
50+
this.stemming = new Stemming(this.apiCall);
4851
this.stopwords = new Stopwords(this.apiCall);
4952
this.individualStopwordsSets = new HashMap<>();
5053
}
@@ -100,6 +103,10 @@ public Analytics analytics(){
100103
return this.analytics;
101104
}
102105

106+
public Stemming stemming(){
107+
return this.stemming;
108+
}
109+
103110
public Stopwords stopwords() {
104111
return this.stopwords;
105112
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.typesense.api;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class Stemming {
7+
private final ApiCall apiCall;
8+
private final StemmingDictionaries dictionaries;
9+
private final Map<String, StemmingDictionary> individualDictionaries;
10+
11+
12+
public Stemming(ApiCall apiCall) {
13+
this.apiCall = apiCall;
14+
this.dictionaries = new StemmingDictionaries(this.apiCall);
15+
this.individualDictionaries = new HashMap<>();
16+
}
17+
18+
public StemmingDictionaries dictionaries() {
19+
return this.dictionaries;
20+
}
21+
22+
public StemmingDictionary dictionaries(String dictionaryId) {
23+
StemmingDictionary retVal;
24+
25+
if (!this.individualDictionaries.containsKey(dictionaryId)) {
26+
this.individualDictionaries.put(dictionaryId, new StemmingDictionary(dictionaryId, apiCall));
27+
}
28+
29+
retVal = this.individualDictionaries.get(dictionaryId);
30+
return retVal;
31+
}
32+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.typesense.api;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import org.typesense.model.StemmingDictionaryWords;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
12+
public class StemmingDictionaries {
13+
private final ApiCall apiCall;
14+
public final static String RESOURCE_PATH = "/stemming/dictionaries";
15+
16+
public StemmingDictionaries(ApiCall apiCall) {
17+
this.apiCall = apiCall;
18+
}
19+
20+
public String upsert(String dictionaryId, String wordRootCombinations) throws Exception {
21+
Map<String, String> params = Collections.singletonMap("id", dictionaryId);
22+
23+
return this.apiCall.post(this.getEndPoint("import"), wordRootCombinations, params, String.class);
24+
}
25+
26+
public List<StemmingDictionaryWords> upsert(String dictionaryId, List<StemmingDictionaryWords> wordRootCombinations)
27+
throws Exception {
28+
ObjectMapper mapper = new ObjectMapper();
29+
List<String> jsonLines = new ArrayList<>();
30+
List<StemmingDictionaryWords> objectList = new ArrayList<>();
31+
32+
for (StemmingDictionaryWords word : wordRootCombinations) {
33+
jsonLines.add(mapper.writeValueAsString(word));
34+
}
35+
36+
String reqBody = String.join("\n", jsonLines);
37+
38+
Map<String, String> params = Collections.singletonMap("id", dictionaryId);
39+
40+
String resInJsonLineFormat = this.apiCall.post(this.getEndPoint("import"), reqBody, params, String.class);
41+
42+
for (String line : resInJsonLineFormat.split("\n")) {
43+
objectList.add(mapper.readValue(line, StemmingDictionaryWords.class));
44+
}
45+
46+
return objectList;
47+
}
48+
49+
public StemmingDictionariesRetrieveSchema retrieve() throws Exception {
50+
StemmingDictionariesRetrieveSchema response = this.apiCall.get(RESOURCE_PATH, null,
51+
StemmingDictionariesRetrieveSchema.class);
52+
return response != null ? response : new StemmingDictionariesRetrieveSchema();
53+
}
54+
55+
public String getEndPoint(String target) {
56+
return RESOURCE_PATH + "/" + target;
57+
}
58+
59+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.typesense.api;
2+
3+
import java.util.List;
4+
5+
public class StemmingDictionariesRetrieveSchema {
6+
private List<String> dictionaries;
7+
8+
public List<String> getDictionaries() {
9+
return dictionaries;
10+
}
11+
12+
public void setDictionaries(List<String> dictionaries) {
13+
this.dictionaries = dictionaries;
14+
}
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.typesense.api;
2+
3+
import org.typesense.api.utils.URLEncoding;
4+
5+
public class StemmingDictionary {
6+
private final ApiCall apiCall;
7+
private final String dictionaryId;
8+
9+
public StemmingDictionary(String dictionaryId, ApiCall apiCall) {
10+
this.apiCall = apiCall;
11+
this.dictionaryId = dictionaryId;
12+
}
13+
14+
15+
public org.typesense.model.StemmingDictionary retrieve() throws Exception {
16+
return this.apiCall.get(this.getEndpoint(), null, org.typesense.model.StemmingDictionary.class);
17+
}
18+
19+
private String getEndpoint() {
20+
return StemmingDictionaries.RESOURCE_PATH + "/" + URLEncoding.encodeURIComponent(this.dictionaryId);
21+
}
22+
23+
}

src/test/java/org/typesense/api/Helper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.typesense.model.SearchOverrideRule;
2626
import org.typesense.model.SearchOverrideSchema;
2727
import org.typesense.model.SearchSynonymSchema;
28+
import org.typesense.model.StemmingDictionaryWords;
2829
import org.typesense.model.StopwordsSetSchema;
2930
import org.typesense.model.StopwordsSetUpsertSchema;
3031
import org.typesense.model.StopwordsSetsRetrieveAllSchema;
@@ -140,6 +141,16 @@ public void createTestStopwordsSet() throws Exception {
140141
client.stopwords().upsert("common-words", stopwordsSetSchema);
141142
}
142143

144+
145+
public void createStemmingDictionary() throws Exception{
146+
List<StemmingDictionaryWords> stemmingDictionaryWords = new ArrayList<>();
147+
148+
stemmingDictionaryWords.add(new StemmingDictionaryWords().word("ran").root("run"));
149+
stemmingDictionaryWords.add(new StemmingDictionaryWords().word("running").root("run"));
150+
151+
client.stemming().dictionaries().upsert("irregular-plurals", stemmingDictionaryWords);
152+
}
153+
143154
public void teardown() throws Exception {
144155
CollectionResponse[] collectionResponses = client.collections().retrieve();
145156
for (CollectionResponse c : collectionResponses) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.typesense.api;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
import org.typesense.model.StemmingDictionary;
10+
import org.typesense.model.StemmingDictionaryWords;
11+
12+
public class StemmingTest {
13+
private Client client;
14+
private Helper helper;
15+
16+
@BeforeEach
17+
void setUp() throws Exception {
18+
helper = new Helper();
19+
helper.teardown();
20+
client = helper.getClient();
21+
helper.createStemmingDictionary();
22+
}
23+
24+
@Test
25+
void testUpsert() throws Exception {
26+
List<StemmingDictionaryWords> stemmingDictionaryWords = new ArrayList<>();
27+
28+
stemmingDictionaryWords.add(new StemmingDictionaryWords().word("ran").root("run"));
29+
stemmingDictionaryWords.add(new StemmingDictionaryWords().word("running").root("run"));
30+
31+
List<StemmingDictionaryWords> res = client.stemming().dictionaries().upsert("irregular-plurals",
32+
stemmingDictionaryWords);
33+
34+
assertEquals(2, res.size());
35+
assertEquals("ran", res.get(0).getWord());
36+
assertEquals("run", res.get(0).getRoot());
37+
assertEquals("running", res.get(1).getWord());
38+
assertEquals("run", res.get(1).getRoot());
39+
}
40+
41+
@Test
42+
void testRetrieveOne() throws Exception {
43+
StemmingDictionary res = client.stemming().dictionaries("irregular-plurals").retrieve();
44+
assertEquals("irregular-plurals", res.getId());
45+
assertEquals(2, res.getWords().size());
46+
}
47+
48+
@Test
49+
void testRetrieveAll() throws Exception {
50+
StemmingDictionariesRetrieveSchema res = client.stemming().dictionaries().retrieve();
51+
assertEquals(1, res.getDictionaries().size());
52+
assertEquals("irregular-plurals", res.getDictionaries().get(0));
53+
}
54+
}

0 commit comments

Comments
 (0)