package com.algolia.search.saas; import com.algolia.search.saas.APIClient.IndexQuery; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.*; /* * Copyright (c) 2015 Algolia * http://www.algolia.com/ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * Contains all the functions related to one index * You should use APIClient.initIndex(indexName) to retrieve this object */ @SuppressWarnings({"WeakerAccess", "SameParameterValue"}) public class Index { private static final long MAX_TIME_MS_TO_WAIT = 10000L; private APIClient client; private String encodedIndexName; private String indexName; /** * Index initialization (You should not call this yourself) */ protected Index(APIClient client, String indexName) { try { this.client = client; this.encodedIndexName = URLEncoder.encode(indexName, "UTF-8"); this.indexName = indexName; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * @return the underlying index name */ public String getIndexName() { return indexName; } /** * Add an object in this index * * @param obj the object to add */ public JSONObject addObject(JSONObject obj) throws AlgoliaException { return this.addObject(obj, RequestOptions.empty); } /** * Add an object in this index * * @param obj the object to add * @param requestOptions Options to pass to this request */ public JSONObject addObject(JSONObject obj, RequestOptions requestOptions) throws AlgoliaException { return client.postRequest("/1/indexes/" + encodedIndexName, obj.toString(), true, false, requestOptions); } /** * Add an object in this index with a uniq identifier * * @param obj the object to add * @param objectID the objectID associated to this object * (if this objectID already exist the old object will be overriden) */ public JSONObject addObject(JSONObject obj, String objectID) throws AlgoliaException { return this.addObject(obj, objectID, RequestOptions.empty); } /** * Add an object in this index with a uniq identifier * * @param obj the object to add * @param objectID the objectID associated to this object * (if this objectID already exist the old object will be overriden) * @param requestOptions Options to pass to this request */ public JSONObject addObject(JSONObject obj, String objectID, RequestOptions requestOptions) throws AlgoliaException { try { return client.putRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8"), obj.toString(), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Custom batch * * @param actions the array of actions */ public JSONObject batch(JSONArray actions) throws AlgoliaException { return postBatch(actions, RequestOptions.empty); } /** * Custom batch * * @param actions the array of actions * @param requestOptions Options to pass to this request */ public JSONObject batch(JSONArray actions, RequestOptions requestOptions) throws AlgoliaException { return postBatch(actions, requestOptions); } /** * Custom batch * * @param actions the array of actions */ public JSONObject batch(List<JSONObject> actions) throws AlgoliaException { return postBatch(actions, RequestOptions.empty); } /** * Custom batch * * @param actions the array of actions * @param requestOptions Options to pass to this request */ public JSONObject batch(List<JSONObject> actions, RequestOptions requestOptions) throws AlgoliaException { return postBatch(actions, requestOptions); } private JSONObject postBatch(Object actions, RequestOptions requestOptions) throws AlgoliaException { try { JSONObject content = new JSONObject(); content.put("requests", actions); return client.postRequest("/1/indexes/" + encodedIndexName + "/batch", content.toString(), true, false, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e); } } /** * Add several objects * * @param objects the array of objects to add */ public JSONObject addObjects(List<JSONObject> objects) throws AlgoliaException { return this.addObjects(objects, RequestOptions.empty); } /** * Add several objects * * @param objects the array of objects to add * @param requestOptions Options to pass to this request */ public JSONObject addObjects(List<JSONObject> objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (JSONObject obj : objects) { JSONObject action = new JSONObject(); action.put("action", "addObject"); action.put("body", obj); array.put(action); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Add several objects * * @param objects the array of objects to add */ public JSONObject addObjects(JSONArray objects) throws AlgoliaException { return this.addObjects(objects, RequestOptions.empty); } /** * Add several objects * * @param objects the array of objects to add * @param requestOptions Options to pass to this request */ public JSONObject addObjects(JSONArray objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (int n = 0; n < objects.length(); n++) { JSONObject action = new JSONObject(); action.put("action", "addObject"); action.put("body", objects.getJSONObject(n)); array.put(action); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Get an object from this index. Return null if the object doens't exist. * * @param objectID the unique identifier of the object to retrieve */ public JSONObject getObject(String objectID) throws AlgoliaException { return this.getObject(objectID, RequestOptions.empty); } /** * Get an object from this index. Return null if the object doens't exist. * * @param objectID the unique identifier of the object to retrieve * @param requestOptions Options to pass to this request */ public JSONObject getObject(String objectID, RequestOptions requestOptions) throws AlgoliaException { try { return client.getRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8"), false, requestOptions); } catch (AlgoliaException e) { if (e.getCode() == 404) { return null; } throw e; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Get an object from this index * * @param objectID the unique identifier of the object to retrieve * @param attributesToRetrieve contains the list of attributes to retrieve. */ public JSONObject getObject(String objectID, List<String> attributesToRetrieve) throws AlgoliaException { return this.getObject(objectID, attributesToRetrieve, RequestOptions.empty); } /** * Get an object from this index * * @param objectID the unique identifier of the object to retrieve * @param attributesToRetrieve contains the list of attributes to retrieve. * @param requestOptions Options to pass to this request */ public JSONObject getObject(String objectID, List<String> attributesToRetrieve, RequestOptions requestOptions) throws AlgoliaException { try { String params = encodeAttributes(attributesToRetrieve, true); return client.getRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8") + params.toString(), false, requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Get several objects from this index * * @param objectIDs the array of unique identifier of objects to retrieve */ public JSONObject getObjects(List<String> objectIDs) throws AlgoliaException { return getObjects(objectIDs, null, RequestOptions.empty); } /** * Get several objects from this index * * @param objectIDs the array of unique identifier of objects to retrieve * @param requestOptions Options to pass to this request */ public JSONObject getObjects(List<String> objectIDs, RequestOptions requestOptions) throws AlgoliaException { return getObjects(objectIDs, null, requestOptions); } /** * Get several objects from this index * * @param objectIDs the array of unique identifier of objects to retrieve * @param attributesToRetrieve contains the list of attributes to retrieve. */ public JSONObject getObjects(List<String> objectIDs, List<String> attributesToRetrieve) throws AlgoliaException { return this.getObjects(objectIDs, attributesToRetrieve, RequestOptions.empty); } /** * Get several objects from this index * * @param objectIDs the array of unique identifier of objects to retrieve * @param attributesToRetrieve contains the list of attributes to retrieve. * @param requestOptions Options to pass to this request */ public JSONObject getObjects(List<String> objectIDs, List<String> attributesToRetrieve, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray requests = new JSONArray(); for (String id : objectIDs) { JSONObject request = new JSONObject(); request.put("indexName", this.indexName); request.put("objectID", id); request.put("attributesToRetrieve", encodeAttributes(attributesToRetrieve, false)); requests.put(request); } JSONObject body = new JSONObject(); body.put("requests", requests); return client.postRequest("/1/indexes/*/objects", body.toString(), false, false, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new AlgoliaException(e.getMessage()); } } private String encodeAttributes(List<String> attributesToRetrieve, boolean forURL) throws UnsupportedEncodingException { if (attributesToRetrieve == null) { return null; } StringBuilder params = new StringBuilder(); if (forURL) { params.append("?attributes="); } for (int i = 0; i < attributesToRetrieve.size(); ++i) { if (i > 0) { params.append(","); } params.append(URLEncoder.encode(attributesToRetrieve.get(i), "UTF-8")); } return params.toString(); } /** * Update partially an object (only update attributes passed in argument), create the object if it does not exist * * @param partialObject the object to override */ public JSONObject partialUpdateObject(JSONObject partialObject, String objectID) throws AlgoliaException { return partialUpdateObject(partialObject, objectID, true, RequestOptions.empty); } /** * Update partially an object (only update attributes passed in argument), create the object if it does not exist * * @param partialObject the object to override * @param requestOptions Options to pass to this request */ public JSONObject partialUpdateObject(JSONObject partialObject, String objectID, RequestOptions requestOptions) throws AlgoliaException { return partialUpdateObject(partialObject, objectID, true, requestOptions); } /** * Update partially an object (only update attributes passed in argument), do nothing if object does not exist * * @param partialObject the object to override */ public JSONObject partialUpdateObjectNoCreate(JSONObject partialObject, String objectID) throws AlgoliaException { return partialUpdateObject(partialObject, objectID, false, RequestOptions.empty); } /** * Update partially an object (only update attributes passed in argument), do nothing if object does not exist * * @param partialObject the object to override * @param requestOptions Options to pass to this request */ public JSONObject partialUpdateObjectNoCreate(JSONObject partialObject, String objectID, RequestOptions requestOptions) throws AlgoliaException { return partialUpdateObject(partialObject, objectID, false, requestOptions); } private JSONObject partialUpdateObject(JSONObject partialObject, String objectID, Boolean createIfNotExists, RequestOptions requestOptions) throws AlgoliaException { String parameters = ""; if (!createIfNotExists) { parameters = "?createIfNotExists=false"; } try { return client.postRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8") + "/partial" + parameters, partialObject.toString(), true, false, requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Partially Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) */ public JSONObject partialUpdateObjects(JSONArray objects) throws AlgoliaException { return this.partialUpdateObjects(objects, RequestOptions.empty); } /** * Partially Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) * @param requestOptions Options to pass to this request */ public JSONObject partialUpdateObjects(JSONArray objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (int n = 0; n < objects.length(); n++) { array.put(partialUpdateObject(objects.getJSONObject(n))); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Partially Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) */ public JSONObject partialUpdateObjects(List<JSONObject> objects) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (JSONObject obj : objects) { array.put(partialUpdateObject(obj)); } return batch(array); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Partially Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) * @param requestOptions Options to pass to this request */ public JSONObject partialUpdateObjects(List<JSONObject> objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (JSONObject obj : objects) { array.put(partialUpdateObject(obj)); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } private JSONObject partialUpdateObject(JSONObject object) throws JSONException { JSONObject action = new JSONObject(); action.put("action", "partialUpdateObject"); action.put("objectID", object.getString("objectID")); action.put("body", object); return action; } /** * Override the content of object * * @param object the object to update */ public JSONObject saveObject(JSONObject object, String objectID) throws AlgoliaException { return this.saveObject(object, objectID, RequestOptions.empty); } /** * Override the content of object * * @param object the object to update * @param requestOptions Options to pass to this request */ public JSONObject saveObject(JSONObject object, String objectID, RequestOptions requestOptions) throws AlgoliaException { try { return client.putRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8"), object.toString(), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) */ public JSONObject saveObjects(List<JSONObject> objects) throws AlgoliaException { return this.saveObjects(objects, RequestOptions.empty); } /** * Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) * @param requestOptions Options to pass to this request */ public JSONObject saveObjects(List<JSONObject> objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (JSONObject obj : objects) { JSONObject action = new JSONObject(); action.put("action", "updateObject"); action.put("objectID", obj.getString("objectID")); action.put("body", obj); array.put(action); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) */ public JSONObject saveObjects(JSONArray objects) throws AlgoliaException { return this.saveObjects(objects, RequestOptions.empty); } /** * Override the content of several objects * * @param objects the array of objects to update (each object must contains an objectID attribute) * @param requestOptions Options to pass to this request */ public JSONObject saveObjects(JSONArray objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (int n = 0; n < objects.length(); n++) { JSONObject obj = objects.getJSONObject(n); JSONObject action = new JSONObject(); action.put("action", "updateObject"); action.put("objectID", obj.getString("objectID")); action.put("body", obj); array.put(action); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Delete an object from the index * * @param objectID the unique identifier of object to delete */ public JSONObject deleteObject(String objectID) throws AlgoliaException { return this.deleteObject(objectID, RequestOptions.empty); } /** * Delete an object from the index * * @param objectID the unique identifier of object to delete * @param requestOptions Options to pass to this request */ public JSONObject deleteObject(String objectID, RequestOptions requestOptions) throws AlgoliaException { if (objectID == null || objectID.length() == 0) { throw new AlgoliaException("Invalid objectID"); } try { return client.deleteRequest("/1/indexes/" + encodedIndexName + "/" + URLEncoder.encode(objectID, "UTF-8"), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Delete all objects matching a query * * @param query the query string */ public JSONObject deleteBy(Query query) throws AlgoliaException { return deleteBy(query, RequestOptions.empty); } /** * Delete all objects matching a query * * @param query the query string * @param requestOptions Options to pass to this request */ public JSONObject deleteBy(Query query, RequestOptions requestOptions) throws AlgoliaException { String paramsString = query.getQueryString(); JSONObject body = new JSONObject(); try { body.put("params", paramsString); } catch (JSONException e) { throw new RuntimeException(e); } return client.postRequest("/1/indexes/" + encodedIndexName + "/deleteByQuery", body.toString(), false, false, requestOptions); } /** * Delete all objects matching a query * * @param query the query string * @deprecated use deleteBy */ @Deprecated public void deleteByQuery(Query query) throws AlgoliaException { deleteByQuery(query, 100000); } /** * Delete all objects matching a query * * @param query the query string * @param requestOptions Options to pass to this request * @deprecated use deleteBy */ @Deprecated public void deleteByQuery(Query query, RequestOptions requestOptions) throws AlgoliaException { this.deleteByQuery(query, 100000, requestOptions); } /** * @throws AlgoliaException * @deprecated use deleteBy */ @Deprecated public void deleteByQuery(Query query, int batchLimit, RequestOptions requestOptions) throws AlgoliaException { List<String> attributesToRetrieve = new ArrayList<String>(); attributesToRetrieve.add("objectID"); query.setAttributesToRetrieve(attributesToRetrieve); query.setAttributesToHighlight(new ArrayList<String>()); query.setAttributesToSnippet(new ArrayList<String>()); query.setHitsPerPage(1000); query.enableDistinct(false); IndexBrowser it = this.browse(query, requestOptions); try { while (true) { List<String> objectIDs = new ArrayList<String>(); while (it.hasNext()) { JSONObject elt = it.next(); objectIDs.add(elt.getString("objectID")); if (objectIDs.size() > batchLimit) { break; } } JSONObject task = this.deleteObjects(objectIDs); this.waitTask(task.getLong("taskID")); if (!it.hasNext()) break; } } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * @throws AlgoliaException * @deprecated use deleteBy */ @Deprecated public void deleteByQuery(Query query, int batchLimit) throws AlgoliaException { this.deleteByQuery(query, batchLimit, RequestOptions.empty); } /** * Search inside the index * * @param the query to search */ public JSONObject search(Query params) throws AlgoliaException { return this.search(params, RequestOptions.empty); } /** * Search inside the index * * @param the query to search * @param requestOptions Options to pass to this request */ public JSONObject search(Query params, RequestOptions requestOptions) throws AlgoliaException { String paramsString = params.getQueryString(); JSONObject body = new JSONObject(); try { body.put("params", paramsString); } catch (JSONException e) { throw new RuntimeException(e); } return client.postRequest("/1/indexes/" + encodedIndexName + "/query", body.toString(), false, true, requestOptions); } /** * Search into a facet value */ public JSONObject searchInFacetValues(String facetName, String facetQuery, Query params) throws AlgoliaException { return this.searchInFacetValues(facetName, facetQuery, params, RequestOptions.empty); } /** * Search into a facet value * * @param requestOptions Options to pass to this request */ public JSONObject searchInFacetValues(String facetName, String facetQuery, Query params, RequestOptions requestOptions) throws AlgoliaException { params = params == null ? new Query() : params; String paramsString = params.setFacetQuery(facetQuery).getQueryString(); JSONObject body = new JSONObject(); String encodedFacetName; try { encodedFacetName = URLEncoder.encode(facetName, "UTF-8"); body.put("params", paramsString); } catch (JSONException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return client.postRequest("/1/indexes/" + encodedIndexName + "/facets/" + encodedFacetName + "/query", body.toString(), false, true, requestOptions); } /** * Delete several objects * * @param objects the array of objectIDs to delete */ public JSONObject deleteObjects(List<String> objects) throws AlgoliaException { return this.deleteObjects(objects, RequestOptions.empty); } /** * Delete several objects * * @param objects the array of objectIDs to delete * @param requestOptions Options to pass to this request */ public JSONObject deleteObjects(List<String> objects, RequestOptions requestOptions) throws AlgoliaException { try { JSONArray array = new JSONArray(); for (String id : objects) { JSONObject obj = new JSONObject(); obj.put("objectID", id); JSONObject action = new JSONObject(); action.put("action", "deleteObject"); action.put("body", obj); array.put(action); } return batch(array, requestOptions); } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } } /** * Browse all index content * * @param page Pagination parameter used to select the page to retrieve. * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9 * @deprecated Use the `browse(Query params)` version */ public JSONObject browse(int page) throws AlgoliaException { return client.getRequest("/1/indexes/" + encodedIndexName + "/browse?page=" + page, false, RequestOptions.empty); } /** * Browse all index content */ public IndexBrowser browse(Query params) throws AlgoliaException { return new IndexBrowser(client, encodedIndexName, params, null, RequestOptions.empty); } /** * Browse all index content * * @param requestOptions Options to pass to this request */ public IndexBrowser browse(Query params, RequestOptions requestOptions) throws AlgoliaException { return new IndexBrowser(client, encodedIndexName, params, null, requestOptions); } /** * Browse all index content starting from a cursor */ public IndexBrowser browseFrom(Query params, String cursor) throws AlgoliaException { return new IndexBrowser(client, encodedIndexName, params, cursor, RequestOptions.empty); } /** * Browse all index content starting from a cursor * * @param requestOptions Options to pass to this request */ public IndexBrowser browseFrom(Query params, String cursor, RequestOptions requestOptions) throws AlgoliaException { return new IndexBrowser(client, encodedIndexName, params, cursor, requestOptions); } @Deprecated public IndexBrowser browseFrow(Query params, String cursor) throws AlgoliaException { return browseFrom(params, cursor); } /** * Browse all index content * * @param page Pagination parameter used to select the page to retrieve. * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9 * @param hitsPerPage Pagination parameter used to select the number of hits per page. Defaults to 1000. */ public JSONObject browse(int page, int hitsPerPage) throws AlgoliaException { return this.browse(page, hitsPerPage, RequestOptions.empty); } /** * Browse all index content * * @param page Pagination parameter used to select the page to retrieve. * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9 * @param hitsPerPage Pagination parameter used to select the number of hits per page. Defaults to 1000. * @param requestOptions Options to pass to this request */ public JSONObject browse(int page, int hitsPerPage, RequestOptions requestOptions) throws AlgoliaException { return client.getRequest("/1/indexes/" + encodedIndexName + "/browse?page=" + page + "&hitsPerPage=" + hitsPerPage, false, requestOptions); } /** * Wait the publication of a task on the server. * All server task are asynchronous and you can check with this method that the task is published. * * @param taskID the id of the task returned by server * @param timeToWait time to sleep seed */ public void waitTask(String taskID, long timeToWait) throws AlgoliaException { this.waitTask(taskID, timeToWait, RequestOptions.empty); } /** * Wait the publication of a task on the server. * All server task are asynchronous and you can check with this method that the task is published. * * @param taskID the id of the task returned by server * @param timeToWait time to sleep seed * @param requestOptions Options to pass to this request */ public void waitTask(String taskID, long timeToWait, RequestOptions requestOptions) throws AlgoliaException { try { while (true) { JSONObject obj = client.getRequest("/1/indexes/" + encodedIndexName + "/task/" + URLEncoder.encode(taskID, "UTF-8"), false, requestOptions); if (obj.getString("status").equals("published")) return; try { Thread.sleep(timeToWait); } catch (InterruptedException ignored) { } timeToWait *= 2; timeToWait = timeToWait > MAX_TIME_MS_TO_WAIT ? MAX_TIME_MS_TO_WAIT : timeToWait; } } catch (JSONException e) { throw new AlgoliaException(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Wait the publication of a task on the server. * All server task are asynchronous and you can check with this method that the task is published. * * @param taskID the id of the task returned by server */ public void waitTask(String taskID) throws AlgoliaException { waitTask(taskID, 100); } /** * Wait the publication of a task on the server. * All server task are asynchronous and you can check with this method that the task is published. * * @param taskID the id of the task returned by server */ public void waitTask(Long taskID) throws AlgoliaException { waitTask(taskID.toString(), 100); } /** * Get settings of this index */ public JSONObject getSettings() throws AlgoliaException { return this.getSettings(RequestOptions.empty); } /** * Get settings of this index * * @param requestOptions Options to pass to this request */ public JSONObject getSettings(RequestOptions requestOptions) throws AlgoliaException { return client.getRequest("/1/indexes/" + encodedIndexName + "/settings?getVersion=2", false, requestOptions); } /** * Delete the index content without removing settings and index specific API keys. */ public JSONObject clearIndex() throws AlgoliaException { return this.clearIndex(RequestOptions.empty); } /** * Delete the index content without removing settings and index specific API keys. * * @param requestOptions Options to pass to this request */ public JSONObject clearIndex(RequestOptions requestOptions) throws AlgoliaException { return client.postRequest("/1/indexes/" + encodedIndexName + "/clear", "", true, false, requestOptions); } /** * Set settings for this index * * @param settings the settings for an index */ public JSONObject setSettings(JSONObject settings) throws AlgoliaException { return setSettings(settings, false); } /** * Set settings for this index * * @param settings the settings for an index * @param requestOptions Options to pass to this request */ public JSONObject setSettings(JSONObject settings, RequestOptions requestOptions) throws AlgoliaException { return setSettings(settings, false, requestOptions); } public JSONObject setSettings(JSONObject settings, Boolean forwardToReplicas) throws AlgoliaException { return this.setSettings(settings, forwardToReplicas, RequestOptions.empty); } public JSONObject setSettings(JSONObject settings, Boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { return client.putRequest("/1/indexes/" + encodedIndexName + "/settings?forwardToReplicas=" + forwardToReplicas.toString(), settings.toString(), requestOptions); } /** * Deprecated: use listApiKeys */ @Deprecated public JSONObject listUserKeys() throws AlgoliaException { return listApiKeys(); } /** * List all existing api keys with their associated ACLs */ public JSONObject listApiKeys() throws AlgoliaException { return this.listApiKeys(RequestOptions.empty); } /** * List all existing api keys with their associated ACLs * * @param requestOptions Options to pass to this request */ public JSONObject listApiKeys(RequestOptions requestOptions) throws AlgoliaException { return client.getRequest("/1/indexes/" + encodedIndexName + "/keys", false, requestOptions); } /** * Deprecated: use getApiKey */ @Deprecated public JSONObject getUserKeyACL(String key) throws AlgoliaException { return getApiKey(key); } /** * Get ACL of an api key */ public JSONObject getApiKey(String key) throws AlgoliaException { return this.getApiKey(key, RequestOptions.empty); } /** * Get ACL of an api key * * @param requestOptions Options to pass to this request */ public JSONObject getApiKey(String key, RequestOptions requestOptions) throws AlgoliaException { return client.getRequest("/1/indexes/" + encodedIndexName + "/keys/" + key, false, requestOptions); } /** * Deprecated: use deleteApiKey */ @Deprecated public JSONObject deleteUserKey(String key) throws AlgoliaException { return deleteApiKey(key); } /** * Delete an existing api key */ public JSONObject deleteApiKey(String key) throws AlgoliaException { return this.deleteApiKey(key, RequestOptions.empty); } /** * Delete an existing api key * * @param requestOptions Options to pass to this request */ public JSONObject deleteApiKey(String key, RequestOptions requestOptions) throws AlgoliaException { return client.deleteRequest("/1/indexes/" + encodedIndexName + "/keys/" + key, requestOptions); } /** * Deprecated: use addApiKey */ @Deprecated public JSONObject addUserKey(JSONObject params) throws AlgoliaException { return addApiKey(params); } /** * Create a new api key * * @param params the list of parameters for this key. Defined by a JSONObject that * can contains the following values: * - acl: array of string * - indices: array of string * - validity: int * - referers: array of string * - description: string * - maxHitsPerQuery: integer * - queryParameters: string * - maxQueriesPerIPPerHour: integer */ public JSONObject addApiKey(JSONObject params) throws AlgoliaException { return this.addApiKey(params, RequestOptions.empty); } /** * Create a new api key * * @param params the list of parameters for this key. Defined by a JSONObject that * can contains the following values: * - acl: array of string * - indices: array of string * - validity: int * - referers: array of string * - description: string * - maxHitsPerQuery: integer * - queryParameters: string * - maxQueriesPerIPPerHour: integer * @param requestOptions Options to pass to this request */ public JSONObject addApiKey(JSONObject params, RequestOptions requestOptions) throws AlgoliaException { return client.postRequest("/1/indexes/" + encodedIndexName + "/keys", params.toString(), true, false, requestOptions); } /** * Deprecated: use addApiKey */ @Deprecated public JSONObject addUserKey(List<String> acls) throws AlgoliaException { return addApiKey(acls); } /** * Create a new api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) */ public JSONObject addApiKey(List<String> acls) throws AlgoliaException { return addApiKey(acls, 0, 0, 0); } /** * Deprecated: use updateApiKey */ @Deprecated public JSONObject updateUserKey(String key, JSONObject params) throws AlgoliaException { return updateApiKey(key, params); } /** * Update a new api key * * @param params the list of parameters for this key. Defined by a JSONObject that * can contains the following values: * - acl: array of string * - indices: array of string * - validity: int * - referers: array of string * - description: string * - maxHitsPerQuery: integer * - queryParameters: string * - maxQueriesPerIPPerHour: integer */ public JSONObject updateApiKey(String key, JSONObject params) throws AlgoliaException { return this.updateApiKey(key, params, RequestOptions.empty); } /** * Update a new api key * * @param params the list of parameters for this key. Defined by a JSONObject that * can contains the following values: * - acl: array of string * - indices: array of string * - validity: int * - referers: array of string * - description: string * - maxHitsPerQuery: integer * - queryParameters: string * - maxQueriesPerIPPerHour: integer * @param requestOptions Options to pass to this request */ public JSONObject updateApiKey(String key, JSONObject params, RequestOptions requestOptions) throws AlgoliaException { return client.putRequest("/1/indexes/" + encodedIndexName + "/keys/" + key, params.toString(), requestOptions); } /** * Deprecated: user updateApiKey */ @Deprecated public JSONObject updateUserKey(String key, List<String> acls) throws AlgoliaException { return updateApiKey(key, acls); } /** * Update an api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) */ public JSONObject updateApiKey(String key, List<String> acls) throws AlgoliaException { return updateApiKey(key, acls, 0, 0, 0); } /** * Deprecated: use addApiKey */ @Deprecated public JSONObject addUserKey(List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery) throws AlgoliaException { return addApiKey(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery); } /** * Create a new api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key) * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour. Defaults to 0 (no rate limit). * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited) */ public JSONObject addApiKey(List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery) throws AlgoliaException { return this.addApiKey(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, RequestOptions.empty); } /** * Create a new api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key) * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour. Defaults to 0 (no rate limit). * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited) * @param requestOptions Options to pass to this request */ public JSONObject addApiKey(List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery, RequestOptions requestOptions) throws AlgoliaException { try { JSONObject jsonObject = generateUpdateUser(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery); return addApiKey(jsonObject, requestOptions); } catch (JSONException e) { throw new RuntimeException(e); } } /** * Deprecated: use updateApiKey */ @Deprecated public JSONObject updateUserKey(String key, List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery) throws AlgoliaException { return updateApiKey(key, acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, RequestOptions.empty); } /** * Update an api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key) * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour. Defaults to 0 (no rate limit). * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited) */ public JSONObject updateApiKey(String key, List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery) throws AlgoliaException { try { JSONObject jsonObject = generateUpdateUser(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery); return updateApiKey(key, jsonObject); } catch (JSONException e) { throw new RuntimeException(e); } } /** * Update an api key * * @param acls the list of ACL for this key. Defined by an array of strings that * can contains the following values: * - search: allow to search (https and http) * - addObject: allows to add/update an object in the index (https only) * - deleteObject : allows to delete an existing object (https only) * - deleteIndex : allows to delete index content (https only) * - settings : allows to get index settings (https only) * - editSettings : allows to change index settings (https only) * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key) * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour. Defaults to 0 (no rate limit). * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited) * @param requestOptions Options to pass to this request */ public JSONObject updateApiKey(String key, List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery, RequestOptions requestOptions) throws AlgoliaException { try { JSONObject jsonObject = generateUpdateUser(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery); return updateApiKey(key, jsonObject, requestOptions); } catch (JSONException e) { throw new RuntimeException(e); } } private JSONObject generateUpdateUser(List<String> acls, int validity, int maxQueriesPerIPPerHour, int maxHitsPerQuery) throws JSONException { JSONObject jsonObject = new JSONObject(); jsonObject.put("acl", new JSONArray(acls)); jsonObject.put("validity", validity); jsonObject.put("maxQueriesPerIPPerHour", maxQueriesPerIPPerHour); jsonObject.put("maxHitsPerQuery", maxHitsPerQuery); return jsonObject; } /** * Perform a search with disjunctive facets generating as many queries as number of disjunctive facets * * @param query the query * @param disjunctiveFacets the array of disjunctive facets * @param refinements Map<String, List<String>> representing the current refinements * ex: { "my_facet1" => ["my_value1", "my_value2"], "my_disjunctive_facet1" => ["my_value1", "my_value2"] } */ public JSONObject searchDisjunctiveFaceting(Query query, List<String> disjunctiveFacets, Map<String, List<String>> refinements) throws AlgoliaException { if (refinements == null) { refinements = new HashMap<String, List<String>>(); } HashMap<String, List<String>> disjunctiveRefinements = new HashMap<String, List<String>>(); for (Map.Entry<String, List<String>> elt : refinements.entrySet()) { if (disjunctiveFacets.contains(elt.getKey())) { disjunctiveRefinements.put(elt.getKey(), elt.getValue()); } } // build queries List<IndexQuery> queries = new ArrayList<IndexQuery>(); // hits + regular facets query StringBuilder filters = new StringBuilder(); boolean first_global = true; for (Map.Entry<String, List<String>> elt : refinements.entrySet()) { StringBuilder or = new StringBuilder(); or.append("("); boolean first = true; for (String val : elt.getValue()) { if (disjunctiveRefinements.containsKey(elt.getKey())) { // disjunctive refinements are ORed if (!first) { or.append(','); } first = false; or.append(String.format("%s:%s", elt.getKey(), val)); } else { if (!first_global) { filters.append(','); } first_global = false; filters.append(String.format("%s:%s", elt.getKey(), val)); } } // Add or if (disjunctiveRefinements.containsKey(elt.getKey())) { or.append(')'); if (!first_global) { filters.append(','); } first_global = false; filters.append(or.toString()); } } queries.add(new IndexQuery(this.indexName, new Query(query).setFacetFilters(filters.toString()))); // one query per disjunctive facet (use all refinements but the current one + hitsPerPage=1 + single facet for (String disjunctiveFacet : disjunctiveFacets) { filters = new StringBuilder(); first_global = true; for (Map.Entry<String, List<String>> elt : refinements.entrySet()) { if (disjunctiveFacet.equals(elt.getKey())) { continue; } StringBuilder or = new StringBuilder(); or.append("("); boolean first = true; for (String val : elt.getValue()) { if (disjunctiveRefinements.containsKey(elt.getKey())) { // disjunctive refinements are ORed if (!first) { or.append(','); } first = false; or.append(String.format("%s:%s", elt.getKey(), val)); } else { if (!first_global) { filters.append(','); } first_global = false; filters.append(String.format("%s:%s", elt.getKey(), val)); } } // Add or if (disjunctiveRefinements.containsKey(elt.getKey())) { or.append(')'); if (!first_global) { filters.append(','); } first_global = false; filters.append(or.toString()); } } List<String> facets = new ArrayList<String>(); facets.add(disjunctiveFacet); queries.add(new IndexQuery(this.indexName, new Query(query).setHitsPerPage(0).enableAnalytics(false).setAttributesToRetrieve(new ArrayList<String>()).setAttributesToHighlight(new ArrayList<String>()).setAttributesToSnippet(new ArrayList<String>()).setFacets(facets).setFacetFilters(filters.toString()))); } JSONObject answers = this.client.multipleQueries(queries); // aggregate answers // first answer stores the hits + regular facets try { JSONArray results = answers.getJSONArray("results"); JSONObject aggregatedAnswer = results.getJSONObject(0); JSONObject disjunctiveFacetsJSON = new JSONObject(); for (int i = 1; i < results.length(); ++i) { JSONObject facets = results.getJSONObject(i).getJSONObject("facets"); @SuppressWarnings("unchecked") Iterator<String> keys = facets.keys(); while (keys.hasNext()) { String key = keys.next(); // Add the facet to the disjunctive facet hash disjunctiveFacetsJSON.put(key, facets.getJSONObject(key)); // concatenate missing refinements if (!disjunctiveRefinements.containsKey(key)) { continue; } for (String refine : disjunctiveRefinements.get(key)) { if (!disjunctiveFacetsJSON.getJSONObject(key).has(refine)) { disjunctiveFacetsJSON.getJSONObject(key).put(refine, 0); } } } } aggregatedAnswer.put("disjunctiveFacets", disjunctiveFacetsJSON); return aggregatedAnswer; } catch (JSONException e) { throw new Error(e); } } public JSONObject searchDisjunctiveFaceting(Query query, List<String> disjunctiveFacets) throws AlgoliaException { return searchDisjunctiveFaceting(query, disjunctiveFacets, null); } /** * Search for synonyms * * @param query the query */ public JSONObject searchSynonyms(SynonymQuery query) throws AlgoliaException, JSONException { return this.searchSynonyms(query, RequestOptions.empty); } /** * Search for synonyms * * @param query the query * @param requestOptions Options to pass to this request */ public JSONObject searchSynonyms(SynonymQuery query, RequestOptions requestOptions) throws AlgoliaException, JSONException { JSONObject body = new JSONObject().put("query", query.getQueryString()); if (query.hasTypes()) { StringBuilder type = new StringBuilder(); boolean first = true; for (SynonymQuery.SynonymType t : query.getTypes()) { if (!first) { type.append(","); } type.append(t.name); first = false; } body = body.put("type", type.toString()); } if (query.getPage() != null) { body = body.put("page", query.getPage()); } if (query.getHitsPerPage() != null) { body = body.put("hitsPerPage", query.getHitsPerPage()); } return client.postRequest("/1/indexes/" + encodedIndexName + "/synonyms/search", body.toString(), false, true, requestOptions); } /** * Get one synonym * * @param objectID the objectId of the synonym to get */ public JSONObject getSynonym(String objectID) throws AlgoliaException { return this.getSynonym(objectID, RequestOptions.empty); } /** * Get one synonym * * @param objectID the objectId of the synonym to get * @param requestOptions Options to pass to this request */ public JSONObject getSynonym(String objectID, RequestOptions requestOptions) throws AlgoliaException { if (objectID == null || objectID.length() == 0) { throw new AlgoliaException("Invalid objectID"); } try { return client.getRequest("/1/indexes/" + encodedIndexName + "/synonyms/" + URLEncoder.encode(objectID, "UTF-8"), true, requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Delete one synonym * * @param objectID The objectId of the synonym to delete * @param forwardToReplicas Forward the operation to the replica indices */ public JSONObject deleteSynonym(String objectID, boolean forwardToReplicas) throws AlgoliaException { return this.deleteSynonym(objectID, forwardToReplicas, RequestOptions.empty); } /** * Delete one synonym * * @param objectID The objectId of the synonym to delete * @param forwardToReplicas Forward the operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject deleteSynonym(String objectID, boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { if (objectID == null || objectID.length() == 0) { throw new AlgoliaException("Invalid objectID"); } try { return client.deleteRequest("/1/indexes/" + encodedIndexName + "/synonyms/" + URLEncoder.encode(objectID, "UTF-8") + "/?page=forwardToReplicas" + forwardToReplicas, requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Delete one synonym * * @param objectID The objectId of the synonym to delete */ public JSONObject deleteSynonym(String objectID) throws AlgoliaException { return deleteSynonym(objectID, false); } /** * Delete one synonym * * @param objectID The objectId of the synonym to delete * @param requestOptions Options to pass to this request */ public JSONObject deleteSynonym(String objectID, RequestOptions requestOptions) throws AlgoliaException { return deleteSynonym(objectID, false, requestOptions); } /** * Delete all synonym set * * @param forwardToReplicas Forward the operation to the replica indices */ public JSONObject clearSynonyms(boolean forwardToReplicas) throws AlgoliaException { return this.clearSynonyms(forwardToReplicas, RequestOptions.empty); } /** * Delete all synonym set * * @param forwardToReplicas Forward the operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject clearSynonyms(boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { return client.postRequest("/1/indexes/" + encodedIndexName + "/synonyms/clear?forwardToReplicas=" + forwardToReplicas, "", true, false, requestOptions); } /** * Delete all synonym set */ public JSONObject clearSynonyms() throws AlgoliaException { return clearSynonyms(false); } /** * Delete all synonym set * * @param requestOptions Options to pass to this request */ public JSONObject clearSynonyms(RequestOptions requestOptions) throws AlgoliaException { return clearSynonyms(false, requestOptions); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms * @param forwardToReplicas Forward the operation to the replica indices * @param replaceExistingSynonyms Replace the existing synonyms with this batch */ public JSONObject batchSynonyms(List<JSONObject> objects, boolean forwardToReplicas, boolean replaceExistingSynonyms) throws AlgoliaException { return this.batchSynonyms(objects, forwardToReplicas, replaceExistingSynonyms, RequestOptions.empty); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms * @param forwardToReplicas Forward the operation to the replica indices * @param replaceExistingSynonyms Replace the existing synonyms with this batch * @param requestOptions Options to pass to this request */ public JSONObject batchSynonyms(List<JSONObject> objects, boolean forwardToReplicas, boolean replaceExistingSynonyms, RequestOptions requestOptions) throws AlgoliaException { JSONArray array = new JSONArray(); for (JSONObject obj : objects) { array.put(obj); } return client.postRequest("/1/indexes/" + encodedIndexName + "/synonyms/batch?forwardToReplicas=" + forwardToReplicas + "&replaceExistingSynonyms=" + replaceExistingSynonyms, array.toString(), true, false, requestOptions); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms * @param forwardToReplicas Forward the operation to the replica indices */ public JSONObject batchSynonyms(List<JSONObject> objects, boolean forwardToReplicas) throws AlgoliaException { return batchSynonyms(objects, forwardToReplicas, false); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms * @param forwardToReplicas Forward the operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject batchSynonyms(List<JSONObject> objects, boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { return batchSynonyms(objects, forwardToReplicas, false, requestOptions); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms */ public JSONObject batchSynonyms(List<JSONObject> objects) throws AlgoliaException { return batchSynonyms(objects, false, false); } /** * Add or Replace a list of synonyms * * @param objects List of synonyms * @param requestOptions Options to pass to this request */ public JSONObject batchSynonyms(List<JSONObject> objects, RequestOptions requestOptions) throws AlgoliaException { return batchSynonyms(objects, false, false, requestOptions); } /** * Update one synonym * * @param objectID The objectId of the synonym to save * @param content The new content of this synonym * @param forwardToReplicas Forward the operation to the replica indices */ public JSONObject saveSynonym(String objectID, JSONObject content, boolean forwardToReplicas) throws AlgoliaException { return this.saveSynonym(objectID, content, forwardToReplicas, RequestOptions.empty); } /** * Update one synonym * * @param objectID The objectId of the synonym to save * @param content The new content of this synonym * @param forwardToReplicas Forward the operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject saveSynonym(String objectID, JSONObject content, boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { try { return client.putRequest("/1/indexes/" + encodedIndexName + "/synonyms/" + URLEncoder.encode(objectID, "UTF-8") + "?forwardToReplicas=" + forwardToReplicas, content.toString(), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Update one synonym * * @param objectID The objectId of the synonym to save * @param content The new content of this synonym */ public JSONObject saveSynonym(String objectID, JSONObject content) throws AlgoliaException { return saveSynonym(objectID, content, false); } /** * Update one synonym * * @param objectID The objectId of the synonym to save * @param content The new content of this synonym * @param requestOptions Options to pass to this request */ public JSONObject saveSynonym(String objectID, JSONObject content, RequestOptions requestOptions) throws AlgoliaException { return saveSynonym(objectID, content, false, requestOptions); } /** * Save a query rule * * @param objectID the objectId of the query rule to save * @param rule the content of this query rule * @param forwardToReplicas Forward this operation to the replica indices */ public JSONObject saveRule(String objectID, JSONObject rule, boolean forwardToReplicas) throws AlgoliaException { return this.saveRule(objectID, rule, forwardToReplicas, RequestOptions.empty); } /** * Save a query rule * * @param objectID the objectId of the query rule to save * @param rule the content of this query rule * @param forwardToReplicas Forward this operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject saveRule(String objectID, JSONObject rule, boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { try { return client.putRequest("/1/indexes/" + encodedIndexName + "/rules/" + URLEncoder.encode(objectID, "UTF-8") + "?forwardToReplicas=" + forwardToReplicas, rule.toString(), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Save a query rule * * @param objectID the objectId of the query rule to save * @param rule the content of this query rule */ public JSONObject saveRule(String objectID, JSONObject rule) throws AlgoliaException { return saveRule(objectID, rule, false); } /** * Add or Replace a list of synonyms * * @param rules the list of rules to add/replace * @param forwardToReplicas Forward this operation to the replica indices * @param clearExistingRules Replace the existing query rules with this batch */ public JSONObject batchRules(List<JSONObject> rules, boolean forwardToReplicas, boolean clearExistingRules) throws AlgoliaException { return this.batchRules(rules, forwardToReplicas, clearExistingRules, RequestOptions.empty); } /** * Add or Replace a list of synonyms * * @param rules the list of rules to add/replace * @param forwardToReplicas Forward this operation to the replica indices * @param clearExistingRules Replace the existing query rules with this batch * @param requestOptions Options to pass to this request */ public JSONObject batchRules(List<JSONObject> rules, boolean forwardToReplicas, boolean clearExistingRules, RequestOptions requestOptions) throws AlgoliaException { JSONArray array = new JSONArray(); for (JSONObject obj : rules) { array.put(obj); } return client.postRequest("/1/indexes/" + encodedIndexName + "/rules/batch?forwardToReplicas=" + forwardToReplicas + "&clearExistingRules=" + clearExistingRules, array.toString(), true, false, requestOptions); } /** * Add or Replace a list of synonyms * * @param rules the list of rules to add/replace * @param forwardToReplicas Forward this operation to the replica indices */ public JSONObject batchRules(List<JSONObject> rules, boolean forwardToReplicas) throws AlgoliaException { return batchRules(rules, forwardToReplicas, false); } /** * Add or Replace a list of synonyms * * @param rules the list of rules to add/replace */ public JSONObject batchRules(List<JSONObject> rules) throws AlgoliaException { return batchRules(rules, false, false); } /** * Get a query rule * * @param objectID the objectID of the query rule to get */ public JSONObject getRule(String objectID) throws AlgoliaException { return this.getRule(objectID, RequestOptions.empty); } /** * Get a query rule * * @param objectID the objectID of the query rule to get * @param requestOptions Options to pass to this request */ public JSONObject getRule(String objectID, RequestOptions requestOptions) throws AlgoliaException { if (objectID == null || objectID.length() == 0) { throw new AlgoliaException("Invalid objectID"); } try { return client.getRequest("/1/indexes/" + encodedIndexName + "/rules/" + URLEncoder.encode(objectID, "UTF-8"), true, requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Delete a query rule * * @param objectID the objectID of the query rule to delete */ public JSONObject deleteRule(String objectID) throws AlgoliaException { return this.deleteRule(objectID, RequestOptions.empty); } /** * Delete a query rule * * @param objectID the objectID of the query rule to delete * @param requestOptions Options to pass to this request */ public JSONObject deleteRule(String objectID, RequestOptions requestOptions) throws AlgoliaException { if (objectID == null || objectID.length() == 0) { throw new AlgoliaException("Invalid objectID"); } try { return client.deleteRequest("/1/indexes/" + encodedIndexName + "/rules/" + URLEncoder.encode(objectID, "UTF-8"), requestOptions); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Delete all query rules * * @param forwardToReplicas Forward the operation to the replica indices */ public JSONObject clearRules(boolean forwardToReplicas) throws AlgoliaException { return this.clearRules(forwardToReplicas, RequestOptions.empty); } /** * Delete all query rules * * @param forwardToReplicas Forward the operation to the replica indices * @param requestOptions Options to pass to this request */ public JSONObject clearRules(boolean forwardToReplicas, RequestOptions requestOptions) throws AlgoliaException { return client.postRequest("/1/indexes/" + encodedIndexName + "/rules/clear?forwardToReplicas=" + forwardToReplicas, "", true, false, requestOptions); } /** * Delete all query rules */ public JSONObject clearRules() throws AlgoliaException { return clearRules(false); } /** * Search for query rules * * @param query the query */ public JSONObject searchRules(RuleQuery query) throws AlgoliaException, JSONException { return this.searchRules(query, RequestOptions.empty); } /** * Search for query rules * * @param query the query * @param requestOptions Options to pass to this request */ public JSONObject searchRules(RuleQuery query, RequestOptions requestOptions) throws AlgoliaException, JSONException { JSONObject body = new JSONObject(); if (query.getQuery() != null) { body = body.put("query", query.getQuery()); } if (query.getAnchoring() != null) { body = body.put("anchoring", query.getAnchoring()); } if (query.getContext() != null) { body = body.put("context", query.getContext()); } if (query.getPage() != null) { body = body.put("page", query.getPage()); } if (query.getHitsPerPage() != null) { body = body.put("hitsPerPage", query.getHitsPerPage()); } return client.postRequest("/1/indexes/" + encodedIndexName + "/rules/search", body.toString(), false, true, requestOptions); } /** * @deprecated See {@code IndexBrowser} */ static class IndexBrower extends IndexBrowser { IndexBrower(APIClient client, String encodedIndexName, Query params, String startingCursor) throws AlgoliaException { super(client, encodedIndexName, params, startingCursor, RequestOptions.empty); } } /** * This class iterates over an index using the cursor-based browse mechanism */ static public class IndexBrowser implements Iterator<JSONObject> { final APIClient client; final Query params; final String encodedIndexName; final RequestOptions requestOptions; JSONObject answer; JSONObject hit; int pos; IndexBrowser(APIClient client, String encodedIndexName, Query params, String startingCursor, RequestOptions requestOptions) throws AlgoliaException { this.client = client; this.params = params; this.encodedIndexName = encodedIndexName; this.requestOptions = requestOptions; doQuery(startingCursor); this.pos = 0; } @Override public boolean hasNext() { try { return pos < answer.getJSONArray("hits").length() || answer.has("cursor") && !answer.getString("cursor").isEmpty(); } catch (JSONException e) { e.printStackTrace(); } return false; } @Override public JSONObject next() { try { do { if (pos < answer.getJSONArray("hits").length()) { hit = answer.getJSONArray("hits").getJSONObject(pos); ++pos; break; } if (answer.has("cursor") && !answer.getString("cursor").isEmpty()) { pos = 0; doQuery(getCursor()); continue; } return null; } while (true); } catch (JSONException e) { throw new IllegalStateException(e); } catch (AlgoliaException e) { throw new IllegalArgumentException(e); } return hit; } /** * @return the underlying cursor used by the enumeration */ public String getCursor() { try { return answer != null && answer.has("cursor") ? answer.getString("cursor") : null; } catch (JSONException e) { throw new IllegalStateException(e); } } @Override public void remove() { throw new IllegalStateException("Cannot remove while browsing"); } private void doQuery(String cursor) throws AlgoliaException { String paramsString = params.getQueryString(); if (cursor != null) { try { paramsString += (paramsString.length() > 0 ? "&" : "") + "cursor=" + URLEncoder.encode(cursor, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } this.answer = client.getRequest("/1/indexes/" + encodedIndexName + "/browse" + ((paramsString.length() > 0) ? ("?" + paramsString) : ""), true, requestOptions); } } }