-
Notifications
You must be signed in to change notification settings - Fork 127
Add backwards-compatible serialization for filtration #840
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
29eed5c
06d8fe8
116f3bd
d2ebd25
b836b8a
ed6e3fc
b48c449
ce8e592
29c4bad
30d27e4
5279c41
b60a5f3
20f563d
912d5e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
import com.meilisearch.sdk.model.*; | ||
import java.io.Serializable; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Map; | ||
import lombok.Getter; | ||
|
@@ -444,7 +445,7 @@ public Searchable search(SearchRequest searchRequest) throws MeilisearchExceptio | |
* href="https://www.meilisearch.com/docs/reference/api/facet_search#perform-a-facet-search">API | ||
* specification</a> | ||
* @see Index#getFilterableAttributesSettings() getFilterableAttributesSettings | ||
* @see Index#updateFilterableAttributesSettings(String[]) updateFilterableAttributesSettings | ||
* @see Index#updateFilterableAttributesSettings(Object[]) updateFilterableAttributesSettings | ||
* @since 1.3 | ||
*/ | ||
public FacetSearchable facetSearch(FacetSearchRequest facetSearchRequest) | ||
|
@@ -747,24 +748,67 @@ public TaskInfo resetLocalizedAttributesSettings() throws MeilisearchException { | |
* specification</a> | ||
*/ | ||
public String[] getFilterableAttributesSettings() throws MeilisearchException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note for myself: Ensure this will be able to handle both old and new styles |
||
FilterableAttribute[] attributes = | ||
this.settingsHandler.getFilterableAttributesSettings(this.uid); | ||
return Arrays.stream(this.settingsHandler.getFilterableAttributesSettings(this.uid)) | ||
.reduce( | ||
new ArrayList<String>(), | ||
(list, next) -> { | ||
list.addAll(Arrays.asList(next.getPatterns())); | ||
return list; | ||
}, | ||
(a, b) -> { | ||
a.addAll(b); | ||
return a; | ||
}) | ||
.toArray(new String[0]); | ||
} | ||
|
||
/** | ||
* Gets the filterable attributes of the index, along with its filtration metadata. | ||
* | ||
* @return filterable attributes of a given uid as FilterableAttribute | ||
* @throws MeilisearchException if an error occurs | ||
* @see <a | ||
* href="https://meilisearch.notion.site/API-usage-Settings-to-opt-out-indexing-features-filterableAttributes-1764b06b651f80aba8bdf359b2df3ca8?pvs=74">API | ||
* Specification</a> | ||
*/ | ||
public FilterableAttribute[] getFullFilterableAttributesSettings() throws MeilisearchException { | ||
return this.settingsHandler.getFilterableAttributesSettings(this.uid); | ||
} | ||
|
||
/** | ||
* Updates the filterable attributes of the index. This will re-index all documents in the index | ||
* Generic getFilterableAttributesSettings. Updates the filterable attributes of the index. This | ||
* will re-index all documents in the index | ||
* | ||
* @param filterableAttributes An array of strings containing the attributes that can be used as | ||
* filters at query time. | ||
* @param filterableAttributes An array of FilterableAttributes or Strings containing the | ||
* attributes that can be used as filters at query time. | ||
* @return TaskInfo instance | ||
* @throws MeilisearchException if an error occurs | ||
* @throws MeilisearchException if an error occurs in the que | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "in the que" 😬 |
||
* @see <a | ||
* href="https://www.meilisearch.com/docs/reference/api/settings#update-filterable-attributes">API | ||
* specification</a> | ||
*/ | ||
public TaskInfo updateFilterableAttributesSettings(String[] filterableAttributes) | ||
public <O> TaskInfo updateFilterableAttributesSettings(O[] filterableAttributes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wouldn't be possible to define two methods with the same name but with just different params, so we would have an overloaded version with |
||
throws MeilisearchException { | ||
if (filterableAttributes == null) | ||
return this.settingsHandler.updateFilterableAttributesSettings(this.uid, null); | ||
else if (filterableAttributes.getClass().getComponentType() == FilterableAttribute.class) | ||
return this.settingsHandler.updateFilterableAttributesSettings( | ||
this.uid, (FilterableAttribute[]) filterableAttributes); | ||
else if (filterableAttributes.getClass().getComponentType() == String.class) | ||
return this.updateFilterableAttributeSettingsLegacy((String[]) filterableAttributes); | ||
else | ||
throw new MeilisearchException( | ||
"filterableAttributes must be of type String or FilterableAttribute"); | ||
} | ||
|
||
private TaskInfo updateFilterableAttributeSettingsLegacy(String[] filterableAttributes) { | ||
return this.settingsHandler.updateFilterableAttributesSettings( | ||
this.uid, filterableAttributes); | ||
this.uid, | ||
Arrays.stream(filterableAttributes) | ||
.map(FilterableAttribute::new) | ||
.toArray(FilterableAttribute[]::new)); | ||
} | ||
Noah-Mack-01 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,7 @@ | |
|
||
import com.meilisearch.sdk.exceptions.MeilisearchException; | ||
import com.meilisearch.sdk.http.URLBuilder; | ||
import com.meilisearch.sdk.model.Faceting; | ||
import com.meilisearch.sdk.model.LocalizedAttribute; | ||
import com.meilisearch.sdk.model.Pagination; | ||
import com.meilisearch.sdk.model.Settings; | ||
import com.meilisearch.sdk.model.TaskInfo; | ||
import com.meilisearch.sdk.model.TypoTolerance; | ||
import com.meilisearch.sdk.model.*; | ||
import java.util.Map; | ||
|
||
/** | ||
|
@@ -318,9 +313,10 @@ TaskInfo resetLocalizedAttributesSettings(String uid) throws MeilisearchExceptio | |
* @return an array of strings that contains the filterable attributes settings | ||
* @throws MeilisearchException if an error occurs | ||
*/ | ||
String[] getFilterableAttributesSettings(String uid) throws MeilisearchException { | ||
FilterableAttribute[] getFilterableAttributesSettings(String uid) throws MeilisearchException { | ||
return httpClient.get( | ||
settingsPath(uid).addSubroute("filterable-attributes").getURL(), String[].class); | ||
settingsPath(uid).addSubroute("filterable-attributes").getURL(), | ||
FilterableAttribute[].class); | ||
} | ||
|
||
/** | ||
|
@@ -332,13 +328,11 @@ String[] getFilterableAttributesSettings(String uid) throws MeilisearchException | |
* @return TaskInfo instance | ||
* @throws MeilisearchException if an error occurs | ||
*/ | ||
TaskInfo updateFilterableAttributesSettings(String uid, String[] filterableAttributes) | ||
throws MeilisearchException { | ||
TaskInfo updateFilterableAttributesSettings( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like I suggested before, do you think it would be possible to have an overload here as well? |
||
String uid, FilterableAttribute[] filterableAttributes) throws MeilisearchException { | ||
return httpClient.put( | ||
settingsPath(uid).addSubroute("filterable-attributes").getURL(), | ||
filterableAttributes == null | ||
? httpClient.jsonHandler.encode(filterableAttributes) | ||
: filterableAttributes, | ||
httpClient.jsonHandler.encode(filterableAttributes), | ||
TaskInfo.class); | ||
Comment on lines
+331
to
336
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainUpdates update method parameter type and simplifies encoding Changed the parameter type from Let's verify how the Update method is called: 🏁 Script executed: #!/bin/bash
# Find calls to updateFilterableAttributesSettings
rg "updateFilterableAttributesSettings" --type java Length of output: 2055 Breaking Change: UpdateFilterableAttributesSettings Signature Now Requires FilterableAttribute[] The parameter type was changed from Please update the following locations:
No change needed in |
||
} | ||
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we call this |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package com.meilisearch.sdk.json; | ||
|
||
import com.google.gson.*; | ||
import com.meilisearch.sdk.model.FilterableAttribute; | ||
import java.lang.reflect.Type; | ||
import java.util.*; | ||
|
||
/** JSON serializer/deserializer for {@link FilterableAttribute} objects. */ | ||
public class GsonFilterableAttributeSerializer | ||
implements JsonSerializer<FilterableAttribute>, JsonDeserializer<FilterableAttribute> { | ||
|
||
@Override | ||
public JsonElement serialize( | ||
FilterableAttribute attributes, | ||
Type type, | ||
JsonSerializationContext jsonSerializationContext) { | ||
// when possible, limit size of data sent by using legacy string format | ||
return (canBeString(attributes)) | ||
? new JsonPrimitive(attributes.getPatterns()[0]) | ||
: serializeAttribute(attributes); | ||
} | ||
|
||
private boolean canBeString(FilterableAttribute attribute) { | ||
if (attribute == null) return false; | ||
Map<String, Boolean> filters = attribute.getFilter(); | ||
if (filters == null) filters = new HashMap<>(); | ||
|
||
boolean equalityAllowed = !filters.containsKey("equality") || filters.get("equality"); | ||
boolean comparisonAllowed = filters.getOrDefault("comparison", false); | ||
|
||
return attribute.getPatterns() != null | ||
&& attribute.getPatterns().length == 1 | ||
&& (attribute.getFacetSearch() == null || !attribute.getFacetSearch()) | ||
&& equalityAllowed | ||
&& !comparisonAllowed; | ||
} | ||
|
||
private JsonElement serializeAttribute(FilterableAttribute attribute) { | ||
if (attribute == null) return null; | ||
List<Exception> exceptions = new ArrayList<>(); | ||
JsonArray patternArray = new JsonArray(); | ||
if (attribute.getPatterns() != null && attribute.getPatterns().length > 0) | ||
try { | ||
// Collect values from POJO | ||
patternArray = | ||
Arrays.stream(attribute.getPatterns()) | ||
.map(JsonPrimitive::new) | ||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll); | ||
} catch (Exception e) { | ||
exceptions.add(e); | ||
} | ||
else exceptions.add(new JsonParseException("Patterns to filter for were not specified!")); | ||
|
||
JsonObject filters = new JsonObject(); | ||
if (attribute.getFilter() != null) { | ||
try { | ||
filters = | ||
attribute.getFilter().entrySet().stream() | ||
.collect( | ||
JsonObject::new, | ||
(jsonObject, kv) -> | ||
jsonObject.addProperty(kv.getKey(), kv.getValue()), | ||
this::combineJsonObjects); | ||
} catch (Exception e) { | ||
exceptions.add(e); | ||
} | ||
} else { | ||
filters.addProperty("comparison", false); | ||
filters.addProperty("equality", true); | ||
} | ||
|
||
if (!exceptions.isEmpty()) { | ||
throw new JsonParseException(String.join("\n", Arrays.toString(exceptions.toArray()))); | ||
} | ||
|
||
// Create JSON object | ||
JsonObject jsonObject = new JsonObject(); | ||
JsonObject features = new JsonObject(); | ||
if (attribute.getFacetSearch() != null) | ||
features.addProperty("facetSearch", attribute.getFacetSearch()); | ||
else features.addProperty("facetSearch", false); | ||
features.add("filter", filters); | ||
jsonObject.add("attributePatterns", patternArray); | ||
jsonObject.add("features", features); | ||
return jsonObject; | ||
} | ||
|
||
private void combineJsonObjects(JsonObject a, JsonObject b) { | ||
for (Map.Entry<String, JsonElement> kv : b.entrySet()) a.add(kv.getKey(), kv.getValue()); | ||
} | ||
|
||
@Override | ||
public FilterableAttribute deserialize( | ||
JsonElement jsonElement, | ||
Type type, | ||
JsonDeserializationContext jsonDeserializationContext) | ||
throws JsonParseException { | ||
try { | ||
// legacy check | ||
if (jsonElement.isJsonPrimitive() && jsonElement.getAsJsonPrimitive().isString()) | ||
return new FilterableAttribute(jsonElement.getAsString()); | ||
|
||
JsonObject object = jsonElement.getAsJsonObject(); | ||
JsonObject features = | ||
object.has("features") ? object.getAsJsonObject("features") : null; | ||
// default values for instance lacking `features` | ||
boolean facetSearch = false; | ||
Map<String, Boolean> filters = new HashMap<>(); | ||
filters.put("equality", true); | ||
filters.put("comparison", false); | ||
|
||
List<Exception> exceptions = new ArrayList<>(); | ||
// pull values from features. | ||
if (features != null && features.has("facetSearch")) { | ||
try { | ||
JsonPrimitive facetSearchPrimitive = features.getAsJsonPrimitive("facetSearch"); | ||
facetSearch = | ||
facetSearchPrimitive != null && facetSearchPrimitive.getAsBoolean(); | ||
} catch (ClassCastException | IllegalStateException e) { | ||
exceptions.add(e); | ||
} | ||
} | ||
if (features != null && features.has("filter")) | ||
try { | ||
filters = | ||
features.getAsJsonObject("filter").entrySet().stream() | ||
.collect( | ||
HashMap::new, | ||
(m, kv) -> | ||
m.put( | ||
kv.getKey(), | ||
kv.getValue().getAsBoolean()), | ||
HashMap::putAll); | ||
} catch (ClassCastException | IllegalStateException e) { | ||
exceptions.add(e); | ||
} | ||
String[] patterns = new String[0]; | ||
try { | ||
patterns = | ||
object.has("attributePatterns") | ||
? object.getAsJsonArray("attributePatterns").asList().stream() | ||
.map(JsonElement::getAsString) | ||
.toArray(String[]::new) | ||
: new String[0]; | ||
} catch (ClassCastException | IllegalStateException e) { | ||
exceptions.add(e); | ||
} | ||
|
||
if (!exceptions.isEmpty()) | ||
throw new JsonParseException( | ||
String.join("\n", Arrays.toString(exceptions.toArray()))); | ||
|
||
if (filters.entrySet().stream().noneMatch(Map.Entry::getValue)) | ||
exceptions.add( | ||
new JsonParseException( | ||
"No filtration methods were allowed! Must have at least one type <comparison, equality> allowed.\n" | ||
+ Arrays.toString(filters.entrySet().toArray()))); | ||
if (patterns.length == 0) | ||
exceptions.add( | ||
new JsonParseException( | ||
"Patterns to filter for were not specified! Invalid Attribute.")); | ||
|
||
if (!exceptions.isEmpty()) | ||
throw new JsonParseException( | ||
String.join("\n", Arrays.toString(exceptions.toArray()))); | ||
|
||
return new FilterableAttribute(patterns, facetSearch, filters); | ||
} catch (Exception e) { | ||
throw new JsonParseException("Failed to deserialize FilterableAttribute", e); | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix variable name typo in code sample.
The map is declared as
filtersTypes
but the third argument passesfilters
, which doesn’t exist and will cause a compilation error. It should usefiltersTypes
. Consider also renaming the variable tofilterTypes
for clarity.Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you apply this change as the @coderabbitai suggested?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brunoocasali I'll apply the suggested fix for the compilation error in the code sample.
Task: Fix compilation error in filterable attributes code sample
filters
reference to use the correct variable name⏳ Preparing changes...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A summary of the edits CodeRabbit can apply:
A plan of each step that CodeRabbit will take in order to execute the edits for each file (with inline rough draft snippets):
✏️ .code-samples.meilisearch.yaml
Replace the variable declaration on line 230 from
Map<String, Boolean> filtersTypes = new HashMap<>();
toMap<String, Boolean> filterTypes = new HashMap<>();
to improve naming clarity by removing the redundant 's' and making it more readable.Update line 231 to use the new variable name by changing
filtersTypes.put("comparison",true);
tofilterTypes.put("comparison", true);
- also adding proper spacing around the comma for consistency.Update line 232 to use the new variable name by changing
filtersTypes.put("equality",true);
tofilterTypes.put("equality", true);
- also adding proper spacing around the comma for consistency.Fix the compilation error on line 233 by replacing the undefined variable
filters
with the correct variable namefilterTypes
. Change the constructor call fromnew FilterableAttribute(new String[]{"director"}, true, filters)
tonew FilterableAttribute(new String[]{"director"}, true, filterTypes)
.A summary of the context that CodeRabbit is considering across the codebase: