Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit 2de30bb

Browse files
authored
refactor(disjunctive faceting): Fix handling of commas via JSONArray syntax (#545)
refactor(disjunctive faceting): Fix handling of commas via JSONArray syntax
1 parent 1706d8d commit 2de30bb

File tree

2 files changed

+28
-53
lines changed

2 files changed

+28
-53
lines changed

algoliasearch/src/main/java/com/algolia/search/saas/helpers/DisjunctiveFaceting.java

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void requestCompleted(JSONObject content, AlgoliaException error) {
9191
* @param refinements Map representing the current refinements
9292
* @return The disjunctive refinements
9393
*/
94-
static private @NonNull <T extends Collection<String>> Map<String, T> computeDisjunctiveRefinements(@NonNull Collection<String> disjunctiveFacets, @NonNull Map<String, T> refinements)
94+
static private @NonNull <T extends Collection<String>> Map<String, T> filterDisjunctiveRefinements(@NonNull Collection<String> disjunctiveFacets, @NonNull Map<String, T> refinements)
9595
{
9696
Map<String, T> disjunctiveRefinements = new HashMap<>();
9797
for (Map.Entry<String, T> elt : refinements.entrySet()) {
@@ -112,91 +112,63 @@ public void requestCompleted(JSONObject content, AlgoliaException error) {
112112
*/
113113
static private @NonNull <T extends Collection<String>> List<Query> computeDisjunctiveFacetingQueries(@NonNull Query query, @NonNull Collection<String> disjunctiveFacets, @NonNull Map<String, T> refinements) {
114114
// Retain only refinements corresponding to the disjunctive facets.
115-
Map<String, ? extends Collection<String>> disjunctiveRefinements = computeDisjunctiveRefinements(disjunctiveFacets, refinements);
115+
Map<String, ? extends Collection<String>> disjunctiveRefinements = filterDisjunctiveRefinements(disjunctiveFacets, refinements);
116116

117117
// build queries
118-
// TODO: Refactor using JSON array notation: safer and clearer.
119118
List<Query> queries = new ArrayList<>();
120-
// hits + regular facets query
121-
StringBuilder filters = new StringBuilder();
122-
boolean first_global = true;
119+
120+
// first query: hits + regular facets
121+
JSONArray facetFilters = new JSONArray();
123122
for (Map.Entry<String, T> elt : refinements.entrySet()) {
124-
StringBuilder or = new StringBuilder();
125-
or.append("(");
126-
boolean first = true;
123+
JSONArray orFilters = new JSONArray();
124+
127125
for (String val : elt.getValue()) {
126+
// When already refined facet, or with existing refinements
128127
if (disjunctiveRefinements.containsKey(elt.getKey())) {
129-
// disjunctive refinements are ORed
130-
if (!first) {
131-
or.append(',');
132-
}
133-
first = false;
134-
or.append(String.format("%s:%s", elt.getKey(), val));
128+
orFilters.put(formatFilter(elt, val));
135129
} else {
136-
if (!first_global) {
137-
filters.append(',');
138-
}
139-
first_global = false;
140-
filters.append(String.format("%s:%s", elt.getKey(), val));
130+
facetFilters.put(formatFilter(elt, val));
141131
}
142132
}
143133
// Add or
144134
if (disjunctiveRefinements.containsKey(elt.getKey())) {
145-
or.append(')');
146-
if (!first_global) {
147-
filters.append(',');
148-
}
149-
first_global = false;
150-
filters.append(or.toString());
135+
facetFilters.put(orFilters);
151136
}
152137
}
153138

154-
queries.add(new Query(query).set("facetFilters", filters.toString()));
139+
queries.add(new Query(query).setFacetFilters(facetFilters));
155140
// one query per disjunctive facet (use all refinements but the current one + hitsPerPage=1 + single facet
156141
for (String disjunctiveFacet : disjunctiveFacets) {
157-
filters = new StringBuilder();
158-
first_global = true;
142+
facetFilters = new JSONArray();
159143
for (Map.Entry<String, T> elt : refinements.entrySet()) {
160144
if (disjunctiveFacet.equals(elt.getKey())) {
161145
continue;
162146
}
163-
StringBuilder or = new StringBuilder();
164-
or.append("(");
165-
boolean first = true;
147+
JSONArray orFilters = new JSONArray();
166148
for (String val : elt.getValue()) {
167149
if (disjunctiveRefinements.containsKey(elt.getKey())) {
168-
// disjunctive refinements are ORed
169-
if (!first) {
170-
or.append(',');
171-
}
172-
first = false;
173-
or.append(String.format("%s:%s", elt.getKey(), val));
150+
orFilters.put(formatFilter(elt, val));
174151
} else {
175-
if (!first_global) {
176-
filters.append(',');
177-
}
178-
first_global = false;
179-
filters.append(String.format("%s:%s", elt.getKey(), val));
152+
facetFilters.put(formatFilter(elt, val));
180153
}
181154
}
182155
// Add or
183156
if (disjunctiveRefinements.containsKey(elt.getKey())) {
184-
or.append(')');
185-
if (!first_global) {
186-
filters.append(',');
187-
}
188-
first_global = false;
189-
filters.append(or.toString());
157+
facetFilters.put(orFilters);
190158
}
191159
}
192160
String[] facets = new String[]{disjunctiveFacet};
193161
queries.add(new Query(query).setHitsPerPage(0).setAnalytics(false)
194162
.setAttributesToRetrieve().setAttributesToHighlight().setAttributesToSnippet()
195-
.setFacets(facets).set("facetFilters", filters.toString()));
163+
.setFacets(facets).setFacetFilters(facetFilters));
196164
}
197165
return queries;
198166
}
199167

168+
private static <T extends Collection<String>> String formatFilter(Map.Entry<String, T> refinement, String value) {
169+
return String.format("%s:%s", refinement.getKey(), value);
170+
}
171+
200172
/**
201173
* Aggregate results from multiple queries into disjunctive faceting results.
202174
*
@@ -208,7 +180,7 @@ public void requestCompleted(JSONObject content, AlgoliaException error) {
208180
*/
209181
static private <T extends Collection<String>> JSONObject aggregateDisjunctiveFacetingResults(@NonNull JSONObject answers, @NonNull Collection<String> disjunctiveFacets, @NonNull Map<String, T> refinements) throws AlgoliaException
210182
{
211-
Map<String, T> disjunctiveRefinements = computeDisjunctiveRefinements(disjunctiveFacets, refinements);
183+
Map<String, T> disjunctiveRefinements = filterDisjunctiveRefinements(disjunctiveFacets, refinements);
212184

213185
// aggregate answers
214186
// first answer stores the hits + regular facets

algoliasearch/src/test/java/com/algolia/search/saas/IndexTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,28 +210,31 @@ public void searchDisjunctiveFacetingAsync() throws Exception {
210210
objects.add(new JSONObject("{\"name\": \"Platinum Phone Cover\", \"brand\": \"Samsung\", \"category\": \"accessory\",\"stars\":2}"));
211211
objects.add(new JSONObject("{\"name\": \"Lame Phone\", \"brand\": \"Whatever\", \"category\": \"device\",\"stars\":1}"));
212212
objects.add(new JSONObject("{\"name\": \"Lame Phone cover\", \"brand\": \"Whatever\", \"category\": \"accessory\",\"stars\":1}"));
213+
// Testing commas and quotes in facet values
214+
objects.add(new JSONObject("{\"name\": \"Symbols Phone\", \"brand\": \"Commas' voice, Ltd\", \"category\": \"device\",\"stars\":2}"));
213215
JSONObject task = index.addObjects(new JSONArray(objects), /* requestOptions: */ null);
214216
index.waitTask(task.getString("taskID"));
215217

216218
final Query query = new Query("phone").setFacets("brand", "category", "stars");
217219
final List<String> disjunctiveFacets = Collections.singletonList("brand");
218220
final Map<String, List<String>> refinements = new HashMap<>();
219-
refinements.put("brand", Arrays.asList("Apple", "Samsung")); // disjunctive facet
221+
refinements.put("brand", Arrays.asList("Apple", "Samsung", "Commas' voice, Ltd")); // disjunctive facet
220222
refinements.put("category", Collections.singletonList("device")); // conjunctive facet
221223
index.searchDisjunctiveFacetingAsync(query, disjunctiveFacets, refinements, new AssertCompletionHandler() {
222224
@Override
223225
public void doRequestCompleted(JSONObject content, AlgoliaException error) {
224226
if (error != null) {
225227
fail(error.getMessage());
226228
} else {
227-
assertEquals(3, content.optInt("nbHits"));
229+
assertEquals(4, content.optInt("nbHits"));
228230
JSONObject disjunctiveFacetsResult = content.optJSONObject("disjunctiveFacets");
229231
assertNotNull(disjunctiveFacetsResult);
230232
JSONObject brandFacetCounts = disjunctiveFacetsResult.optJSONObject("brand");
231233
assertNotNull(brandFacetCounts);
232234
assertEquals(2, brandFacetCounts.optInt("Apple"));
233235
assertEquals(1, brandFacetCounts.optInt("Samsung"));
234236
assertEquals(1, brandFacetCounts.optInt("Whatever"));
237+
assertEquals(1, brandFacetCounts.optInt("Commas' voice, Ltd"));
235238
}
236239
}
237240
});

0 commit comments

Comments
 (0)