Skip to content

Commit 7c61f5a

Browse files
authored
Merge branch 'main' into feat/sort-facet-values
2 parents 8a78b19 + 49919bd commit 7c61f5a

File tree

10 files changed

+242
-3
lines changed

10 files changed

+242
-3
lines changed

.code-samples.meilisearch.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,9 @@ search_parameter_guide_matching_strategy_2: |-
394394
search_parameter_guide_attributes_to_search_on_1: |-
395395
SearchRequest searchRequest = SearchRequest.builder().q("adventure").attributesToSearchOn(new String[] {"overview"});
396396
client.index("movies").searchRequest(searchRequest);
397+
search_parameter_guide_show_ranking_score_1: |-
398+
SearchRequest searchRequest = SearchRequest.builder().q("dragon").showRankingScore(true).build();
399+
client.index("movies").search(searchRequest);
397400
synonyms_guide_1: |-
398401
HashMap<String, String[]> synonyms = new HashMap<String, String[]>();
399402
synonyms.put("great", new String[] {"fantastic"});
@@ -636,12 +639,18 @@ update_sortable_attributes_1: |-
636639
client.index("books").updateSortableAttributesSettings(new String[] {"price", "author"});
637640
reset_sortable_attributes_1: |-
638641
client.index("books").resetSortableAttributesSettings();
642+
facet_search_1: |-
643+
FacetSearchRequest fsr = FacetSearchRequest.builder().facetName("genres").facetQuery("fiction").filter(new String[]{"rating > 3"}).build();
644+
client.index("books").facetSearch(fsr);
639645
facet_search_2: |-
640646
Faceting newFaceting = new Faceting();
641647
HashMap<String, FacetSortValue> facetSortValues = new HashMap<>();
642648
facetSortValues.put("genres", FacetSortValue.COUNT);
643649
newFaceting.setSortFacetValuesBy(facetSortValues);
644650
client.index("books").updateFacetingSettings(newFaceting);
651+
facet_search_3: |-
652+
FacetSearchRequest fsr = FacetSearchRequest.builder().facetName("genres").facetQuery("c").build();
653+
client.index("books").facetSearch(fsr);
645654
search_parameter_guide_sort_1: |-
646655
SearchRequest searchRequest = SearchRequest.builder().q("science fiction").sort(new String[] {"price:asc"}).build();
647656
client.index("search_parameter_guide_sort_1").search(searchRequest);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.meilisearch.sdk;
2+
3+
import com.meilisearch.sdk.exceptions.MeilisearchException;
4+
import com.meilisearch.sdk.model.FacetSearchResult;
5+
import com.meilisearch.sdk.model.FacetSearchable;
6+
7+
/**
8+
* Class used for performing facet searching on Meilisearch indexes
9+
*
10+
* @see <a href="https://www.meilisearch.com/docs/reference/api/facet_search">API specification</a>
11+
*/
12+
public class FacetSearch {
13+
private final HttpClient httpClient;
14+
15+
/**
16+
* Constructor for the Meilisearch Facet Search object
17+
*
18+
* @param config Meilisearch configuration
19+
*/
20+
protected FacetSearch(Config config) {
21+
httpClient = config.httpClient;
22+
}
23+
24+
/**
25+
* Performs a facet search on a given index with a given query
26+
*
27+
* @param uid Index identifier
28+
* @param fsr FacetSearchRequest to search on index
29+
* @return search results, as raw data
30+
* @throws MeilisearchException Search Exception or Client Error
31+
*/
32+
String rawSearch(String uid, FacetSearchRequest fsr) throws MeilisearchException {
33+
String requestQuery = "/indexes/" + uid + "/facet-search";
34+
if (fsr.getFacetName() == null) {
35+
throw new MeilisearchException("Facet name is required for a facet search");
36+
}
37+
return httpClient.post(requestQuery, fsr.toString(), String.class);
38+
}
39+
40+
FacetSearchable facetSearch(String uid, FacetSearchRequest fsr) throws MeilisearchException {
41+
return httpClient.jsonHandler.decode(rawSearch(uid, fsr), FacetSearchResult.class);
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.meilisearch.sdk;
2+
3+
import com.meilisearch.sdk.model.MatchingStrategy;
4+
import lombok.AccessLevel;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
import lombok.Setter;
10+
import lombok.experimental.Accessors;
11+
import org.json.JSONObject;
12+
13+
/** Facet search request query string builder */
14+
@Builder
15+
@AllArgsConstructor(access = AccessLevel.PACKAGE)
16+
@NoArgsConstructor(access = AccessLevel.PACKAGE)
17+
@Getter
18+
@Setter
19+
@Accessors(chain = true)
20+
public class FacetSearchRequest {
21+
private String facetName;
22+
private String facetQuery;
23+
private String q;
24+
private MatchingStrategy matchingStrategy;
25+
private String[] attributesToSearchOn;
26+
private String[] filter;
27+
private String[][] filterArray;
28+
29+
/**
30+
* Constructor for FacetSearchRequest for building facet search queries with the default values:
31+
* facetQuery: null, query: null, matchingStrategy: null, attributesToSearchOn: null, filter:
32+
* null
33+
*
34+
* @param facetName FacetName String
35+
*/
36+
public FacetSearchRequest(String facetName) {
37+
this();
38+
this.facetName = facetName;
39+
}
40+
41+
/**
42+
* Method to set the Query String
43+
*
44+
* <p>This method is an alias of setQ()
45+
*
46+
* @param q Query String
47+
* @return SearchRequest
48+
*/
49+
public FacetSearchRequest setQuery(String q) {
50+
return setQ(q);
51+
}
52+
53+
/**
54+
* Method that returns the JSON String of the SearchRequest
55+
*
56+
* @return JSON String of the SearchRequest query
57+
*/
58+
@Override
59+
public String toString() {
60+
JSONObject jsonObject =
61+
new JSONObject()
62+
.put("facetName", this.facetName)
63+
.put("facetQuery", this.facetQuery)
64+
.put("q", this.q)
65+
.put(
66+
"matchingStrategy",
67+
this.matchingStrategy == null
68+
? null
69+
: this.matchingStrategy.toString())
70+
.putOpt("attributesToSearchOn", this.attributesToSearchOn)
71+
.putOpt("filter", this.filter)
72+
.putOpt("filter", this.filterArray);
73+
74+
return jsonObject.toString();
75+
}
76+
}

src/main/java/com/meilisearch/sdk/Index.java

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
package com.meilisearch.sdk;
22

33
import com.meilisearch.sdk.exceptions.MeilisearchException;
4-
import com.meilisearch.sdk.model.*;
4+
import com.meilisearch.sdk.model.DocumentQuery;
5+
import com.meilisearch.sdk.model.DocumentsQuery;
6+
import com.meilisearch.sdk.model.FacetSearchable;
7+
import com.meilisearch.sdk.model.Faceting;
8+
import com.meilisearch.sdk.model.IndexStats;
9+
import com.meilisearch.sdk.model.Pagination;
10+
import com.meilisearch.sdk.model.Results;
11+
import com.meilisearch.sdk.model.SearchResult;
12+
import com.meilisearch.sdk.model.Searchable;
13+
import com.meilisearch.sdk.model.Settings;
14+
import com.meilisearch.sdk.model.Task;
15+
import com.meilisearch.sdk.model.TaskInfo;
16+
import com.meilisearch.sdk.model.TasksQuery;
17+
import com.meilisearch.sdk.model.TasksResults;
18+
import com.meilisearch.sdk.model.TypoTolerance;
519
import java.io.Serializable;
620
import java.util.ArrayList;
721
import java.util.List;
@@ -23,6 +37,7 @@ public class Index implements Serializable {
2337
@ToString.Exclude protected transient Documents documents;
2438
@ToString.Exclude protected transient TasksHandler tasksHandler;
2539
@ToString.Exclude protected transient Search search;
40+
@ToString.Exclude protected transient FacetSearch facetSearch;
2641
@ToString.Exclude protected transient SettingsHandler settingsHandler;
2742
@ToString.Exclude protected transient InstanceHandler instanceHandler;
2843

@@ -36,6 +51,7 @@ void setConfig(Config config) {
3651
this.documents = new Documents(config);
3752
this.tasksHandler = new TasksHandler(config);
3853
this.search = new Search(config);
54+
this.facetSearch = new FacetSearch(config);
3955
this.settingsHandler = new SettingsHandler(config);
4056
this.instanceHandler = new InstanceHandler(config);
4157
}
@@ -411,6 +427,32 @@ public Searchable search(SearchRequest searchRequest) throws MeilisearchExceptio
411427
return this.search.search(this.uid, searchRequest);
412428
}
413429

430+
/**
431+
* Performs a Facet Search in the index
432+
*
433+
* <p>Ensure that FacetName is set in the FacetSearchRequest and note that facet search requires
434+
* attributes to the filterableAttributes list.
435+
*
436+
* @param facetSearchRequest FacetSearchRequest FacetSearchRequest
437+
* @return Meilisearch API response
438+
* @throws MeilisearchException if an error occurs
439+
* @see <a
440+
* href="https://www.meilisearch.com/docs/reference/api/facet_search#perform-a-facet-search">API
441+
* specification</a>
442+
* @see Index#getFilterableAttributesSettings() getFilterableAttributesSettings
443+
* @see Index#updateFilterableAttributesSettings(String[]) updateFilterableAttributesSettings
444+
* @since 1.3
445+
*/
446+
public FacetSearchable facetSearch(FacetSearchRequest facetSearchRequest)
447+
throws MeilisearchException {
448+
return this.facetSearch.facetSearch(this.uid, facetSearchRequest);
449+
}
450+
451+
public String rawFacetSearch(FacetSearchRequest facetSearchRequest)
452+
throws MeilisearchException {
453+
return this.facetSearch.rawSearch(this.uid, facetSearchRequest);
454+
}
455+
414456
public String rawSearch(String query) throws MeilisearchException {
415457
return this.search.rawSearch(this.uid, query);
416458
}

src/main/java/com/meilisearch/sdk/SearchRequest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class SearchRequest {
3737
private String[] sort;
3838
protected Integer page;
3939
protected Integer hitsPerPage;
40+
protected Boolean showRankingScore;
4041

4142
/**
4243
* Constructor for SearchRequest for building search queries with the default values: offset: 0,
@@ -94,7 +95,8 @@ public String toString() {
9495
.putOpt("attributesToHighlight", this.attributesToHighlight)
9596
.putOpt("attributesToSearchOn", this.attributesToSearchOn)
9697
.putOpt("filter", this.filter)
97-
.putOpt("filter", this.filterArray);
98+
.putOpt("filter", this.filterArray)
99+
.putOpt("showRankingScore", this.showRankingScore);
98100

99101
return jsonObject.toString();
100102
}

src/main/java/com/meilisearch/sdk/TasksHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ void waitForTask(int taskUid, int timeoutInMs, int intervalInMs) throws Meilisea
143143
long elapsedTime = 0;
144144

145145
while (status == null
146-
|| (!status.equals(TaskStatus.SUCCEEDED) && !status.equals(TaskStatus.FAILED))) {
146+
|| (status.equals(TaskStatus.ENQUEUED) || status.equals(TaskStatus.PROCESSING))) {
147147
if (elapsedTime >= timeoutInMs) {
148148
throw new MeilisearchTimeoutException();
149149
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.meilisearch.sdk.model;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import lombok.Getter;
6+
import lombok.ToString;
7+
8+
/**
9+
* Meilisearch facet search response data structure
10+
*
11+
* @see <a href="https://www.meilisearch.com/docs/reference/api/facet_search#response">API
12+
* specification</a>
13+
*/
14+
@Getter
15+
@ToString
16+
public class FacetSearchResult implements FacetSearchable {
17+
ArrayList<HashMap<String, Object>> facetHits;
18+
int processingTimeMs;
19+
String facetQuery;
20+
21+
public FacetSearchResult() {}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.meilisearch.sdk.model;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
6+
/**
7+
* Meilisearch facet search response data structure
8+
*
9+
* @see <a href="https://www.meilisearch.com/docs/reference/api/facet_search">API specification</a>
10+
*/
11+
public interface FacetSearchable {
12+
ArrayList<HashMap<String, Object>> getFacetHits();
13+
14+
int getProcessingTimeMs();
15+
16+
String getFacetQuery();
17+
}

src/test/java/com/meilisearch/integration/SearchTest.java

+23
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.hamcrest.MatcherAssert.assertThat;
44
import static org.hamcrest.Matchers.arrayWithSize;
55
import static org.hamcrest.Matchers.equalTo;
6+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
67
import static org.hamcrest.Matchers.hasLength;
78
import static org.hamcrest.Matchers.hasSize;
89
import static org.hamcrest.Matchers.instanceOf;
@@ -319,6 +320,28 @@ public void testSearchWithMatchingStrategy() throws Exception {
319320
assertThat(searchResult.getEstimatedTotalHits(), is(equalTo(21)));
320321
}
321322

323+
/** Test search with show ranking score */
324+
@Test
325+
public void testSearchWithShowRankingScore() throws Exception {
326+
String indexUid = "SearchRankingScore";
327+
Index index = client.index(indexUid);
328+
GsonJsonHandler jsonGson = new GsonJsonHandler();
329+
330+
TestData<Movie> testData = this.getTestData(MOVIES_INDEX, Movie.class);
331+
TaskInfo task = index.addDocuments(testData.getRaw());
332+
333+
index.waitForTask(task.getTaskUid());
334+
335+
SearchRequest searchRequest =
336+
SearchRequest.builder().q("and").showRankingScore(true).build();
337+
338+
Results resGson = jsonGson.decode(index.rawSearch(searchRequest), Results.class);
339+
340+
assertThat(resGson.hits, is(arrayWithSize(20)));
341+
assertThat(resGson.hits[0].getRankingScore(), instanceOf(Double.class));
342+
assertThat(resGson.hits[0].getRankingScore(), is(greaterThanOrEqualTo(0.0)));
343+
}
344+
322345
/** Test search with phrase */
323346
@Test
324347
public void testSearchPhrase() throws Exception {

src/test/java/com/meilisearch/sdk/utils/Movie.java

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class Movie {
1313
private String[] genres;
1414
private Movie _formatted;
1515
private HashMap<String, List<Match>> _matchesPosition;
16+
private Double _rankingScore;
1617

1718
public class Match {
1819
public int start;
@@ -119,6 +120,10 @@ public HashMap<String, List<Match>> getMatchesInfo() {
119120
return _matchesPosition;
120121
}
121122

123+
public Double getRankingScore() {
124+
return _rankingScore;
125+
}
126+
122127
public Movie setMatchesInfo(HashMap<String, List<Match>> _matchesPosition) {
123128
this._matchesPosition = _matchesPosition;
124129
return this;

0 commit comments

Comments
 (0)