From 46c1201ffc14ad90f9261959485953eb58226315 Mon Sep 17 00:00:00 2001 From: Hung Date: Mon, 27 Nov 2023 16:48:53 +0800 Subject: [PATCH] add product search autocomplete --- .../visearch/android/ProductSearch.java | 10 +- .../android/model/AutoCompleteResponse.java | 96 +++++++++++++ .../android/model/AutoCompleteResultItem.java | 28 ++++ .../visearch/android/model/ErrorData.java | 7 +- .../android/network/APIProductService.java | 10 ++ .../android/network/ProductSearchService.java | 134 ++++++++++++++---- 6 files changed, 251 insertions(+), 34 deletions(-) create mode 100644 visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResponse.java create mode 100644 visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResultItem.java diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/ProductSearch.java b/visearch-android/src/main/java/com/visenze/visearch/android/ProductSearch.java index 2d4dc82..e5e61c3 100644 --- a/visearch-android/src/main/java/com/visenze/visearch/android/ProductSearch.java +++ b/visearch-android/src/main/java/com/visenze/visearch/android/ProductSearch.java @@ -6,6 +6,7 @@ import com.visenze.datatracking.Tracker; import com.visenze.datatracking.VisenzeAnalytics; import com.visenze.datatracking.data.DataCollection; +import com.visenze.visearch.android.model.AutoCompleteResponse; import com.visenze.visearch.android.model.ErrorData; import com.visenze.visearch.android.model.ProductResponse; import com.visenze.visearch.android.network.ProductSearchService; @@ -39,7 +40,7 @@ public void searchById(ProductSearchByIdParams visualSimilarParams, ResultListen productSearchService.searchById(visualSimilarParams, listener); } - public void multisearcch(ProductSearchByImageParams imageSearchParams, ResultListener listener) { + public void multisearch(ProductSearchByImageParams imageSearchParams, ResultListener listener) { addAnalyticsParams(imageSearchParams); productSearchService.searchByImage(imageSearchParams, listener, true); } @@ -165,9 +166,12 @@ public ProductSearch build(Context context) { } } - public static interface ResultListener { - public void onSearchResult(final ProductResponse response, ErrorData error); + public interface ResultListener { + void onSearchResult(final ProductResponse response, ErrorData error); + } + public interface AutoCompleteResultListener { + void onResult(final AutoCompleteResponse response, ErrorData error); } } diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResponse.java b/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResponse.java new file mode 100644 index 0000000..b85a2f3 --- /dev/null +++ b/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResponse.java @@ -0,0 +1,96 @@ +package com.visenze.visearch.android.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class AutoCompleteResponse { + + @SerializedName("status") + private String status; + + @SerializedName("method") + private String method; + + @SerializedName("error") + private ErrorData error; + + @SerializedName("result") + private List result; + + @SerializedName("page") + private int page; + + @SerializedName("limit") + private int limit; + + @SerializedName("total") + private int total; + + @SerializedName("reqid") + private String reqId; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public ErrorData getError() { + return error; + } + + public void setError(ErrorData error) { + this.error = error; + } + + public List getResult() { + return result; + } + + public void setResult(List result) { + this.result = result; + } + + public int getPage() { + return page; + } + + public void setPage(int page) { + this.page = page; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public String getReqId() { + return reqId; + } + + public void setReqId(String reqId) { + this.reqId = reqId; + } +} diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResultItem.java b/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResultItem.java new file mode 100644 index 0000000..8968263 --- /dev/null +++ b/visearch-android/src/main/java/com/visenze/visearch/android/model/AutoCompleteResultItem.java @@ -0,0 +1,28 @@ +package com.visenze.visearch.android.model; + +import com.google.gson.annotations.SerializedName; + +public class AutoCompleteResultItem { + + @SerializedName("text") + private String text; + + @SerializedName("score") + private double score; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public double getScore() { + return score; + } + + public void setScore(double score) { + this.score = score; + } +} diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/model/ErrorData.java b/visearch-android/src/main/java/com/visenze/visearch/android/model/ErrorData.java index 3b4e2a0..a76e275 100644 --- a/visearch-android/src/main/java/com/visenze/visearch/android/model/ErrorData.java +++ b/visearch-android/src/main/java/com/visenze/visearch/android/model/ErrorData.java @@ -27,5 +27,10 @@ public void setMessage(String message) { this.message = message; } - + public static ErrorData unknownError(String msg) { + ErrorData error = new ErrorData(); + error.setMessage(msg); + error.setCode(-1); + return error; + } } diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/network/APIProductService.java b/visearch-android/src/main/java/com/visenze/visearch/android/network/APIProductService.java index b4c1222..1db80c8 100644 --- a/visearch-android/src/main/java/com/visenze/visearch/android/network/APIProductService.java +++ b/visearch-android/src/main/java/com/visenze/visearch/android/network/APIProductService.java @@ -1,5 +1,6 @@ package com.visenze.visearch.android.network; +import com.visenze.visearch.android.model.AutoCompleteResponse; import com.visenze.visearch.android.model.ProductResponse; import com.visenze.visearch.android.network.retry.Retry; @@ -36,4 +37,13 @@ public interface APIProductService { @Multipart @POST("product/multisearch") Call multisearch(@Part MultipartBody.Part image, @QueryMap RetrofitQueryMap query); + + @Retry + @POST("product/multisearch/autocomplete") + Call multisearchAutocomplete(@QueryMap RetrofitQueryMap query); + + @Retry + @Multipart + @POST("product/multisearch/autocomplete") + Call multisearchAutocomplete(@Part MultipartBody.Part image, @QueryMap RetrofitQueryMap query); } diff --git a/visearch-android/src/main/java/com/visenze/visearch/android/network/ProductSearchService.java b/visearch-android/src/main/java/com/visenze/visearch/android/network/ProductSearchService.java index 381d1d0..8a71d64 100644 --- a/visearch-android/src/main/java/com/visenze/visearch/android/network/ProductSearchService.java +++ b/visearch-android/src/main/java/com/visenze/visearch/android/network/ProductSearchService.java @@ -5,6 +5,7 @@ import com.visenze.visearch.android.ProductSearchByImageParams; import com.visenze.visearch.android.ProductSearch; import com.visenze.visearch.android.ProductSearchByIdParams; +import com.visenze.visearch.android.model.AutoCompleteResponse; import com.visenze.visearch.android.model.ErrorData; import com.visenze.visearch.android.model.ProductResponse; @@ -44,6 +45,46 @@ public void searchByImage(ProductSearchByImageParams imageSearchParams, final Pr } public void searchByImage(ProductSearchByImageParams imageSearchParams, final ProductSearch.ResultListener listener, boolean multiSearch) { + byte[] imageBytes = validateImageParams(imageSearchParams, multiSearch); + + RetrofitQueryMap params = buildQueryMap(imageSearchParams); + + Call call; + if(imageBytes != null) { + RequestBody imageBody = RequestBody.create(MediaType.parse("image/*"), imageBytes); + MultipartBody.Part image = MultipartBody.Part.createFormData("image", "image", imageBody); + call = getProductResponseCall(params, image, multiSearch); + } else { + call = getProductResponseCall(params, null, multiSearch); + } + handleCallback(call, listener); + } + + public void multisearchAutocomplete(ProductSearchByImageParams imageSearchParams, + final ProductSearch.AutoCompleteResultListener listener) { + byte[] imageBytes = validateImageParams(imageSearchParams, true); + + RetrofitQueryMap params = buildQueryMap(imageSearchParams); + + Call call; + if(imageBytes != null) { + RequestBody imageBody = RequestBody.create(MediaType.parse("image/*"), imageBytes); + MultipartBody.Part image = MultipartBody.Part.createFormData("image", "image", imageBody); + call = getAutoCompleteResponseCall(params, image); + } else { + call = getAutoCompleteResponseCall(params, null); + } + handleCallback(call, listener); + } + + /** + * If not multi-search, 1 of image, im_url or im_id must be provided, throw Exception if missing + * + * @param imageSearchParams request params + * @param multiSearch whether this is multisearch related API or normal SBI + * @return image file bytes if provided + */ + private byte[] validateImageParams(ProductSearchByImageParams imageSearchParams, boolean multiSearch) { byte[] imageBytes = null; if (imageSearchParams.getImage() != null) { @@ -59,18 +100,7 @@ public void searchByImage(ProductSearchByImageParams imageSearchParams, final Pr throw new IllegalArgumentException("Please provide imUrl , imId or image parameter"); } } - - RetrofitQueryMap params = buildQueryMap(imageSearchParams); - - Call call; - if(imageBytes != null) { - RequestBody imageBody = RequestBody.create(MediaType.parse("image/*"), imageBytes); - MultipartBody.Part image = MultipartBody.Part.createFormData("image", "image", imageBody); - call = getProductResponseCall(params, image, multiSearch); - } else { - call = getProductResponseCall(params, null, multiSearch); - } - handleCallback(call, listener); + return imageBytes; } private Call getProductResponseCall(RetrofitQueryMap params, MultipartBody.Part image, boolean multiSearch) { @@ -89,6 +119,14 @@ private Call getProductResponseCall(RetrofitQueryMap params, Mu } } + private Call getAutoCompleteResponseCall(RetrofitQueryMap params, MultipartBody.Part image) { + if (image == null) { + return apiService.multisearchAutocomplete(params); + } + + return apiService.multisearchAutocomplete(image, params); + } + private RetrofitQueryMap buildQueryMap(BaseProductSearchParams params) { RetrofitQueryMap map = params.getQueryMap(); @@ -101,32 +139,28 @@ private void handleCallback(Call call, final ProductSearch.Resu call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - if(response.isSuccessful() && response.body() !=null) { + if(response.isSuccessful() && response.body() != null) { ProductResponse data = response.body(); handleResponse(data, resultListener); - } else { - if (response.errorBody() != null) { - Gson gson = new Gson(); - ProductResponse resp = gson.fromJson(response.errorBody().charStream(), ProductResponse.class); - if (resp != null && resp.getError()!= null) { - resultListener.onSearchResult(null, resp.getError()); - return; - } + return; + } + + if (response.errorBody() != null) { + Gson gson = new Gson(); + ProductResponse resp = gson.fromJson(response.errorBody().charStream(), ProductResponse.class); + if (resp != null && resp.getError() != null) { + resultListener.onSearchResult(null, resp.getError()); + return; } - ErrorData error = new ErrorData(); - error.setMessage("api failed"); - error.setCode(-1); - resultListener.onSearchResult(null, error); } + + resultListener.onSearchResult(null, ErrorData.unknownError("api failed")); + } @Override public void onFailure(Call call, Throwable t) { - - ErrorData error = new ErrorData(); - error.setMessage(t.getMessage()); - error.setCode(-1); - resultListener.onSearchResult(null, error); + resultListener.onSearchResult(null, ErrorData.unknownError(t.getMessage())); } }); @@ -142,4 +176,44 @@ public void handleResponse(ProductResponse response, final ProductSearch.ResultL } } + private void handleCallback(Call call, final ProductSearch.AutoCompleteResultListener resultListener) { + call.enqueue(new Callback() { + + @Override + public void onResponse(Call call, Response response) { + if(response.isSuccessful() && response.body() != null) { + AutoCompleteResponse data = response.body(); + handleAutoCompleteResponse(data, resultListener); + return; + } + + if (response.errorBody() != null) { + Gson gson = new Gson(); + AutoCompleteResponse resp = gson.fromJson(response.errorBody().charStream(), AutoCompleteResponse.class); + if (resp != null && resp.getError() != null) { + resultListener.onResult(null, resp.getError()); + return; + } + } + + resultListener.onResult(null, ErrorData.unknownError("api failed")); + } + + @Override + public void onFailure(Call call, Throwable t) { + resultListener.onResult(null, ErrorData.unknownError(t.getMessage())); + } + + }); + } + + public void handleAutoCompleteResponse(AutoCompleteResponse response, final ProductSearch.AutoCompleteResultListener listener) { + ErrorData error = response.getError(); + if(error != null) { + listener.onResult(null, error); + } else { + listener.onResult(response, null); + } + } + }