From 810b7bf8f60bb6c85c852e2887c3471dcaed121d Mon Sep 17 00:00:00 2001 From: Holash Chand Date: Mon, 6 May 2024 11:50:09 +0530 Subject: [PATCH] Added pagination for search and list apis including both of elastic and native search --- docker-compose-v1.yml | 2 +- docker-compose.yml | 2 +- .../e2e/registry/fusionauth-registry.feature | 2 +- .../test/java/e2e/registry/registry.feature | 33 +++++++------- .../sunbirdrc/elastic/ElasticServiceImpl.java | 30 +++++++------ .../registry/middleware/util/Constants.java | 4 ++ .../registry/middleware/util/JSONUtil.java | 28 +++++++++++- .../registry/config/SchemaLoader.java | 7 ++- .../controller/RegistryController.java | 2 +- .../controller/RegistryEntityController.java | 40 +++++++++++++---- .../sunbirdrc/registry/dao/SearchDaoImpl.java | 13 ++++-- .../registry/helper/RegistryHelper.java | 39 +++++++++++----- .../registry/service/NativeSearchService.java | 45 ++++++++++++++----- .../registry/config/SchemaLoaderTest.java | 4 +- .../registry/dao/impl/SearchDaoImplTest.java | 28 ++++++------ .../service/impl/NativeSearchServiceTest.java | 18 ++++---- 16 files changed, 201 insertions(+), 96 deletions(-) diff --git a/docker-compose-v1.yml b/docker-compose-v1.yml index a9c0933e9..d7f80c331 100644 --- a/docker-compose-v1.yml +++ b/docker-compose-v1.yml @@ -2,7 +2,7 @@ version: '2.4' services: es: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.13 + image: docker.elastic.co/elasticsearch/elasticsearch:6.8.23 volumes: - ./${ES_DIR-es-data}:/usr/share/elasticsearch/data/* environment: diff --git a/docker-compose.yml b/docker-compose.yml index e7ac9778f..7775714d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '2.4' services: es: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.13 + image: docker.elastic.co/elasticsearch/elasticsearch:6.8.23 volumes: - ./${ES_DIR-es-data}:/usr/share/elasticsearch/data/* environment: diff --git a/java/apitest/src/test/java/e2e/registry/fusionauth-registry.feature b/java/apitest/src/test/java/e2e/registry/fusionauth-registry.feature index 1bea1f399..c1a19a8a8 100644 --- a/java/apitest/src/test/java/e2e/registry/fusionauth-registry.feature +++ b/java/apitest/src/test/java/e2e/registry/fusionauth-registry.feature @@ -57,4 +57,4 @@ Feature: Registry api tests And header Authorization = student_token When method get Then status 200 - And response[0].osid.length > 0 + And response.data[0].osid.length > 0 diff --git a/java/apitest/src/test/java/e2e/registry/registry.feature b/java/apitest/src/test/java/e2e/registry/registry.feature index f3ca112c8..f25970822 100644 --- a/java/apitest/src/test/java/e2e/registry/registry.feature +++ b/java/apitest/src/test/java/e2e/registry/registry.feature @@ -151,8 +151,8 @@ Feature: Registry api tests And header Authorization = student_token When method get Then status 200 - And response[0].osid.length > 0 - * def studentOsid = response[0].osid + And response.data[0].osid.length > 0 + * def studentOsid = response.data[0].osid # update student info Given url registryUrl And path 'api/v1/Student/' + studentOsid @@ -168,7 +168,7 @@ Feature: Registry api tests And header Authorization = student_token When method get Then status 200 - And response[0].name == "xyz" + And response.data[0].name == "xyz" # get student Given url registryUrl And path 'api/v1/Student/search' @@ -177,8 +177,8 @@ Feature: Registry api tests Then status 200 * print response And response.length == 1 - And match response[0].contact == '#notpresent' - And match response[0].favoriteSubject == '#notpresent' + And match response.data[0].contact == '#notpresent' + And match response.data[0].favoriteSubject == '#notpresent' # delete student info Given url registryUrl And path 'api/v1/Student/' + studentOsid @@ -191,7 +191,8 @@ Feature: Registry api tests And path 'api/v1/Student' And header Authorization = student_token When method get - Then status 404 + Then status 200 + And response.totalCount == 0 @env=async Scenario: Create a teacher schema and create teacher entity asynchronously @@ -240,8 +241,8 @@ Feature: Registry api tests When method post Then status 200 * print response - And response.length == 1 - And response[0].contact == '#notpresent' + And response.totalCount == 1 + And response.data[0].contact == '#notpresent' # get teacher info Given url registryUrl And path 'api/v1/Teacher/search' @@ -249,8 +250,8 @@ Feature: Registry api tests When method post Then status 200 * print response - And response.length == 1 - And response[0].contact == '#notpresent' + And response.totalCount == 1 + And response.data[0].contact == '#notpresent' @envnot=fusionauth Scenario: Create Board and invite institutes @@ -316,7 +317,7 @@ Feature: Registry api tests And header Authorization = board_token When method get Then status 200 - And response[0].osid.length > 0 + And response.data[0].osid.length > 0 # invite institute with token Given url registryUrl @@ -347,9 +348,9 @@ Feature: Registry api tests And header Authorization = institute_token When method get Then status 200 - And response[0].osid.length > 0 - * def instituteOsid = response[0].osid - * def address = response[0].address + And response.data[0].osid.length > 0 + * def instituteOsid = response.data[0].osid + * def address = response.data[0].address # update property of institute Given url registryUrl @@ -366,8 +367,8 @@ Feature: Registry api tests And header Authorization = institute_token When method get Then status 200 - And assert response[0].address[0].phoneNo.length == 1 - And assert response[0].address[0].phoneNo[0] == "444" + And assert response.data[0].address[0].phoneNo.length == 1 + And assert response.data[0].address[0].phoneNo[0] == "444" @envnot=fusionauth Scenario: write a api test, to test the schema not found error diff --git a/java/elastic-search/src/main/java/dev/sunbirdrc/elastic/ElasticServiceImpl.java b/java/elastic-search/src/main/java/dev/sunbirdrc/elastic/ElasticServiceImpl.java index 7eda5e32c..b519fdaf9 100644 --- a/java/elastic-search/src/main/java/dev/sunbirdrc/elastic/ElasticServiceImpl.java +++ b/java/elastic-search/src/main/java/dev/sunbirdrc/elastic/ElasticServiceImpl.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import dev.sunbirdrc.pojos.ComponentHealthInfo; import dev.sunbirdrc.pojos.Filter; import dev.sunbirdrc.pojos.FilterOperators; @@ -49,8 +50,7 @@ import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; -import static dev.sunbirdrc.registry.middleware.util.Constants.CONNECTION_FAILURE; -import static dev.sunbirdrc.registry.middleware.util.Constants.SUNBIRD_ELASTIC_SERVICE_NAME; +import static dev.sunbirdrc.registry.middleware.util.Constants.*; public class ElasticServiceImpl implements IElasticService { private static Map esClient = new HashMap(); @@ -283,21 +283,25 @@ public JsonNode search(String index, SearchQuery searchQuery) throws IOException SearchSourceBuilder sourceBuilder = new SearchSourceBuilder() .query(query) .size(searchQuery.getLimit()) - .from(searchQuery.getOffset()); + .from(searchQuery.getOffset()) + .trackTotalHits(true); SearchRequest searchRequest = new SearchRequest(index).source(sourceBuilder); - ArrayNode resultArray = JsonNodeFactory.instance.arrayNode(); + ObjectNode resultNode = JsonNodeFactory.instance.objectNode(); + ArrayNode dataArray = JsonNodeFactory.instance.arrayNode(); ObjectMapper mapper = new ObjectMapper(); - SearchResponse searchResponse = getClient(index).search(searchRequest, RequestOptions.DEFAULT); - for (SearchHit hit : searchResponse.getHits()) { - JsonNode node = mapper.readValue(hit.getSourceAsString(), JsonNode.class); - // TODO: Add draft mode condition - if(node.get("_status") == null || node.get("_status").asBoolean()) { - resultArray.add(node); - } + SearchResponse searchResponse = getClient(index).search(searchRequest, RequestOptions.DEFAULT); + for (SearchHit hit : searchResponse.getHits()) { + JsonNode node = mapper.readValue(hit.getSourceAsString(), JsonNode.class); + // TODO: Add draft mode condition + if(node.get(STATUS_KEYWORD) == null || node.get(STATUS_KEYWORD).asBoolean()) { + dataArray.add(node); } - logger.debug("Total search records found " + resultArray.size()); + } + resultNode.set(ENTITY_LIST, dataArray); + resultNode.put(TOTAL_COUNT, searchResponse.getHits().getTotalHits()); + logger.debug("Total search records found " + dataArray.size()); - return resultArray; + return resultNode; } diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java index 6d3fd4380..cfea2abd4 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java @@ -43,6 +43,10 @@ public class Constants { public static final String END_DATE="endDate"; public static final String LIMIT="limit"; public static final String OFFSET="offset"; + public static final String NEXT_PAGE="nextPage"; + public static final String PREV_PAGE="prevPage"; + public static final String TOTAL_COUNT="totalCount"; + public static final String ENTITY_LIST="data"; // JSON LD specific public static final String CONTEXT_KEYWORD = "@context"; diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java index 382b90f2a..fb31098d5 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,7 +31,8 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.NestedExceptionUtils; + +import static dev.sunbirdrc.registry.middleware.util.Constants.*; public class JSONUtil { @@ -545,4 +547,28 @@ public static JsonNode extractPropertyDataFromEntity(JsonNode entityNode, Map=0) result.put(PREV_PAGE, url + "?search=" + prevPageToken); + if(offset + limit <= totalCount) result.put(NEXT_PAGE, url + "?search=" + nextPageToken); + return result; + } + + public static ObjectNode parseSearchToken(String endcodedValue) { + try { + byte[] decoded = Base64.getDecoder().decode(endcodedValue); + return (ObjectNode) objectMapper.readTree(decoded); + } catch (Exception ignored) { + logger.warn("Unable to parse next page token"); + } + return null; + } } diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/config/SchemaLoader.java b/java/registry/src/main/java/dev/sunbirdrc/registry/config/SchemaLoader.java index 197e9353b..2d12019ae 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/config/SchemaLoader.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/config/SchemaLoader.java @@ -21,8 +21,7 @@ import java.util.ArrayList; import static dev.sunbirdrc.registry.Constants.Schema; -import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_TYPE; -import static dev.sunbirdrc.registry.middleware.util.Constants.FILTERS; +import static dev.sunbirdrc.registry.middleware.util.Constants.*; @Component public class SchemaLoader implements ApplicationListener { @@ -46,14 +45,14 @@ private void loadSchemasFromDB() { objectNode.set(FILTERS, JsonNodeFactory.instance.objectNode()); try { JsonNode searchResults = searchService.search(objectNode, ""); - for (JsonNode schemaNode : searchResults.get(Schema)) { + for (JsonNode schemaNode : searchResults.get(Schema).get(ENTITY_LIST)) { try { schemaService.addSchema(JsonNodeFactory.instance.objectNode().set(Schema, schemaNode)); } catch (Exception e) { logger.error("Failed loading schema to definition manager: {}", ExceptionUtils.getStackTrace(e)); } } - logger.error("Loaded {} schema from DB", searchResults.get(Schema).size()); + logger.error("Loaded {} schema from DB", searchResults.get(Schema).get(TOTAL_COUNT)); } catch (IOException e) { logger.error("Exception occurred while loading schema from db: {}", ExceptionUtils.getStackTrace(e)); } catch (Exception e) { diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryController.java index bce2c06c0..c2b5d844e 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryController.java @@ -56,7 +56,7 @@ public ResponseEntity searchEntity(@RequestHeader HttpHeaders header) try { watch.start("RegistryController.searchEntity"); - JsonNode result = registryHelper.searchEntity(payload, null); + JsonNode result = registryHelper.searchEntity(payload, null, false); response.setResult(result); responseParams.setStatus(Response.Status.SUCCESSFUL); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java index 0286d0d77..72b85a060 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java @@ -48,12 +48,14 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.BadRequestException; import java.lang.reflect.InvocationTargetException; import java.util.*; import static dev.sunbirdrc.registry.Constants.*; import static dev.sunbirdrc.registry.helper.RegistryHelper.ServiceNotEnabledResponse; import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_TYPE; +import static dev.sunbirdrc.registry.middleware.util.Constants.TOTAL_COUNT; @RestController public class RegistryEntityController extends AbstractController { @@ -83,6 +85,10 @@ public class RegistryEntityController extends AbstractController { boolean securityEnabled; @Value("${certificate.enableExternalTemplates:false}") boolean externalTemplatesEnabled; + @Value("${search.offset:0}") + private int searchOffset; + @Value("${search.limit:2000}") + private int searchLimit; @RequestMapping(value = "/api/v1/{entityName}/invite", method = RequestMethod.POST) public ResponseEntity invite( @@ -191,8 +197,11 @@ public ResponseEntity deleteEntity( } } - @RequestMapping(value = "/api/v1/{entityName}/search", method = RequestMethod.POST) - public ResponseEntity searchEntity(@PathVariable String entityName, @RequestHeader HttpHeaders header, @RequestBody ObjectNode searchNode) { + @RequestMapping(value = "/api/v1/{entityName}/search", method = {RequestMethod.POST, RequestMethod.GET}) + public ResponseEntity searchEntity(@PathVariable String entityName, + HttpServletRequest request, + @RequestHeader HttpHeaders header, @RequestBody(required = false) ObjectNode searchNode, + @RequestParam(value = "search", required = false) String searchQueryString) { ResponseParams responseParams = new ResponseParams(); Response response = new Response(Response.API_ID.SEARCH, "OK", responseParams); @@ -201,12 +210,21 @@ public ResponseEntity searchEntity(@PathVariable String entityName, @Req watch.start("RegistryController.searchEntity"); ArrayNode entity = JsonNodeFactory.instance.arrayNode(); entity.add(entityName); + if(searchNode == null && (searchQueryString == null || searchQueryString.isEmpty())) { + throw new BadRequestException("Search Request body not found"); + } + if(searchNode == null) { + searchNode = JsonNodeFactory.instance.objectNode(); + registryHelper.addSearchTokenToQuery(searchQueryString, searchNode); + } searchNode.set(ENTITY_TYPE, entity); checkEntityNameInDefinitionManager(entityName); if (definitionsManager.getDefinition(entityName).getOsSchemaConfiguration().getEnableSearch()) { - JsonNode result = registryHelper.searchEntity(searchNode, null); + JsonNode result = registryHelper.searchEntity(searchNode, null, false).get(entityName); + ObjectNode pageUrls = JSONUtil.getSearchPageUrls(searchNode, searchLimit, searchOffset, result.get(TOTAL_COUNT).asLong(), request.getRequestURL().toString()); + ((ObjectNode) result).setAll(pageUrls); watch.stop("RegistryController.searchEntity"); - return new ResponseEntity<>(result.get(entityName), HttpStatus.OK); + return new ResponseEntity<>(result, HttpStatus.OK); } else { watch.stop("RegistryController.searchEntity"); logger.error("Searching on entity {} not allowed", entityName); @@ -669,17 +687,21 @@ private JsonNode getEntityJsonNode(@PathVariable String entityName, @PathVariabl @RequestMapping(value = "/api/v1/{entityName}", method = RequestMethod.GET) public ResponseEntity getEntityByToken(@PathVariable String entityName, HttpServletRequest request, - @RequestHeader(required = false) String viewTemplateId) throws RecordNotFoundException { + @RequestHeader(required = false) String viewTemplateId, + @RequestParam(value = "search", required = false) String searchToken) throws RecordNotFoundException { ResponseParams responseParams = new ResponseParams(); Response response = new Response(Response.API_ID.GET, "OK", responseParams); try { checkEntityNameInDefinitionManager(entityName); String userId = registryHelper.getUserId(entityName); if (!Strings.isEmpty(userId)) { - JsonNode responseFromDb = registryHelper.searchEntitiesByUserId(entityName, userId, viewTemplateId); - JsonNode entities = responseFromDb.get(entityName); - if (!entities.isEmpty()) { - return new ResponseEntity<>(entities, HttpStatus.OK); + JsonNode searchQuery = registryHelper.searchQueryByUserId(entityName, userId, searchToken, viewTemplateId); + JsonNode responseFromDb = registryHelper.searchEntity(searchQuery, userId, true); + JsonNode results = responseFromDb.get(entityName); + if (!results.isEmpty()) { + ObjectNode pageUrls = JSONUtil.getSearchPageUrls(searchQuery, searchLimit, searchOffset, results.get(TOTAL_COUNT).asLong(), request.getRequestURL().toString()); + ((ObjectNode) results).setAll(pageUrls); + return new ResponseEntity<>(results, HttpStatus.OK); } else { responseParams.setErrmsg("No record found"); responseParams.setStatus(Response.Status.UNSUCCESSFUL); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/dao/SearchDaoImpl.java b/java/registry/src/main/java/dev/sunbirdrc/registry/dao/SearchDaoImpl.java index 806d06078..9ca50a82f 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/dao/SearchDaoImpl.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/dao/SearchDaoImpl.java @@ -22,6 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_LIST; +import static dev.sunbirdrc.registry.middleware.util.Constants.TOTAL_COUNT; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.has; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.hasNot; @@ -45,10 +47,15 @@ public JsonNode search(Graph graphFromStore, SearchQuery searchQuery, boolean ex GraphTraversal parentTraversal = resultGraphTraversal.asAdmin(); resultGraphTraversal = getFilteredResultTraversal(resultGraphTraversal, filterList) - .or(hasNot(Constants.STATUS_KEYWORD), has(Constants.STATUS_KEYWORD, Constants.STATUS_ACTIVE)) - .range(offset, offset + searchQuery.getLimit()).limit(searchQuery.getLimit()); + .or(hasNot(Constants.STATUS_KEYWORD), has(Constants.STATUS_KEYWORD, Constants.STATUS_ACTIVE)); + GraphTraversal resultGraphTraversalCount = resultGraphTraversal.asAdmin().clone(); + resultGraphTraversal.range(offset, offset + searchQuery.getLimit()); JsonNode result = getResult(graphFromStore, resultGraphTraversal, parentTraversal, expandInternal); - resultNode.set(entity, result); + ObjectNode response = JsonNodeFactory.instance.objectNode(); + response.set(ENTITY_LIST, result); + long count = resultGraphTraversalCount.count().next(); + response.set(TOTAL_COUNT, JsonNodeFactory.instance.numberNode(count)); + resultNode.set(entity, response); } return resultNode; diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java index 9a29096da..e79e37653 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java @@ -350,17 +350,17 @@ public JsonNode readEntity(JsonNode inputJson, String userId) throws Exception { * @return * @throws Exception */ - public JsonNode searchEntity(JsonNode inputJson, String userId) throws Exception { - return searchEntity(inputJson, searchService, userId); + public JsonNode searchEntity(JsonNode inputJson, String userId, boolean forceNativeSearch) throws Exception { + return searchEntity(inputJson, forceNativeSearch ? nativeSearchService : searchService, userId); } private JsonNode searchEntity(JsonNode inputJson, ISearchService service, String userId) throws Exception { logger.debug("searchEntity starts"); - JsonNode resultNode = service.search(inputJson, userId); + ObjectNode resultNode = (ObjectNode) service.search(inputJson, userId); ViewTemplate viewTemplate = viewTemplateManager.getViewTemplate(inputJson); if (viewTemplate != null) { ViewTransformer vTransformer = new ViewTransformer(); - resultNode = vTransformer.transform(viewTemplate, resultNode); + resultNode.set(ENTITY_LIST, vTransformer.transform(viewTemplate, resultNode.get(ENTITY_LIST))); } // Search is tricky to support LD. Needs a revisit here. logger.debug("searchEntity ends"); @@ -826,7 +826,7 @@ private JsonNode getUserInfoFromRegistry(HttpServletRequest request, String enti ObjectNode payload = getSearchByOwnerQuery(entityName, userId); watch.start("RegistryController.searchEntity"); - JsonNode result = searchEntity(payload, userId); + JsonNode result = searchEntity(payload, userId, false); watch.stop("RegistryController.searchEntity"); if(result != null && result.get(entityName) != null && !result.get(entityName).isEmpty()) { String uuid = result.get(entityName).get(0).get(uuidPropertyName).asText(); @@ -843,7 +843,7 @@ private JsonNode getUserInfoFromRegistry(HttpServletRequest request, String enti private JsonNode getEntityByUserId(String entityName, String userId) throws Exception { ObjectNode payload = getSearchByOwnerQuery(entityName, userId); watch.start("RegistryController.searchEntity"); - JsonNode result = searchEntity(payload, userId); + JsonNode result = searchEntity(payload, userId, false); watch.stop("RegistryController.searchEntity"); return result; } @@ -919,12 +919,27 @@ public ArrayNode fetchFromDBUsingEsResponse(String entity, ArrayNode esSearchRes } return result; } - public JsonNode searchEntitiesByUserId(String entity, String userId, String viewTemplateId) throws Exception { - ObjectNode searchByOwnerQuery = getSearchByOwnerQuery(entity, userId); + public void addSearchTokenToQuery(String searchToken, ObjectNode searchQuery) { + ObjectNode searchTokenNode = JSONUtil.parseSearchToken(searchToken); + if(searchTokenNode != null) { + if(searchTokenNode.has(FILTERS)) { + if(!searchQuery.has(FILTERS)) { + searchQuery.set(FILTERS, JsonNodeFactory.instance.objectNode()); + } + ((ObjectNode)searchQuery.get(FILTERS)).setAll((ObjectNode) searchTokenNode.get(FILTERS)); + } + if(searchTokenNode.has(LIMIT)) searchQuery.set(LIMIT, searchTokenNode.get(LIMIT)); + if(searchTokenNode.has(OFFSET)) searchQuery.set(OFFSET, searchTokenNode.get(OFFSET)); + if(searchTokenNode.has(VIEW_TEMPLATE_ID)) searchQuery.set(VIEW_TEMPLATE_ID, searchTokenNode.get(VIEW_TEMPLATE_ID)); + } + } + public JsonNode searchQueryByUserId(String entityName, String userId, String searchToken, String viewTemplateId) throws Exception { + ObjectNode searchByOwnerQuery = getSearchByOwnerQuery(entityName, userId); + addSearchTokenToQuery(searchToken, searchByOwnerQuery); if (!Strings.isEmpty(viewTemplateId)) { searchByOwnerQuery.put(VIEW_TEMPLATE_ID, viewTemplateId); } - return searchEntity(searchByOwnerQuery, nativeSearchService, userId); + return searchByOwnerQuery; } public void authorizeInviteEntity(HttpServletRequest request, String entityName) throws Exception { @@ -1172,7 +1187,7 @@ private List getAttestationsFromRegistry(String entityName) { " }\n" + " }\n" + "}"); - JsonNode searchResponse = searchEntity(searchRequest, ""); + JsonNode searchResponse = searchEntity(searchRequest, "", false); return convertJsonNodeToAttestationList(searchResponse); } catch (Exception e) { logger.error("Error fetching attestation policy: {}", ExceptionUtils.getStackTrace(e)); @@ -1239,7 +1254,7 @@ public List findAttestationPolicyByEntityAndCreatedBy(String " }\n" + " }\n" + "}"); - searchEntity(searchRequest, userId); + searchEntity(searchRequest, userId, false); return Collections.emptyList(); } @@ -1328,7 +1343,7 @@ public boolean checkIfCredentialIsRevoked(String signedData, String userId) thro searchNode.set(FILTERS, JsonNodeFactory.instance.objectNode().set(SIGNED_HASH, JsonNodeFactory.instance.objectNode().put("eq", generateHash(signedData)))); - JsonNode searchResponse = searchEntity(searchNode, userId); + JsonNode searchResponse = searchEntity(searchNode, userId, false); return searchResponse.get(REVOKED_CREDENTIAL) != null && searchResponse.get(REVOKED_CREDENTIAL).size() > 0; } diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java b/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java index ff3ba583f..eafe98e4d 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/service/NativeSearchService.java @@ -2,7 +2,9 @@ import java.io.IOException; import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import com.fasterxml.jackson.databind.node.*; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Transaction; @@ -13,9 +15,6 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import dev.sunbirdrc.pojos.APIMessage; import dev.sunbirdrc.pojos.AuditRecord; @@ -33,6 +32,10 @@ import dev.sunbirdrc.registry.sink.shard.ShardManager; import dev.sunbirdrc.registry.util.IDefinitionsManager; import dev.sunbirdrc.registry.util.RecordIdentifier; + +import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_LIST; +import static dev.sunbirdrc.registry.middleware.util.Constants.TOTAL_COUNT; + /** * This class provides native search which hits the native database * Hence, this have performance in-efficiency on search operations @@ -122,7 +125,7 @@ public JsonNode search(JsonNode inputQueryNode, String userId) throws IOExceptio String prefix = shard.getShardLabel() + RecordIdentifier.getSeparator(); JSONUtil.addPrefix((ObjectNode) shardResult, prefix, new ArrayList<>(Arrays.asList(uuidPropertyName))); } - result = removeNonPublicFields(searchQuery, shardResult); + result.add(removeNonPublicFields(searchQuery, shardResult)); if (tx != null) { transaction.add(tx.hashCode()); } @@ -149,19 +152,27 @@ public JsonNode search(JsonNode inputQueryNode, String userId) throws IOExceptio return buildResultNode(searchQuery, result); } - private ArrayNode removeNonPublicFields(SearchQuery searchQuery, ObjectNode shardResult) throws Exception { - ArrayNode result = JsonNodeFactory.instance.arrayNode(); + private ObjectNode removeNonPublicFields(SearchQuery searchQuery, ObjectNode shardResult) throws Exception { + ObjectNode response = JsonNodeFactory.instance.objectNode(); + NumericNode count; for(String entityType: searchQuery.getEntityTypes()) { - ArrayNode arrayNode = (ArrayNode) shardResult.get(entityType); + ObjectNode result = JsonNodeFactory.instance.objectNode(); + ArrayNode data = JsonNodeFactory.instance.arrayNode(); + ArrayNode arrayNode = (ArrayNode) (shardResult.get(entityType).get(ENTITY_LIST)); + count = (NumericNode) shardResult.get(entityType).get(TOTAL_COUNT); if (removeNonPublicFieldsForNativeSearch) { for(JsonNode node : arrayNode) { - result.add(JSONUtil.removeNodesByPath(node, definitionsManager.getExcludingFieldsForEntity(entityType))); + data.add(JSONUtil.removeNodesByPath(node, definitionsManager.getExcludingFieldsForEntity(entityType))); } } else { - result = arrayNode; + data = arrayNode; } + result.set(TOTAL_COUNT, count); + result.set(ENTITY_LIST, data); + response.set(entityType, result); } - return result; + + return response; } /** @@ -170,10 +181,20 @@ private ArrayNode removeNonPublicFields(SearchQuery searchQuery, ObjectNode shar * @param allShardResult * @return */ - private JsonNode buildResultNode(SearchQuery searchQuery, ArrayNode allShardResult) { + private JsonNode buildResultNode(SearchQuery searchQuery, ArrayNode allShardResult) throws IOException { ObjectNode resultNode = JsonNodeFactory.instance.objectNode(); for (String entity : searchQuery.getEntityTypes()) { - resultNode.set(entity, allShardResult); + List entityResults = allShardResult.findValues(entity); + ArrayNode data = JsonNodeFactory.instance.arrayNode(); + AtomicLong count = new AtomicLong(0L); + ObjectNode entityResultsAggregate = JsonNodeFactory.instance.objectNode(); + entityResults.forEach(shardData -> { + data.addAll((ArrayNode) shardData.get(ENTITY_LIST)); + count.addAndGet(shardData.get(TOTAL_COUNT).asLong()); + }); + entityResultsAggregate.set(TOTAL_COUNT, JsonNodeFactory.instance.numberNode(count.get())); + entityResultsAggregate.set(ENTITY_LIST, data); + resultNode.set(entity, entityResultsAggregate); } return resultNode; } diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/config/SchemaLoaderTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/config/SchemaLoaderTest.java index cd846c016..26ee25d68 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/config/SchemaLoaderTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/config/SchemaLoaderTest.java @@ -48,7 +48,8 @@ public void setUp() throws Exception { @Test public void shouldLoadSchemasToDefinitionManager() throws IOException { Mockito.when(searchService.search(Mockito.any(), Mockito.anyString())).thenReturn(objectMapper.readTree("{\n" + - " \"Schema\": [\n" + + " \"Schema\": {\n" + + " \"data\": [\n" + " {\n" + " \"name\": \"DeathCertificateV2\",\n" + " \"schema\": \"{\\n \\\"$schema\\\": \\\"http://json-schema.org/draft-07/schema\\\",\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"DeathCertificate\\\": {\\n \\\"$ref\\\": \\\"#/definitions/DeathCertificate\\\"\\n }\\n },\\n \\\"required\\\": [\\n \\\"DeathCertificate\\\"\\n ],\\n \\\"title\\\": \\\"DeathCertificate\\\",\\n \\\"definitions\\\": {\\n \\\"DeathCertificate\\\": {\\n \\\"$id\\\": \\\"#/properties/DeathCertificate\\\",\\n \\\"type\\\": \\\"object\\\",\\n \\\"title\\\": \\\"The DeathCertificate Schema\\\",\\n \\\"required\\\": [\\n \\\"name\\\",\\n \\\"gender\\\",\\n \\\"date_of_death\\\",\\n \\\"place_of_death\\\",\\n \\\"date_of_registration\\\",\\n \\\"registration_no\\\",\\n \\\"name_of_mother\\\",\\n \\\"name_of_father\\\",\\n \\\"name_of_spouse\\\",\\n \\\"present_address\\\",\\n \\\"permanent_address\\\"\\n ],\\n \\\"properties\\\": {\\n \\\"name\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"gender\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"date_of_death\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"place_of_death\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"date_of_registration\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"registration_no\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"name_of_mother\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"name_of_father\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"name_of_spouse\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"present_address\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"permanent_address\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n },\\n \\\"_osConfig\\\": {\\n \\\"uniqueIndexFields\\\": [\\n \\\"contact\\\"\\n ],\\n \\\"ownershipAttributes\\\": [],\\n \\\"roles\\\": [],\\n \\\"inviteRoles\\\": [\\n \\\"anonymous\\\"\\n ],\\n \\\"enableLogin\\\": false,\\n \\\"credentialTemplate\\\": {\\n \\\"@context\\\": [\\n \\\"https://www.w3.org/2018/credentials/v1\\\",\\n {\\n \\\"@context\\\": {\\n \\\"@version\\\": 1.1,\\n \\\"@protected\\\": true,\\n \\\"DeathCertificate\\\": {\\n \\\"@id\\\": \\\"https://github.com/sunbird-specs/vc-specs#DeathCertificate\\\",\\n \\\"@context\\\": {\\n \\\"id\\\": \\\"@id\\\",\\n \\\"@version\\\": 1.1,\\n \\\"@protected\\\": true,\\n \\\"skills\\\": \\\"schema:Text\\\"\\n }\\n },\\n \\\"Person\\\": {\\n \\\"@id\\\": \\\"https://github.com/sunbird-specs/vc-specs#Person\\\",\\n \\\"@context\\\": {\\n \\\"name\\\": \\\"schema:Text\\\",\\n \\\"gender\\\": \\\"schema:Text\\\",\\n \\\"date_of_death\\\": \\\"schema:Text\\\",\\n \\\"place_of_death\\\": \\\"schema:Text\\\",\\n \\\"date_of_registration\\\": \\\"schema:Text\\\",\\n \\\"registration_no\\\": \\\"schema:Text\\\",\\n \\\"name_of_mother\\\": \\\"schema:Text\\\",\\n \\\"name_of_father\\\": \\\"schema:Text\\\",\\n \\\"name_of_spouse\\\": \\\"schema:Text\\\",\\n \\\"present_address\\\": \\\"schema:Text\\\",\\n \\\"permanent_address\\\": \\\"schema:Text\\\"\\n }\\n },\\n \\\"trainedOn\\\": {\\n \\\"@id\\\": \\\"https://github.com/sunbird-specs/vc-specs#trainedOn\\\",\\n \\\"@context\\\": {\\n \\\"name\\\": \\\"schema:Text\\\"\\n }\\n }\\n }\\n }\\n ],\\n \\\"type\\\": [\\n \\\"VerifiableCredential\\\"\\n ],\\n \\\"issuanceDate\\\": \\\"2021-08-27T10:57:57.237Z\\\",\\n \\\"credentialSubject\\\": {\\n \\\"type\\\": \\\"Person\\\",\\n \\\"name\\\": \\\"{{name}}\\\",\\n \\\"gender\\\": \\\"{{gender}}\\\",\\n \\\"date_of_death\\\": \\\"{{date_of_death}}\\\",\\n \\\"place_of_death\\\": \\\"{{place_of_death}}\\\",\\n \\\"date_of_registration\\\": \\\"{{date_of_registration}}\\\",\\n \\\"registration_no\\\": \\\"{{registration_no}}\\\",\\n \\\"name_of_mother\\\": \\\"{{name_of_mother}}\\\",\\n \\\"name_of_father\\\": \\\"{{name_of_father}}\\\",\\n \\\"name_of_spouse\\\": \\\"{{name_of_spouse}}\\\",\\n \\\"present_address\\\": \\\"{{present_address}}\\\",\\n \\\"permanent_address\\\": \\\"{{permanent_address}}\\\"\\n },\\n \\\"issuer\\\": \\\"did:web:sunbirdrc.dev/vc/skill\\\"\\n },\\n \\\"certificateTemplates\\\": {\\n \\\"html\\\": \\\"https://gist.githubusercontent.com/snehalmadakatti/b2179a3e6c6a6101bfabc92a632a57ad/raw/efbbe82f9e582b260e06acedbb0c6318cc04cb2b/deathcertificate.html\\\",\\n \\\"svg\\\": \\\"https://raw.githubusercontent.com/dileepbapat/ref-sunbirdrc-certificate/main/schemas/templates/DeathCertificate.svg\\\"\\n }\\n }\\n}\",\n" + @@ -63,6 +64,7 @@ public void shouldLoadSchemasToDefinitionManager() throws IOException { " \"osid\": \"1-e6042101-c6c7-4a62-a448-68e663b0c3c9\"\n" + " }\n" + " ]\n" + + " }\n" + "}")); schemaLoader.onApplicationEvent(new ContextRefreshedEvent(new GenericApplicationContext())); assertEquals(1, definitionsManager.getAllDefinitions().size()); diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/dao/impl/SearchDaoImplTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/dao/impl/SearchDaoImplTest.java index 061d77928..422a38608 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/dao/impl/SearchDaoImplTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/dao/impl/SearchDaoImplTest.java @@ -37,6 +37,8 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_LIST; +import static dev.sunbirdrc.registry.middleware.util.Constants.TOTAL_COUNT; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @@ -83,14 +85,14 @@ public void initializeGraph() throws IOException { public void test_search_no_response() throws AuditFailedException, EncryptionException, RecordNotFoundException { SearchQuery searchQuery = getSearchQuery(entities, "", "", FilterOperators.eq);//new SearchQuery("", 0, 0); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - assertTrue(result.get("Teacher").isEmpty()); + assertTrue(result.get("Teacher").get(ENTITY_LIST).isEmpty()); } @Test public void testEqOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "marko", FilterOperators.eq); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertEquals("marko", d.get("teacherName").asText()); }); } @@ -99,7 +101,7 @@ public void testEqOperator() { public void testNeqOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "marko", FilterOperators.neq); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertNotEquals("marko", d.get("teacherName").asText()); }); } @@ -111,7 +113,7 @@ public void testRangeOperator() { range.add(3); SearchQuery searchQuery = getSearchQuery(entities, "serialNum", range, FilterOperators.between); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertTrue(d.get("serialNum").asLong() >= 1); assertTrue(d.get("serialNum").asLong() <= 3); }); @@ -125,7 +127,7 @@ public void testOrOperator() { values.add(VALUE_NOT_PRESENT); SearchQuery searchQuery = getSearchQuery(entities, "teacherName", values, FilterOperators.or); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertTrue(values.contains(d.get("teacherName").asText())); }); } @@ -134,7 +136,7 @@ public void testOrOperator() { public void testStartsWithOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "ma", FilterOperators.startsWith); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertTrue(d.get("teacherName").asText().startsWith("ma")); }); } @@ -142,7 +144,7 @@ public void testStartsWithOperator() { public void testNotStartsWithOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "ma", FilterOperators.notStartsWith); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertFalse(d.get("teacherName").asText().startsWith("ma")); }); } @@ -151,7 +153,7 @@ public void testNotStartsWithOperator() { public void testEndsWithOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "as", FilterOperators.endsWith); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertTrue(d.get("teacherName").asText().endsWith("as")); }); } @@ -159,7 +161,7 @@ public void testEndsWithOperator() { public void testNotEndsWithOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "as", FilterOperators.notEndsWith); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertFalse(d.get("teacherName").asText().endsWith("as")); }); } @@ -168,7 +170,7 @@ public void testNotEndsWithOperator() { public void testContainsOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "as", FilterOperators.contains); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertTrue(d.get("teacherName").asText().contains("as")); }); } @@ -176,7 +178,7 @@ public void testContainsOperator() { public void testNotContainsOperator() { SearchQuery searchQuery = getSearchQuery(entities, "teacherName", "as", FilterOperators.notContains); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - result.get("Teacher").forEach(d -> { + result.get("Teacher").get(ENTITY_LIST).forEach(d -> { assertFalse(d.get("teacherName").asText().contains("as")); }); } @@ -190,14 +192,14 @@ public void testMultiOperators() { searchQuery.getFilters().add(new Filter("serialNum", FilterOperators.gt, 1)); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - assertTrue(result.get("Teacher").isEmpty()); + assertTrue(result.get("Teacher").get(ENTITY_LIST).isEmpty()); } @Test public void testResponseLimit() { SearchQuery searchQuery = new SearchQuery(entities, offset, limit); JsonNode result = searchDao.search(graph, searchQuery, expandInternal); - assertEquals(1, result.get("Teacher").size()); + assertEquals(1, result.get("Teacher").get(ENTITY_LIST).size()); } @PreDestroy diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java index 272f2bad8..afa21391e 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java @@ -36,6 +36,9 @@ import java.io.IOException; import java.util.Collections; +import static dev.sunbirdrc.registry.middleware.util.Constants.ENTITY_LIST; +import static dev.sunbirdrc.registry.middleware.util.Constants.TOTAL_COUNT; + @RunWith(SpringRunner.class) @SpringBootTest(classes = {DefinitionsManager.class, ObjectMapper.class, DBProviderFactory.class, DBConnectionInfoMgr.class, OSResourceLoader.class, ShardManager.class, DefaultShardAdvisor.class}) @@ -85,7 +88,7 @@ public void init() throws IOException { public void shouldReturnRecordsMatchingFilters() throws IOException { JsonNode query = getSearchQuery(); JsonNode results = nativeSearchService.search(query, ""); - Assert.assertEquals(1, results.get("Teacher").size()); + Assert.assertEquals(1L, results.get("Teacher").get(TOTAL_COUNT).asLong()); } private JsonNode getSearchQuery() throws JsonProcessingException { @@ -105,10 +108,9 @@ public void shouldRemovePublicFields() throws IOException { ReflectionTestUtils.setField(nativeSearchService, "removeNonPublicFieldsForNativeSearch", true); JsonNode query = getSearchQuery(); JsonNode results = nativeSearchService.search(query, ""); - System.out.println(results.get("Teacher")); - Assert.assertEquals(1, results.get("Teacher").size()); - Assert.assertEquals(4, results.get("Teacher").get(0).size()); - Assert.assertNull(results.get("Teacher").get(0).get("serialNum")); + Assert.assertEquals(1L, results.get("Teacher").get(TOTAL_COUNT).asLong()); + Assert.assertEquals(4, results.get("Teacher").get(ENTITY_LIST).get(0).size()); + Assert.assertNull(results.get("Teacher").get(ENTITY_LIST).get(0).get("serialNum")); } @@ -117,9 +119,9 @@ public void shouldNotRemovePublicFields() throws IOException { ReflectionTestUtils.setField(nativeSearchService, "removeNonPublicFieldsForNativeSearch", false); JsonNode query = getSearchQuery(); JsonNode results = nativeSearchService.search(query, ""); - Assert.assertEquals(1, results.get("Teacher").size()); - Assert.assertEquals(5, results.get("Teacher").get(0).size()); - Assert.assertNotNull(results.get("Teacher").get(0).get("serialNum")); + Assert.assertEquals(1L, results.get("Teacher").get(TOTAL_COUNT).asLong()); + Assert.assertEquals(5, results.get("Teacher").get(ENTITY_LIST).get(0).size()); + Assert.assertNotNull(results.get("Teacher").get(ENTITY_LIST).get(0).get("serialNum")); }