From 06d31abe3065205c87447687ad87c05e602c97d7 Mon Sep 17 00:00:00 2001 From: renealr Date: Mon, 19 Mar 2018 10:50:27 -0400 Subject: add endpoint to query suggestion via ES Added logic to ensure that search data service is able to handle suggests type request Issue-ID: AAI-896 Change-Id: I11b3e8dadc5f7023017f01055c24e9c014813eb5 Signed-off-by: renealr --- .../java/org/onap/aai/sa/rest/DocumentApi.java | 224 +++++--- .../org/onap/aai/sa/rest/SearchServiceApi.java | 126 ++--- .../elasticsearch/dao/DocumentStoreInterface.java | 22 +- .../dao/ElasticSearchHttpController.java | 618 +++++++++++---------- .../entity/SearchOperationResult.java | 13 +- .../sa/searchdbabstraction/entity/SuggestHit.java | 57 ++ .../sa/searchdbabstraction/entity/SuggestHits.java | 50 ++ .../searchapi/SuggestionStatement.java | 94 ++++ 8 files changed, 755 insertions(+), 449 deletions(-) create mode 100644 src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHit.java create mode 100644 src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHits.java create mode 100644 src/main/java/org/onap/aai/sa/searchdbabstraction/searchapi/SuggestionStatement.java (limited to 'src/main/java/org') diff --git a/src/main/java/org/onap/aai/sa/rest/DocumentApi.java b/src/main/java/org/onap/aai/sa/rest/DocumentApi.java index 0ec29a2..63109ef 100644 --- a/src/main/java/org/onap/aai/sa/rest/DocumentApi.java +++ b/src/main/java/org/onap/aai/sa/rest/DocumentApi.java @@ -31,6 +31,7 @@ import org.onap.aai.sa.searchdbabstraction.entity.DocumentOperationResult; import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult; import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs; import org.onap.aai.sa.searchdbabstraction.searchapi.SearchStatement; +import org.onap.aai.sa.searchdbabstraction.searchapi.SuggestionStatement; import org.onap.aai.cl.api.LogFields; import org.onap.aai.cl.api.LogLine; import org.onap.aai.cl.api.Logger; @@ -47,20 +48,19 @@ public class DocumentApi { private static final String REQUEST_HEADER_RESOURCE_VERSION = "If-Match"; private static final String RESPONSE_HEADER_RESOURCE_VERSION = "ETag"; private static final String REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION = "X-CreateIndex"; - + protected SearchServiceApi searchService = null; private Logger logger = LoggerFactory.getInstance().getLogger(DocumentApi.class.getName()); - private Logger auditLogger = LoggerFactory.getInstance() - .getAuditLogger(DocumentApi.class.getName()); + private Logger auditLogger = + LoggerFactory.getInstance().getAuditLogger(DocumentApi.class.getName()); public DocumentApi(SearchServiceApi searchService) { this.searchService = searchService; } public Response processPost(String content, HttpServletRequest request, HttpHeaders headers, - HttpServletResponse httpResponse, String index, - DocumentStoreInterface documentStore) { + HttpServletResponse httpResponse, String index, DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); @@ -77,8 +77,7 @@ public class DocumentApi { isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST, ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "DocumentApi.processPost", + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPost", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -90,7 +89,8 @@ public class DocumentApi { DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl(); document.setContent(content); - DocumentOperationResult result = documentStore.createDocument(index, document, implicitlyCreateIndex(headers)); + DocumentOperationResult result = + documentStore.createDocument(index, document, implicitlyCreateIndex(headers)); String output = null; if (result.getResultCode() >= 200 && result.getResultCode() <= 299) { output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getDocument()); @@ -117,8 +117,8 @@ public class DocumentApi { } public Response processPut(String content, HttpServletRequest request, HttpHeaders headers, - HttpServletResponse httpResponse, String index, - String id, DocumentStoreInterface documentStore) { + HttpServletResponse httpResponse, String index, String id, + DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); @@ -135,8 +135,7 @@ public class DocumentApi { isValid = searchService.validateRequest(headers, request, ApiUtils.Action.PUT, ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "DocumentApi.processPut", + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processPut", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -145,8 +144,8 @@ public class DocumentApi { return handleError(request, content, Status.FORBIDDEN); } - String resourceVersion = headers.getRequestHeaders() - .getFirst(REQUEST_HEADER_RESOURCE_VERSION); + String resourceVersion = + headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION); DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl(); document.setId(id); @@ -185,8 +184,8 @@ public class DocumentApi { } public Response processDelete(String content, HttpServletRequest request, HttpHeaders headers, - HttpServletResponse httpResponse, String index, String id, - DocumentStoreInterface documentStore) { + HttpServletResponse httpResponse, String index, String id, + DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); @@ -199,8 +198,7 @@ public class DocumentApi { isValid = searchService.validateRequest(headers, request, ApiUtils.Action.DELETE, ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "DocumentApi.processDelete", + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processDelete", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -209,8 +207,8 @@ public class DocumentApi { return handleError(request, content, Status.FORBIDDEN); } - String resourceVersion = headers.getRequestHeaders() - .getFirst(REQUEST_HEADER_RESOURCE_VERSION); + String resourceVersion = + headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION); if (resourceVersion == null || resourceVersion.isEmpty()) { return handleError(request, "Request header 'If-Match' missing", javax.ws.rs.core.Response.Status.BAD_REQUEST); @@ -251,8 +249,8 @@ public class DocumentApi { } public Response processGet(String content, HttpServletRequest request, HttpHeaders headers, - HttpServletResponse httpResponse, String index, String id, - DocumentStoreInterface documentStore) { + HttpServletResponse httpResponse, String index, String id, + DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); @@ -265,8 +263,7 @@ public class DocumentApi { isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET, ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "DocumentApi.processGet", + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "DocumentApi.processGet", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -275,8 +272,8 @@ public class DocumentApi { return handleError(request, content, Status.FORBIDDEN); } - String resourceVersion = headers.getRequestHeaders() - .getFirst(REQUEST_HEADER_RESOURCE_VERSION); + String resourceVersion = + headers.getRequestHeaders().getFirst(REQUEST_HEADER_RESOURCE_VERSION); DocumentStoreDataEntityImpl document = new DocumentStoreDataEntityImpl(); document.setId(id); @@ -308,8 +305,7 @@ public class DocumentApi { } public Response processSearchWithGet(String content, HttpServletRequest request, - HttpHeaders headers, String index, - String queryText, DocumentStoreInterface documentStore) { + HttpHeaders headers, String index, String queryText, DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); @@ -323,8 +319,7 @@ public class DocumentApi { isValid = searchService.validateRequest(headers, request, ApiUtils.Action.GET, ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "processSearchWithGet", + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processSearchWithGet", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -336,8 +331,8 @@ public class DocumentApi { SearchOperationResult result = documentStore.search(index, queryText); String output = null; if (result.getResultCode() >= 200 && result.getResultCode() <= 299) { - output = mapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(result.getSearchResult()); + output = + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult()); } else { output = result.getError() != null ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError()) @@ -356,14 +351,13 @@ public class DocumentApi { } public Response queryWithGetWithPayload(String content, HttpServletRequest request, - HttpHeaders headers, String index, - DocumentStoreInterface documentStore) { + HttpHeaders headers, String index, DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); - logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET", (request != null) - ? request.getRequestURL().toString() : ""); + logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "GET", + (request != null) ? request.getRequestURL().toString() : ""); if (logger.isDebugEnabled()) { logger.debug("Request Body: " + content); } @@ -371,14 +365,13 @@ public class DocumentApi { } public Response processSearchWithPost(String content, HttpServletRequest request, - HttpHeaders headers, String index, - DocumentStoreInterface documentStore) { + HttpHeaders headers, String index, DocumentStoreInterface documentStore) { // Initialize the MDC Context for logging purposes. ApiUtils.initMdcContext(request, headers); - logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST", (request != null) - ? request.getRequestURL().toString() : ""); + logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST", + (request != null) ? request.getRequestURL().toString() : ""); if (logger.isDebugEnabled()) { logger.debug("Request Body: " + content); } @@ -386,18 +379,33 @@ public class DocumentApi { return processQuery(index, content, request, headers, documentStore); } + public Response processSuggestQueryWithPost(String content, HttpServletRequest request, + HttpHeaders headers, String index, DocumentStoreInterface documentStore) { + + // Initialize the MDC Context for logging purposes. + ApiUtils.initMdcContext(request, headers); + + logger.info(SearchDbMsgs.PROCESS_PAYLOAD_QUERY, "POST", + (request != null) ? request.getRequestURL().toString() : ""); + if (logger.isDebugEnabled()) { + logger.debug("Request Body: " + content); + } + + return processSuggestQuery(index, content, request, headers, documentStore); + } + /** - * Common handler for query requests. This is called by both the GET with - * payload and POST with payload variants of the query endpoint. + * Common handler for query requests. This is called by both the GET with payload and POST with + * payload variants of the query endpoint. * - * @param index - The index to be queried against. + * @param index - The index to be queried against. * @param content - The payload containing the query structure. * @param request - The HTTP request. * @param headers - The HTTP headers. * @return - A standard HTTP response. */ private Response processQuery(String index, String content, HttpServletRequest request, - HttpHeaders headers, DocumentStoreInterface documentStore) { + HttpHeaders headers, DocumentStoreInterface documentStore) { try { ObjectMapper mapper = new ObjectMapper(); @@ -415,9 +423,7 @@ public class DocumentApi { ApiUtils.SEARCH_AUTH_POLICY_NAME); } catch (Exception e) { - logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "processQuery", - e.getMessage()); + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage()); return handleError(request, content, Status.FORBIDDEN); } @@ -438,8 +444,8 @@ public class DocumentApi { // Now, submit the search statement, translated into // ElasticSearch syntax, to the document store DAO. - SearchOperationResult result = documentStore.searchWithPayload(index, - searchStatement.toElasticSearch()); + SearchOperationResult result = + documentStore.searchWithPayload(index, searchStatement.toElasticSearch()); String output = null; if (result.getResultCode() >= 200 && result.getResultCode() <= 299) { output = prepareOutput(mapper, result); @@ -461,37 +467,124 @@ public class DocumentApi { } } - /** - * Checks the supplied HTTP headers to see if we should allow the underlying document - * store to implicitly create the index referenced in a document PUT or POST if it - * does not already exist in the data store. + * Common handler for query requests. This is called by both the GET with payload and POST with + * payload variants of the query endpoint. + * + * @param index - The index to be queried against. + * @param content - The payload containing the query structure. + * @param request - The HTTP request. + * @param headers - The HTTP headers. + * @return - A standard HTTP response. + */ + private Response processSuggestQuery(String index, String content, HttpServletRequest request, + HttpHeaders headers, DocumentStoreInterface documentStore) { + + try { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_EMPTY); + + // Make sure that we were supplied a payload before proceeding. + if (content == null) { + return handleError(request, content, Status.BAD_REQUEST); + } + + // Validate that the request has the appropriate authorization. + boolean isValid; + try { + isValid = searchService.validateRequest(headers, request, ApiUtils.Action.POST, + ApiUtils.SEARCH_AUTH_POLICY_NAME); + + } catch (Exception e) { + logger.info(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "processQuery", e.getMessage()); + return handleError(request, content, Status.FORBIDDEN); + } + + if (!isValid) { + return handleError(request, content, Status.FORBIDDEN); + } + + SuggestionStatement suggestionStatement; + + try { + // Marshall the supplied request payload into a search statement + // object. + suggestionStatement = mapper.readValue(content, SuggestionStatement.class); + + } catch (Exception e) { + return handleError(request, e.getMessage(), Status.BAD_REQUEST); + } + + // Now, submit the search statement, translated into + // ElasticSearch syntax, to the document store DAO. + SearchOperationResult result = + documentStore.suggestionQueryWithPayload(index, suggestionStatement.toElasticSearch()); + String output = null; + if (result.getResultCode() >= 200 && result.getResultCode() <= 299) { + output = prepareSuggestOutput(mapper, result); + } else { + output = result.getError() != null + ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getError()) + : result.getFailureCause(); + } + Response response = Response.status(result.getResultCode()).entity(output).build(); + + // Clear the MDC context so that no other transaction inadvertently + // uses our transaction id. + ApiUtils.clearMdcContext(); + + return response; + + } catch (Exception e) { + return handleError(request, e.getMessage(), Status.INTERNAL_SERVER_ERROR); + } + } + + /** + * Checks the supplied HTTP headers to see if we should allow the underlying document store to + * implicitly create the index referenced in a document PUT or POST if it does not already exist + * in the data store. * * @param headers - The HTTP headers to examine. * - * @return - true if the headers indicate that missing indices should be implicitly created, - * false otherwise. + * @return - true if the headers indicate that missing indices should be implicitly created, false + * otherwise. */ private boolean implicitlyCreateIndex(HttpHeaders headers) { - + boolean createIndexIfNotPresent = false; - String implicitIndexCreationHeader = + String implicitIndexCreationHeader = headers.getRequestHeaders().getFirst(REQUEST_HEADER_ALLOW_IMPLICIT_INDEX_CREATION); - - if( (implicitIndexCreationHeader != null) && (implicitIndexCreationHeader.equals("true")) ) { + + if ((implicitIndexCreationHeader != null) && (implicitIndexCreationHeader.equals("true"))) { createIndexIfNotPresent = true; } - + return createIndexIfNotPresent; } - - + private String prepareOutput(ObjectMapper mapper, SearchOperationResult result) throws JsonProcessingException { StringBuffer output = new StringBuffer(); output.append("{\r\n\"searchResult\":"); - output.append(mapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(result.getSearchResult())); + output.append( + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSearchResult())); + AggregationResults aggs = result.getAggregationResult(); + if (aggs != null) { + output.append(",\r\n\"aggregationResult\":"); + output.append(mapper.setSerializationInclusion(Include.NON_NULL) + .writerWithDefaultPrettyPrinter().writeValueAsString(aggs)); + } + output.append("\r\n}"); + return output.toString(); + } + + private String prepareSuggestOutput(ObjectMapper mapper, SearchOperationResult result) + throws JsonProcessingException { + StringBuffer output = new StringBuffer(); + output.append("{\r\n\"searchResult\":"); + output.append( + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.getSuggestResult())); AggregationResults aggs = result.getAggregationResult(); if (aggs != null) { output.append(",\r\n\"aggregationResult\":"); @@ -514,8 +607,7 @@ public class DocumentApi { (request != null) ? request.getRemoteHost() : "", Integer.toString(status.getStatusCode())); auditLogger.info(SearchDbMsgs.PROCESS_REST_REQUEST, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, status.getStatusCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, status.getReasonPhrase()), (request != null) ? request.getMethod() : "", (request != null) ? request.getRequestURL().toString() : "", diff --git a/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java b/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java index 249f6b1..99b4615 100644 --- a/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java +++ b/src/main/java/org/onap/aai/sa/rest/SearchServiceApi.java @@ -41,17 +41,14 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - public class SearchServiceApi { /** - * The Data Access Object that we will use to interact with the - * document store. + * The Data Access Object that we will use to interact with the document store. */ protected DocumentStoreInterface documentStore = null; protected ApiUtils apiUtils = null; - /** * Create a new instance of the end point. */ @@ -61,7 +58,6 @@ public class SearchServiceApi { init(); } - /** * Performs all one-time initialization required for the end point. */ @@ -76,10 +72,8 @@ public class SearchServiceApi { @PUT @Path("/indexes/{index}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processCreateIndex(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processCreateIndex(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our index API to create the index. IndexApi indexApi = new IndexApi(this); @@ -89,10 +83,8 @@ public class SearchServiceApi { @PUT @Path("/indexes/dynamic/{index}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processCreateDynamicIndex(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processCreateDynamicIndex(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our index API to create the index. IndexApi indexApi = new IndexApi(this); @@ -102,104 +94,84 @@ public class SearchServiceApi { @DELETE @Path("/indexes/{index}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processDeleteIndex(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processDeleteIndex(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our index API to delete the index. IndexApi indexApi = new IndexApi(this); return indexApi.processDelete(index, request, headers, documentStore); } - @GET @Path("/indexes/{index}/documents/{id}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processGetDocument(String requestBody, - @Context HttpServletRequest request, - @Context HttpServletResponse httpResponse, - @Context HttpHeaders headers, - @PathParam("index") String index, - @PathParam("id") String id) { + public Response processGetDocument(String requestBody, @Context HttpServletRequest request, + @Context HttpServletResponse httpResponse, @Context HttpHeaders headers, + @PathParam("index") String index, @PathParam("id") String id) { // Forward the request to our document API to retrieve the document. DocumentApi documentApi = new DocumentApi(this); - return documentApi.processGet(requestBody, request, headers, httpResponse, - index, id, documentStore); + return documentApi.processGet(requestBody, request, headers, httpResponse, index, id, + documentStore); } @POST @Path("/indexes/{index}/documents") @Consumes({MediaType.APPLICATION_JSON}) - public Response processCreateDocWithoutId(String requestBody, - @Context HttpServletRequest request, - @Context HttpServletResponse httpResponse, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processCreateDocWithoutId(String requestBody, @Context HttpServletRequest request, + @Context HttpServletResponse httpResponse, @Context HttpHeaders headers, + @PathParam("index") String index) { // Forward the request to our document API to create the document. DocumentApi documentApi = new DocumentApi(this); - return documentApi.processPost(requestBody, request, headers, httpResponse, - index, documentStore); + return documentApi.processPost(requestBody, request, headers, httpResponse, index, + documentStore); } @PUT @Path("/indexes/{index}/documents/{id}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processUpsertDoc(String requestBody, - @Context HttpServletRequest request, - @Context HttpServletResponse httpResponse, - @Context HttpHeaders headers, - @PathParam("index") String index, - @PathParam("id") String id) { + public Response processUpsertDoc(String requestBody, @Context HttpServletRequest request, + @Context HttpServletResponse httpResponse, @Context HttpHeaders headers, + @PathParam("index") String index, @PathParam("id") String id) { // Forward the request to our document API to upsert the document. DocumentApi documentApi = new DocumentApi(this); - return documentApi.processPut(requestBody, request, headers, httpResponse, - index, id, documentStore); + return documentApi.processPut(requestBody, request, headers, httpResponse, index, id, + documentStore); } @DELETE @Path("/indexes/{index}/documents/{id}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processDeleteDoc(String requestBody, - @Context HttpServletRequest request, - @Context HttpServletResponse httpResponse, - @Context HttpHeaders headers, - @PathParam("index") String index, - @PathParam("id") String id) { + public Response processDeleteDoc(String requestBody, @Context HttpServletRequest request, + @Context HttpServletResponse httpResponse, @Context HttpHeaders headers, + @PathParam("index") String index, @PathParam("id") String id) { // Forward the request to our document API to delete the document. DocumentApi documentApi = new DocumentApi(this); - return documentApi.processDelete(requestBody, request, headers, httpResponse, - index, id, documentStore); + return documentApi.processDelete(requestBody, request, headers, httpResponse, index, id, + documentStore); } - @GET @Path("/indexes/{index}/query/{queryText}") @Consumes({MediaType.APPLICATION_JSON}) - public Response processInlineQuery(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index, - @PathParam("queryText") String queryText) { + public Response processInlineQuery(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index, + @PathParam("queryText") String queryText) { // Forward the request to our document API to delete the document. DocumentApi documentApi = new DocumentApi(this); - return documentApi.processSearchWithGet(requestBody, request, headers, - index, queryText, documentStore); + return documentApi.processSearchWithGet(requestBody, request, headers, index, queryText, + documentStore); } - @GET @Path("/indexes/{index}/query") @Consumes({MediaType.APPLICATION_JSON}) - public Response processQueryWithGet(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processQueryWithGet(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our document API to delete the document. DocumentApi documentApi = new DocumentApi(this); @@ -209,34 +181,40 @@ public class SearchServiceApi { @POST @Path("/indexes/{index}/query") @Consumes({MediaType.APPLICATION_JSON}) - public Response processQuery(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processQuery(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our document API to delete the document. DocumentApi documentApi = new DocumentApi(this); return documentApi.processSearchWithPost(requestBody, request, headers, index, documentStore); } + @POST + @Path("/indexes/{index}/suggest") + @Consumes({MediaType.APPLICATION_JSON}) + public Response processSuggestQuery(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { + + // Forward the request to our document API to query suggestions in the + // document. + DocumentApi documentApi = new DocumentApi(this); + return documentApi.processSuggestQueryWithPost(requestBody, request, headers, index, + documentStore); + } @POST @Path("/bulk") @Consumes({MediaType.APPLICATION_JSON}) - public Response processBulkRequest(String requestBody, - @Context HttpServletRequest request, - @Context HttpHeaders headers, - @PathParam("index") String index) { + public Response processBulkRequest(String requestBody, @Context HttpServletRequest request, + @Context HttpHeaders headers, @PathParam("index") String index) { // Forward the request to our document API to delete the document. BulkApi bulkApi = new BulkApi(this); return bulkApi.processPost(requestBody, request, headers, documentStore, apiUtils); } - protected boolean validateRequest(HttpHeaders headers, - HttpServletRequest req, - Action action, - String authPolicyFunctionName) throws Exception { + protected boolean validateRequest(HttpHeaders headers, HttpServletRequest req, Action action, + String authPolicyFunctionName) throws Exception { SearchDbServiceAuth serviceAuth = new SearchDbServiceAuth(); diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java index 2f3350c..a3a8e8e 100644 --- a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/DocumentStoreInterface.java @@ -20,7 +20,6 @@ */ package org.onap.aai.sa.searchdbabstraction.elasticsearch.dao; - import org.onap.aai.sa.rest.BulkRequest; import org.onap.aai.sa.searchdbabstraction.elasticsearch.exception.DocumentStoreOperationException; import org.onap.aai.sa.searchdbabstraction.entity.DocumentOperationResult; @@ -28,7 +27,6 @@ import org.onap.aai.sa.searchdbabstraction.entity.OperationResult; import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult; import org.onap.aai.sa.rest.DocumentSchema; - public interface DocumentStoreInterface { public OperationResult createIndex(String index, DocumentSchema documentSchema); @@ -37,13 +35,11 @@ public interface DocumentStoreInterface { public OperationResult deleteIndex(String indexName) throws DocumentStoreOperationException; - public DocumentOperationResult createDocument(String indexName, - DocumentStoreDataEntity document, - boolean allowImplicitIndexCreation) throws DocumentStoreOperationException; + public DocumentOperationResult createDocument(String indexName, DocumentStoreDataEntity document, + boolean allowImplicitIndexCreation) throws DocumentStoreOperationException; - public DocumentOperationResult updateDocument(String indexName, - DocumentStoreDataEntity document, - boolean allowImplicitIndexCreation) throws DocumentStoreOperationException; + public DocumentOperationResult updateDocument(String indexName, DocumentStoreDataEntity document, + boolean allowImplicitIndexCreation) throws DocumentStoreOperationException; public DocumentOperationResult deleteDocument(String indexName, DocumentStoreDataEntity document) throws DocumentStoreOperationException; @@ -57,14 +53,14 @@ public interface DocumentStoreInterface { public SearchOperationResult searchWithPayload(String indexName, String query) throws DocumentStoreOperationException; + public SearchOperationResult suggestionQueryWithPayload(String indexName, String query) + throws DocumentStoreOperationException; /** - * Forwards a set of operations to the document store as a single, bulk - * request. + * Forwards a set of operations to the document store as a single, bulk request. * - * @param anIndex - The index to apply the operations to. - * @param operations - A java object containing the set of operations to - * be performed. + * @param anIndex - The index to apply the operations to. + * @param operations - A java object containing the set of operations to be performed. * @return - An operation result. * @throws DocumentStoreOperationException */ diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java index ef141ec..00f66e0 100644 --- a/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/elasticsearch/dao/ElasticSearchHttpController.java @@ -46,6 +46,8 @@ import org.onap.aai.sa.searchdbabstraction.entity.OperationResult; import org.onap.aai.sa.searchdbabstraction.entity.SearchHit; import org.onap.aai.sa.searchdbabstraction.entity.SearchHits; import org.onap.aai.sa.searchdbabstraction.entity.SearchOperationResult; +import org.onap.aai.sa.searchdbabstraction.entity.SuggestHit; +import org.onap.aai.sa.searchdbabstraction.entity.SuggestHits; import org.onap.aai.sa.searchdbabstraction.logging.SearchDbMsgs; import org.onap.aai.sa.searchdbabstraction.util.AggregationParsingUtil; import org.onap.aai.sa.searchdbabstraction.util.DocumentSchemaUtil; @@ -79,10 +81,9 @@ import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import javax.ws.rs.core.Response.Status; - /** - * This class has the Elasticsearch implementation of the - * DB operations defined in DocumentStoreInterface. + * This class has the Elasticsearch implementation of the DB operations defined in + * DocumentStoreInterface. */ public class ElasticSearchHttpController implements DocumentStoreInterface { @@ -97,10 +98,10 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { private static final String INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT = "Internal Error: ElasticSearch operation fault occurred"; - private static final Logger logger = LoggerFactory.getInstance() - .getLogger(ElasticSearchHttpController.class.getName()); - private static final Logger metricsLogger = LoggerFactory.getInstance() - .getMetricsLogger(ElasticSearchHttpController.class.getName()); + private static final Logger logger = + LoggerFactory.getInstance().getLogger(ElasticSearchHttpController.class.getName()); + private static final Logger metricsLogger = + LoggerFactory.getInstance().getMetricsLogger(ElasticSearchHttpController.class.getName()); private final ElasticSearchConfig config; private static final String DEFAULT_TYPE = "default"; @@ -121,8 +122,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { properties.load(new FileInputStream(file)); } catch (Exception e) { logger.error(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, - "ElasticSearchHTTPController.getInstance", - e.getLocalizedMessage()); + "ElasticSearchHTTPController.getInstance", e.getLocalizedMessage()); } ElasticSearchConfig config = new ElasticSearchConfig(properties); @@ -142,12 +142,11 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { checkConnection(); logger.info(SearchDbMsgs.ELASTIC_SEARCH_CONNECTION_SUCCESS, getFullUrl("", false)); } catch (Exception e) { - logger.error(SearchDbMsgs.ELASTIC_SEARCH_CONNECTION_FAILURE, null, e, - getFullUrl("", false), e.getMessage()); + logger.error(SearchDbMsgs.ELASTIC_SEARCH_CONNECTION_FAILURE, null, e, getFullUrl("", false), + e.getMessage()); } } - public AnalysisConfiguration getAnalysisConfig() { return analysisConfig; } @@ -162,9 +161,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Submit the request to ElasticSearch to create the index using a // default document type. - result = createTable(index, - DEFAULT_TYPE, - analysisConfig.getEsIndexSettings(), + result = createTable(index, DEFAULT_TYPE, analysisConfig.getEsIndexSettings(), DocumentSchemaUtil.generateDocumentMappings(documentSchema)); // ElasticSearch will return us a 200 code on success when we @@ -172,7 +169,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { result.setResultCode((result.getResultCode() == 200) ? 201 : result.getResultCode()); if (isSuccess(result)) { result.setResult("{\"url\": \"" + ApiUtils.buildIndexUri(index) + "\"}"); - //result.setResult("{\"index\": \"" + index + ", \"type\": \"" + DEFAULT_TYPE + "\"}"); + // result.setResult("{\"index\": \"" + index + ", \"type\": \"" + // + DEFAULT_TYPE + "\"}"); } } catch (DocumentStoreOperationException e) { @@ -187,10 +185,10 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { public OperationResult createDynamicIndex(String index, String dynamicSchema) { OperationResult result = new OperationResult(); result.setResultCode(500); - + try { result = createTable(index, dynamicSchema); - + // ElasticSearch will return us a 200 code on success when we // want to report a 201, so translate the result here. result.setResultCode((result.getResultCode() == 200) ? 201 : result.getResultCode()); @@ -200,14 +198,14 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } catch (DocumentStoreOperationException e) { result.setFailureCause("Document store operation failure. Cause: " + e.getMessage()); } - + return result; } @Override public OperationResult deleteIndex(String indexName) throws DocumentStoreOperationException { - //Initialize operation result with a failure codes / fault string + // Initialize operation result with a failure codes / fault string OperationResult opResult = new OperationResult(); opResult.setResultCode(500); opResult.setResult(INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT); @@ -231,18 +229,15 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.DELETE_INDEX_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName); + override, indexName); shutdownConnection(conn); return opResult; } - private OperationResult checkConnection() throws Exception { String fullUrl = getFullUrl("/_cluster/health", false); @@ -288,7 +283,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { try { inputstream = connection.getInputStream(); } catch (IOException e) { - logger.debug(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "shutdownConnection", e.getLocalizedMessage()); + logger.debug(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "shutdownConnection", + e.getLocalizedMessage()); } finally { if (inputstream != null) { try { @@ -303,7 +299,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { try { outputstream = connection.getOutputStream(); } catch (IOException e) { - logger.debug(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "shutdownConnection", e.getLocalizedMessage()); + logger.debug(SearchDbMsgs.EXCEPTION_DURING_METHOD_CALL, "shutdownConnection", + e.getLocalizedMessage()); } finally { if (outputstream != null) { try { @@ -318,10 +315,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { connection.disconnect(); } - //@Override - protected OperationResult createTable(String indexName, String typeName, - String indexSettings, String indexMappings) - throws DocumentStoreOperationException { + // @Override + protected OperationResult createTable(String indexName, String typeName, String indexSettings, + String indexMappings) throws DocumentStoreOperationException { if (indexSettings == null) { logger.debug("No settings provided."); @@ -371,32 +367,31 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.CREATE_INDEX_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResultCode()), - override, - indexName); + override, indexName); return opResult; } - /** - * Will send the passed in JSON payload to Elasticsearch using the - * provided index name in an attempt to create the index. + /** + * Will send the passed in JSON payload to Elasticsearch using the provided index name in an + * attempt to create the index. * * @param indexName - The name of the index to be created * @param settingsAndMappings - The actual JSON object that will define the index * @return - The operation result of writing into Elasticsearch * @throws DocumentStoreOperationException */ - protected OperationResult createTable(String indexName, String settingsAndMappings) throws DocumentStoreOperationException { + protected OperationResult createTable(String indexName, String settingsAndMappings) + throws DocumentStoreOperationException { OperationResult result = new OperationResult(); result.setResultCode(500); result.setResult(INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT); - + // Grab the current time so we can use it to generate a metrics log. MdcOverride override = getStartTime(new MdcOverride()); - + String fullUrl = getFullUrl("/" + indexName + "/", false); HttpURLConnection conn = initializeConnection(fullUrl); @@ -406,36 +401,38 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { shutdownConnection(conn); throw new DocumentStoreOperationException("Failed to set HTTP request method to PUT.", e); } - + attachContent(conn, settingsAndMappings); handleResponse(conn, result); - + // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.CREATE_INDEX_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, result.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResultCode()), - override, - indexName); - + override, indexName); + + shutdownConnection(conn); + return result; } @Override - public DocumentOperationResult createDocument(String indexName, - DocumentStoreDataEntity document, - boolean allowImplicitIndexCreation) - throws DocumentStoreOperationException { - - if(!allowImplicitIndexCreation) { - - // Before we do anything, make sure that the specified index actually exists in the - // document store - we don't want to rely on ElasticSearch to fail the document - // create because it could be configured to implicitly create a non-existent index, - // which can lead to hard-to-debug behaviour with queries down the road. + public DocumentOperationResult createDocument(String indexName, DocumentStoreDataEntity document, + boolean allowImplicitIndexCreation) throws DocumentStoreOperationException { + + if (!allowImplicitIndexCreation) { + + // Before we do anything, make sure that the specified index + // actually exists in the + // document store - we don't want to rely on ElasticSearch to fail + // the document + // create because it could be configured to implicitly create a + // non-existent index, + // which can lead to hard-to-debug behaviour with queries down the + // road. OperationResult indexExistsResult = checkIndexExistence(indexName); if ((indexExistsResult.getResultCode() < 200) || (indexExistsResult.getResultCode() >= 300)) { - + DocumentOperationResult opResult = new DocumentOperationResult(); opResult.setResultCode(Status.NOT_FOUND.getStatusCode()); opResult.setResult("Document Index '" + indexName + "' does not exist."); @@ -443,7 +440,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { return opResult; } } - + if (document.getId() == null || document.getId().isEmpty()) { return createDocumentWithoutId(indexName, document); } else { @@ -452,17 +449,16 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } private DocumentOperationResult createDocumentWithId(String indexName, - DocumentStoreDataEntity document) - throws DocumentStoreOperationException { + DocumentStoreDataEntity document) throws DocumentStoreOperationException { // check if the document already exists DocumentOperationResult opResult = checkDocumentExistence(indexName, document.getId()); - if (opResult.getResultCode() != Status.NOT_FOUND.getStatusCode()) { if (opResult.getResultCode() == Status.OK.getStatusCode()) { opResult.setFailureCause("A document with the same id already exists."); } else { - opResult.setFailureCause("Failed to verify a document with the specified id does not already exist."); + opResult.setFailureCause( + "Failed to verify a document with the specified id does not already exist."); } opResult.setResultCode(Status.CONFLICT.getStatusCode()); return opResult; @@ -476,8 +472,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Grab the current time so we can use it to generate a metrics log. MdcOverride override = getStartTime(new MdcOverride()); - String fullUrl = getFullUrl("/" + indexName + "/" + DEFAULT_TYPE - + "/" + document.getId(), false); + String fullUrl = + getFullUrl("/" + indexName + "/" + DEFAULT_TYPE + "/" + document.getId(), false); HttpURLConnection conn = initializeConnection(fullUrl); try { @@ -496,11 +492,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.CREATE_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName); + override, indexName); shutdownConnection(conn); @@ -509,8 +503,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } private DocumentOperationResult createDocumentWithoutId(String indexName, - DocumentStoreDataEntity document) - throws DocumentStoreOperationException { + DocumentStoreDataEntity document) throws DocumentStoreOperationException { DocumentOperationResult response = new DocumentOperationResult(); // Initialize operation result with a failure codes / fault string @@ -539,11 +532,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.CREATE_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, response.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, response.getResult()), - override, - indexName); + override, indexName); shutdownConnection(conn); @@ -558,8 +549,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { attachContent(conn, doc.getContentInJson()); } - private DocumentOperationResult checkDocumentExistence(String indexName, - String docId) + private DocumentOperationResult checkDocumentExistence(String indexName, String docId) throws DocumentStoreOperationException { DocumentOperationResult opResult = new DocumentOperationResult(); @@ -586,7 +576,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { resultCode = conn.getResponseCode(); } catch (IOException e) { shutdownConnection(conn); - throw new DocumentStoreOperationException("Failed to get the response code from the connection.", e); + throw new DocumentStoreOperationException( + "Failed to get the response code from the connection.", e); } logger.debug("Response Code : " + resultCode); @@ -595,12 +586,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.GET_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName, - docId); + override, indexName, docId); shutdownConnection(conn); @@ -608,20 +596,22 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } @Override - public DocumentOperationResult updateDocument(String indexName, - DocumentStoreDataEntity document, - boolean allowImplicitIndexCreation) - throws DocumentStoreOperationException { - - if(!allowImplicitIndexCreation) { - - // Before we do anything, make sure that the specified index actually exists in the - // document store - we don't want to rely on ElasticSearch to fail the document - // create because it could be configured to implicitly create a non-existent index, - // which can lead to hard-to-debug behaviour with queries down the road. + public DocumentOperationResult updateDocument(String indexName, DocumentStoreDataEntity document, + boolean allowImplicitIndexCreation) throws DocumentStoreOperationException { + + if (!allowImplicitIndexCreation) { + + // Before we do anything, make sure that the specified index + // actually exists in the + // document store - we don't want to rely on ElasticSearch to fail + // the document + // create because it could be configured to implicitly create a + // non-existent index, + // which can lead to hard-to-debug behaviour with queries down the + // road. OperationResult indexExistsResult = checkIndexExistence(indexName); if ((indexExistsResult.getResultCode() < 200) || (indexExistsResult.getResultCode() >= 300)) { - + DocumentOperationResult opResult = new DocumentOperationResult(); opResult.setResultCode(Status.NOT_FOUND.getStatusCode()); opResult.setResult("Document Index '" + indexName + "' does not exist."); @@ -629,7 +619,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { return opResult; } } - + DocumentOperationResult opResult = new DocumentOperationResult(); // Initialize operation result with a failure codes / fault string @@ -659,12 +649,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.UPDATE_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName, - document.getId()); + override, indexName, document.getId()); shutdownConnection(conn); @@ -698,7 +685,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { handleResponse(conn, opResult); buildDocumentResult(opResult, indexName); - //supress the etag and url in response for delete as they are not required + // supress the etag and url in response for delete as they are not + // required if (opResult.getDocument() != null) { opResult.getDocument().setEtag(null); opResult.getDocument().setUrl(null); @@ -706,12 +694,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.DELETE_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResult()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResult()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResultCode()), - override, - indexName, - document.getId()); + override, indexName, document.getId()); shutdownConnection(conn); @@ -746,12 +731,9 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.GET_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName, - document.getId()); + override, indexName, document.getId()); shutdownConnection(conn); @@ -785,14 +767,10 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { handleResponse(conn, opResult); buildSearchResult(opResult, indexName); - metricsLogger.info(SearchDbMsgs.QUERY_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName, - queryString); + override, indexName, queryString); return opResult; } @@ -832,12 +810,54 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { buildSearchResult(opResult, indexName); metricsLogger.info(SearchDbMsgs.QUERY_DOCUMENT_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), + override, indexName, query); + + shutdownConnection(conn); + + return opResult; + } + + public SearchOperationResult suggestionQueryWithPayload(String indexName, String query) + throws DocumentStoreOperationException { + + SearchOperationResult opResult = new SearchOperationResult(); + + if (logger.isDebugEnabled()) { + logger.debug("Querying Suggestion index: " + indexName + " with query string: " + query); + } + + // Initialize operation result with a failure codes / fault string + opResult.setResultCode(500); + opResult.setResult(INTERNAL_SERVER_ERROR_ELASTIC_SEARCH_OPERATION_FAULT); + + String fullUrl = getFullUrl("/" + indexName + "/_suggest", false); + + // Grab the current time so we can use it to generate a metrics log. + MdcOverride override = getStartTime(new MdcOverride()); + + HttpURLConnection conn = initializeConnection(fullUrl); + + try { + conn.setRequestMethod("POST"); + } catch (ProtocolException e) { + shutdownConnection(conn); + throw new DocumentStoreOperationException("Failed to set HTTP request method to POST.", e); + } + + attachContent(conn, query); + + logger.debug("\nsearch(), Sending 'POST' request to URL : " + conn.getURL()); + logger.debug("Request body = Elasticsearch query = " + query); + + handleResponse(conn, opResult); + buildSuggestResult(opResult, indexName); + + metricsLogger.info(SearchDbMsgs.QUERY_DOCUMENT_TIME, + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResult()), - override, - indexName, - query); + override, indexName, query); shutdownConnection(conn); @@ -897,14 +917,16 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { resultCode = conn.getResponseCode(); } catch (IOException e) { shutdownConnection(conn); - throw new DocumentStoreOperationException("Failed to get the response code from the connection.", e); + throw new DocumentStoreOperationException( + "Failed to get the response code from the connection.", e); } logger.debug("Response Code : " + resultCode); InputStream inputStream = null; - if (!(resultCode >= 200 && resultCode <= 299)) { // 2xx response indicates success + if (!(resultCode >= 200 && resultCode <= 299)) { // 2xx response + // indicates success inputStream = conn.getErrorStream(); } else { try { @@ -963,9 +985,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } /** - * This convenience method gets the current system time and stores - * it in an attribute in the supplied {@link MdcOverride} object so - * that it can be used later by the metrics logger. + * This convenience method gets the current system time and stores it in an attribute in the + * supplied {@link MdcOverride} object so that it can be used later by the metrics logger. * * @param override - The {@link MdcOverride} object to update. * @return - The supplied {@link MdcOverride} object. @@ -983,7 +1004,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Return the MdcOverride object that we were passed. // This looks odd, but it allows us to do stuff like: // - // MdcOverride ov = getStartTime(new MdcOverride()) + // MdcOverride ov = getStartTime(new MdcOverride()) // // which is quite handy, but also allows us to pass in an existing // MdcOverride object which already has some attributes set. @@ -995,12 +1016,10 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { return isSuccessCode(result.getResultCode()); } - private boolean isSuccessCode(int statusCode) { return ((statusCode >= 200) && (statusCode < 300)); } - @Override public OperationResult performBulkOperations(BulkRequest[] requests) throws DocumentStoreOperationException { @@ -1055,32 +1074,34 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { logger.debug(Throwables.getStackTraceAsString(e)); } - throw new DocumentStoreOperationException("Failed to open connection to document store. Cause: " - + e.getMessage(), e); + throw new DocumentStoreOperationException( + "Failed to open connection to document store. Cause: " + e.getMessage(), e); } StringBuilder bulkResult = new StringBuilder(128); try { // Create an output stream to write our request to. - OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); - ; + OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());; if (logger.isDebugEnabled()) { logger.debug("ESController: Sending 'BULK' request to " + conn.getURL()); - logger.debug("ESController: operations: " + esOperationSet.toString().replaceAll("\n", - "\\n")); + logger.debug( + "ESController: operations: " + esOperationSet.toString().replaceAll("\n", "\\n")); } - // Write the resulting request string to our output stream. (this sends the request to ES?) + // Write the resulting request string to our output stream. + // (this sends the request to ES?) out.write(esOperationSet.toString()); out.close(); - // Open an input stream on our connection in order to read back the results. + // Open an input stream on our connection in order to read back + // the results. InputStream is = conn.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader(is); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); - // Read the contents of the input stream into our result string... + // Read the contents of the input stream into our result + // string... String esResponseString = null; while ((esResponseString = bufferedreader.readLine()) != null) { @@ -1096,13 +1117,13 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { logger.debug(sw.toString()); } - throw new DocumentStoreOperationException("Failure interacting with document store. Cause: " - + e.getMessage(), e); + throw new DocumentStoreOperationException( + "Failure interacting with document store. Cause: " + e.getMessage(), e); } if (logger.isDebugEnabled()) { - logger.debug("ESController: Received result string from ElasticSearch: = " - + bulkResult.toString()); + logger.debug( + "ESController: Received result string from ElasticSearch: = " + bulkResult.toString()); } // ...and marshal the resulting string into a Java object. @@ -1116,8 +1137,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { logger.debug(Throwables.getStackTraceAsString(e)); } - throw new DocumentStoreOperationException("Failed to marshal response body. Cause: " - + e.getMessage(), e); + throw new DocumentStoreOperationException( + "Failed to marshal response body. Cause: " + e.getMessage(), e); } } @@ -1130,31 +1151,29 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // dumped into the metrics log, so concatenate it. String resultStringForMetricsLog = result.getResult(); if ((result.getResultCode() >= 200) && (result.getResultCode() < 300)) { - resultStringForMetricsLog = resultStringForMetricsLog.substring(0, - Math.max(resultStringForMetricsLog.length(), 85)) + "..."; + resultStringForMetricsLog = + resultStringForMetricsLog.substring(0, Math.max(resultStringForMetricsLog.length(), 85)) + + "..."; } metricsLogger.info(SearchDbMsgs.BULK_OPERATIONS_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, result.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, resultStringForMetricsLog), override); return result; } - /** - * This method converts a {@link BulkRequest} object into a json structure - * which can be understood by ElasticSearch. + * This method converts a {@link BulkRequest} object into a json structure which can be understood + * by ElasticSearch. * * @param request - The request to be performed. - * @param sb - The string builder to append the json data to + * @param sb - The string builder to append the json data to * @throws DocumentStoreOperationException */ private boolean buildEsOperation(BulkRequest request, StringBuilder sb, - List fails) - throws DocumentStoreOperationException { + List fails) throws DocumentStoreOperationException { boolean retVal = true; OperationResult indexExistsResult = null; @@ -1168,11 +1187,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Make sure that we were supplied a document payload. if (request.getOperation().getDocument() == null) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Missing document payload", - request.getIndex(), - request.getId(), - 400, + fails.add(generateRejectionEntry(request.getOperationType(), "Missing document payload", + request.getIndex(), request.getId(), 400, request.getOperation().getMetaData().getUrl())); return false; } @@ -1182,24 +1198,21 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { if (!ApiUtils.validateDocumentUri(request.getOperation().getMetaData().getUrl(), false)) { fails.add(generateRejectionEntry(request.getOperationType(), "Invalid document URL: " + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - "", - 400, - request.getOperation().getMetaData().getUrl())); + request.getIndex(), "", 400, request.getOperation().getMetaData().getUrl())); return false; } // Validate that the specified index actually exists before we // try to perform the create. - if (!indexExists(ApiUtils.extractIndexFromUri(request.getOperation().getMetaData().getUrl()))) { - - fails.add(generateRejectionEntry(request.getOperationType(), - "Specified resource does not exist: " - + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - request.getId(), - 404, - request.getOperation().getMetaData().getUrl())); + if (!indexExists( + ApiUtils.extractIndexFromUri(request.getOperation().getMetaData().getUrl()))) { + + fails + .add(generateRejectionEntry(request.getOperationType(), + "Specified resource does not exist: " + + request.getOperation().getMetaData().getUrl(), + request.getIndex(), request.getId(), 404, + request.getOperation().getMetaData().getUrl())); return false; } @@ -1207,16 +1220,13 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // include it in the bulk operation to Elastic Search if (request.getId() == null) { - sb.append(String.format(BULK_CREATE_WITHOUT_INDEX_TEMPLATE, - request.getIndex(), - DEFAULT_TYPE)); + sb.append( + String.format(BULK_CREATE_WITHOUT_INDEX_TEMPLATE, request.getIndex(), DEFAULT_TYPE)); // Otherwise, we just leave that parameter off and ElasticSearch // will generate one for us. } else { - sb.append(String.format(BULK_CREATE_WITH_INDEX_TEMPLATE, - request.getIndex(), - DEFAULT_TYPE, + sb.append(String.format(BULK_CREATE_WITH_INDEX_TEMPLATE, request.getIndex(), DEFAULT_TYPE, request.getId())); } @@ -1235,11 +1245,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Make sure that we were supplied a document payload. if (request.getOperation().getDocument() == null) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Missing document payload", - request.getIndex(), - request.getId(), - 400, + fails.add(generateRejectionEntry(request.getOperationType(), "Missing document payload", + request.getIndex(), request.getId(), 400, request.getOperation().getMetaData().getUrl())); return false; } @@ -1249,10 +1256,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { if (!ApiUtils.validateDocumentUri(request.getOperation().getMetaData().getUrl(), true)) { fails.add(generateRejectionEntry(request.getOperationType(), "Invalid document URL: " + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - "", - 400, - request.getOperation().getMetaData().getUrl())); + request.getIndex(), "", 400, request.getOperation().getMetaData().getUrl())); return false; } @@ -1260,13 +1264,12 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // try to perform the update. if (!indexExists(request.getIndex())) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Specified resource does not exist: " - + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - request.getId(), - 404, - request.getOperation().getMetaData().getUrl())); + fails + .add(generateRejectionEntry(request.getOperationType(), + "Specified resource does not exist: " + + request.getOperation().getMetaData().getUrl(), + request.getIndex(), request.getId(), 404, + request.getOperation().getMetaData().getUrl())); return false; } @@ -1274,35 +1277,29 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // exists before we try to perform the update. if (!documentExists(request.getIndex(), request.getId())) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Specified resource does not exist: " - + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - request.getId(), - 404, - request.getOperation().getMetaData().getUrl())); + fails + .add(generateRejectionEntry(request.getOperationType(), + "Specified resource does not exist: " + + request.getOperation().getMetaData().getUrl(), + request.getIndex(), request.getId(), 404, + request.getOperation().getMetaData().getUrl())); return false; } - // It is mandatory that a version be supplied for an update operation, + // It is mandatory that a version be supplied for an update + // operation, // so validate that now. if (request.getOperation().getMetaData().getEtag() == null) { fails.add(generateRejectionEntry(request.getOperationType(), - "Missing mandatory ETag field", - request.getIndex(), - request.getId(), - 400, + "Missing mandatory ETag field", request.getIndex(), request.getId(), 400, request.getOperation().getMetaData().getUrl())); return false; } // Generate the update request... - sb.append(String.format(BULK_IMPORT_INDEX_TEMPLATE, - request.getIndex(), - DEFAULT_TYPE, - request.getId(), - request.getOperation().getMetaData().getEtag())); + sb.append(String.format(BULK_IMPORT_INDEX_TEMPLATE, request.getIndex(), DEFAULT_TYPE, + request.getId(), request.getOperation().getMetaData().getEtag())); // ...and append the document that we want to update. try { @@ -1320,10 +1317,7 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { if (!ApiUtils.validateDocumentUri(request.getOperation().getMetaData().getUrl(), true)) { fails.add(generateRejectionEntry(request.getOperationType(), "Invalid document URL: " + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - "", - 400, - request.getOperation().getMetaData().getUrl())); + request.getIndex(), "", 400, request.getOperation().getMetaData().getUrl())); return false; } @@ -1331,13 +1325,12 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // try to perform the delete. if (!indexExists(request.getIndex())) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Specified resource does not exist: " - + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - request.getId(), - 404, - request.getOperation().getMetaData().getUrl())); + fails + .add(generateRejectionEntry(request.getOperationType(), + "Specified resource does not exist: " + + request.getOperation().getMetaData().getUrl(), + request.getIndex(), request.getId(), 404, + request.getOperation().getMetaData().getUrl())); return false; } @@ -1345,35 +1338,29 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // exists before we try to perform the delete. if (!documentExists(request.getIndex(), request.getId())) { - fails.add(generateRejectionEntry(request.getOperationType(), - "Specified resource does not exist: " - + request.getOperation().getMetaData().getUrl(), - request.getIndex(), - request.getId(), - 404, - request.getOperation().getMetaData().getUrl())); + fails + .add(generateRejectionEntry(request.getOperationType(), + "Specified resource does not exist: " + + request.getOperation().getMetaData().getUrl(), + request.getIndex(), request.getId(), 404, + request.getOperation().getMetaData().getUrl())); return false; } - // It is mandatory that a version be supplied for a delete operation, + // It is mandatory that a version be supplied for a delete + // operation, // so validate that now. if (request.getOperation().getMetaData().getEtag() == null) { fails.add(generateRejectionEntry(request.getOperationType(), - "Missing mandatory ETag field", - request.getIndex(), - request.getId(), - 400, + "Missing mandatory ETag field", request.getIndex(), request.getId(), 400, request.getOperation().getMetaData().getUrl())); return false; } // Generate the delete request. - sb.append(String.format(BULK_DELETE_TEMPLATE, - request.getIndex(), - DEFAULT_TYPE, - request.getId(), - request.getOperation().getMetaData().getEtag())); + sb.append(String.format(BULK_DELETE_TEMPLATE, request.getIndex(), DEFAULT_TYPE, + request.getId(), request.getOperation().getMetaData().getEtag())); break; default: } @@ -1397,21 +1384,16 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } /** - * This method constructs a status entry for a bulk operation which has - * been rejected before even sending it to the document store. + * This method constructs a status entry for a bulk operation which has been rejected before even + * sending it to the document store. * * @param rejectReason - A message describing why the operation was rejected. - * @param anId - The identifier associated with the document being - * acted on. - * @param statusCode - An HTTP status code. + * @param anId - The identifier associated with the document being acted on. + * @param statusCode - An HTTP status code. * @return - A result set item. */ - private ElasticSearchResultItem generateRejectionEntry(OperationType opType, - String rejectReason, - String index, - String anId, - int statusCode, - String originalUrl) { + private ElasticSearchResultItem generateRejectionEntry(OperationType opType, String rejectReason, + String index, String anId, int statusCode, String originalUrl) { ElasticSearchError err = new ElasticSearchError(); err.setReason(rejectReason); @@ -1441,14 +1423,11 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { return rejectionResult; } - /** - * This method takes the json structure returned from ElasticSearch in - * response to a bulk operations request and marshals it into a Java - * object. + * This method takes the json structure returned from ElasticSearch in response to a bulk + * operations request and marshals it into a Java object. * - * @param jsonResult - The bulk operations response returned from - * ElasticSearch. + * @param jsonResult - The bulk operations response returned from ElasticSearch. * @return - The marshalled response. * @throws JsonParseException * @throws JsonMappingException @@ -1472,16 +1451,15 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { return null; } - /** - * This method takes the marshalled ElasticSearch bulk response and - * converts it into a generic response payload. + * This method takes the marshalled ElasticSearch bulk response and converts it into a generic + * response payload. * * @param esResult - ElasticSearch bulk operations response. * @return - A generic result set. */ private String buildGenericBulkResultSet(ElasticSearchBulkOperationResult esResult, - List rejectedOps) { + List rejectedOps) { int totalOps = 0; int totalSuccess = 0; @@ -1490,8 +1468,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { if (logger.isDebugEnabled()) { logger.debug("ESController: Build generic result set. ES Results: " - + ((esResult != null) ? esResult.toString() : "[]") - + " Rejected Ops: " + rejectedOps.toString()); + + ((esResult != null) ? esResult.toString() : "[]") + " Rejected Ops: " + + rejectedOps.toString()); } // Build a combined list of result items from the results returned @@ -1527,24 +1505,19 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } // Now, build the result string and return it. - String responseBody = "{ \"total_operations\": " + totalOps + ", " - + "\"total_success\": " + totalSuccess + ", " - + "\"total_fails\": " + totalFails + ", " - + "\"results\": [" - + resultsBuilder.toString() - + "]}"; + String responseBody = "{ \"total_operations\": " + totalOps + ", " + "\"total_success\": " + + totalSuccess + ", " + "\"total_fails\": " + totalFails + ", " + "\"results\": [" + + resultsBuilder.toString() + "]}"; return responseBody; } - /** - * This method queryies ElasticSearch to determine if the supplied - * index is present in the document store. + * This method queryies ElasticSearch to determine if the supplied index is present in the + * document store. * * @param indexName - The index to look for. - * @return - An operation result indicating the success or failure of - * the check. + * @return - An operation result indicating the success or failure of the check. * @throws DocumentStoreOperationException */ public OperationResult checkIndexExistence(String indexName) @@ -1575,7 +1548,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { resultCode = conn.getResponseCode(); } catch (IOException e) { shutdownConnection(conn); - throw new DocumentStoreOperationException("Failed to get the response code from the connection.", e); + throw new DocumentStoreOperationException( + "Failed to get the response code from the connection.", e); } logger.debug("Response Code : " + resultCode); @@ -1583,18 +1557,15 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Generate a metrics log so we can track how long the operation took. metricsLogger.info(SearchDbMsgs.CHECK_INDEX_TIME, - new LogFields() - .setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) + new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, opResult.getResultCode()) .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, opResult.getResultCode()), - override, - indexName); + override, indexName); shutdownConnection(conn); return opResult; } - private void buildDocumentResult(DocumentOperationResult result, String index) throws DocumentStoreOperationException { @@ -1616,17 +1587,16 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { // Error response object JSONObject error = (JSONObject) root.get("error"); if (error != null) { - result.setError(new ErrorResult(error.get("type").toString(), - error.get("reason").toString())); + result.setError( + new ErrorResult(error.get("type").toString(), error.get("reason").toString())); } } } catch (Exception e) { - throw new DocumentStoreOperationException("Failed to parse Elastic Search response." - + result.getResult()); + throw new DocumentStoreOperationException( + "Failed to parse Elastic Search response." + result.getResult()); } - } private String buildDocumentResponseUrl(String index, String id) { @@ -1657,8 +1627,8 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { doc.setEtag((hit.get("_version") != null) ? hit.get("_version").toString() : ""); } - doc.setUrl(buildDocumentResponseUrl(index, (hit.get("_id") != null) - ? hit.get("_id").toString() : "")); + doc.setUrl(buildDocumentResponseUrl(index, + (hit.get("_id") != null) ? hit.get("_id").toString() : "")); doc.setContent((JSONObject) hit.get("_source")); searchHit.setDocument(doc); searchHitArray.add(searchHit); @@ -1679,13 +1649,73 @@ public class ElasticSearchHttpController implements DocumentStoreInterface { } else { JSONObject error = (JSONObject) root.get("error"); if (error != null) { - result.setError(new ErrorResult(error.get("type").toString(), - error.get("reason").toString())); + result.setError( + new ErrorResult(error.get("type").toString(), error.get("reason").toString())); + } + } + } catch (Exception e) { + throw new DocumentStoreOperationException( + "Failed to parse Elastic Search response." + result.getResult()); + } + + } + + private void buildSuggestResult(SearchOperationResult result, String index) + throws DocumentStoreOperationException { + + JSONParser parser = new JSONParser(); + JSONObject root; + try { + root = (JSONObject) parser.parse(result.getResult()); + if (result.getResultCode() >= 200 && result.getResultCode() <= 299) { + JSONArray hitArray = (JSONArray) root.get("suggest-vnf"); + JSONObject hitdata = (JSONObject) hitArray.get(0); + JSONArray optionsArray = (JSONArray) hitdata.get("options"); + SuggestHits suggestHits = new SuggestHits(); + suggestHits.setTotalHits(String.valueOf(optionsArray.size())); + + ArrayList suggestHitArray = new ArrayList(); + + for (int i = 0; i < optionsArray.size(); i++) { + JSONObject hit = (JSONObject) optionsArray.get(i); + + SuggestHit suggestHit = new SuggestHit(); + suggestHit.setScore((hit.get("score") != null) ? hit.get("score").toString() : ""); + suggestHit.setText((hit.get("text") != null) ? hit.get("text").toString() : ""); + Document doc = new Document(); + if (hit.get("_version") != null) { + doc.setEtag((hit.get("_version") != null) ? hit.get("_version").toString() : ""); + } + doc.setUrl(buildDocumentResponseUrl(index, + (hit.get("_id") != null) ? hit.get("_id").toString() : "")); + + doc.setContent((JSONObject) hit.get("payload")); + suggestHit.setDocument(doc); + suggestHitArray.add(suggestHit); + } + suggestHits.setHits(suggestHitArray.toArray(new SuggestHit[suggestHitArray.size()])); + result.setSuggestResult(suggestHits); + + JSONObject aggregations = (JSONObject) root.get("aggregations"); + if (aggregations != null) { + AggregationResult[] aggResults = + AggregationParsingUtil.parseAggregationResults(aggregations); + AggregationResults aggs = new AggregationResults(); + aggs.setAggregations(aggResults); + result.setAggregationResult(aggs); + } + + // success + } else { + JSONObject error = (JSONObject) root.get("error"); + if (error != null) { + result.setError( + new ErrorResult(error.get("type").toString(), error.get("reason").toString())); } } } catch (Exception e) { - throw new DocumentStoreOperationException("Failed to parse Elastic Search response." - + result.getResult()); + throw new DocumentStoreOperationException( + "Failed to parse Elastic Search response." + result.getResult()); } } diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SearchOperationResult.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SearchOperationResult.java index b48f94d..31482c4 100644 --- a/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SearchOperationResult.java +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SearchOperationResult.java @@ -24,11 +24,16 @@ public class SearchOperationResult extends OperationResult { private SearchHits searchResult; private AggregationResults aggregationResult; + private SuggestHits suggestResult; public SearchHits getSearchResult() { return searchResult; } + public SuggestHits getSuggestResult() { + return suggestResult; + } + public AggregationResults getAggregationResult() { return aggregationResult; } @@ -41,10 +46,14 @@ public class SearchOperationResult extends OperationResult { this.searchResult = hits; } + public void setSuggestResult(SuggestHits hits) { + this.suggestResult = hits; + } + @Override public String toString() { - return "SearchOperationResult [searchResult=" + searchResult - + ", aggregationResult=" + aggregationResult; + return "SearchOperationResult [searchResult=" + searchResult + ", aggregationResult=" + + aggregationResult + ", suggestResult=" + suggestResult; } } diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHit.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHit.java new file mode 100644 index 0000000..f1c69a9 --- /dev/null +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHit.java @@ -0,0 +1,57 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sa.searchdbabstraction.entity; + +public class SuggestHit { + + private String score; + private String text; + Document document; + + public String getScore() { + return score; + } + + public void setScore(String score) { + this.score = score; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Document getDocument() { + return document; + } + + public void setDocument(Document document) { + this.document = document; + } + + @Override + public String toString() { + return "SearchHit [text=" + text + ",score=" + score + ", document=" + document + "]"; + } +} diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHits.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHits.java new file mode 100644 index 0000000..6c65465 --- /dev/null +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/entity/SuggestHits.java @@ -0,0 +1,50 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sa.searchdbabstraction.entity; + +import java.util.Arrays; + +public class SuggestHits { + + private String totalHits; + private SuggestHit[] hits; + + public String getTotalHits() { + return totalHits; + } + + public void setTotalHits(String totalHits) { + this.totalHits = totalHits; + } + + public SuggestHit[] getHits() { + return hits; + } + + public void setHits(SuggestHit[] hits) { + this.hits = hits; + } + + @Override + public String toString() { + return "SuggestHit [totalHits=" + totalHits + ", hits=" + Arrays.toString(hits) + "]"; + } +} diff --git a/src/main/java/org/onap/aai/sa/searchdbabstraction/searchapi/SuggestionStatement.java b/src/main/java/org/onap/aai/sa/searchdbabstraction/searchapi/SuggestionStatement.java new file mode 100644 index 0000000..0631281 --- /dev/null +++ b/src/main/java/org/onap/aai/sa/searchdbabstraction/searchapi/SuggestionStatement.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sa.searchdbabstraction.searchapi; + +import org.radeox.util.logging.Logger; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * This class represents the structure of a search statement. + * + *

+ * The expected JSON structure to represent a Completion suggest search statement is as follows: + * + * { "suggest-vnf" : { "text" : "VNFs", "completion" : { "field" : "entity_suggest", "size": 1 } } } + */ +public class SuggestionStatement { + + @JsonProperty("results-size") + private Integer size; + + @JsonProperty("suggest-field") + private String field; + + @JsonProperty("suggest-text") + private String text; + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + /** + * This method returns a string which represents this statement in syntax that is understandable + * by ElasticSearch and is suitable for inclusion in an ElasticSearch query string. + * + * @return - ElasticSearch syntax string. + */ + public String toElasticSearch() { + + StringBuilder sb = new StringBuilder(); + + sb.append("{"); + sb.append("\"suggest-vnf\": {"); + sb.append("\"text\": ").append("\"" + text + "\"").append(", "); + sb.append("\"completion\": {"); + sb.append("\"field\": ").append("\"" + field + "\"").append(", "); + sb.append("\"size\": ").append(size); + sb.append("}"); + sb.append("}"); + sb.append("}"); + + Logger.debug("Generated raw ElasticSearch suggest statement: " + sb.toString()); + return sb.toString(); + } + +} -- cgit 1.2.3-korg