diff options
10 files changed, 117 insertions, 11 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java index ac6fe38ee7..e102765a64 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java @@ -23,6 +23,7 @@ package org.onap.cps.ri; +import static org.onap.cps.api.CpsQueryService.NO_LIMIT; import static org.onap.cps.api.parameters.PaginationOption.NO_PAGINATION; import com.google.common.collect.ImmutableSet; @@ -223,10 +224,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService description = "Time taken to query data nodes") public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { + return queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption, NO_LIMIT); + } + + @Override + public List<DataNode> queryDataNodes(final String dataspaceName, + final String anchorName, + final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption, + final int queryResultLimit) { final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); final CpsPathQuery cpsPathQuery = getCpsPathQuery(cpsPath); final Collection<FragmentEntity> fragmentEntities = - fragmentRepository.findByAnchorAndCpsPath(anchorEntity, cpsPathQuery); + fragmentRepository.findByAnchorAndCpsPath(anchorEntity, cpsPathQuery, queryResultLimit); return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java index 3f3ca79900..bf354be024 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java @@ -51,13 +51,18 @@ public class FragmentQueryBuilder { private EntityManager entityManager; /** - * Create a sql query to retrieve by anchor(id) and cps path. + * Create a sql query to retrieve by anchor(id) and cps path with an optional queryResultLimit on results. * * @param anchorEntity the anchor * @param cpsPathQuery the cps path query to be transformed into a sql query + * @param queryResultLimit queryResultLimit number of returned entities + * (if the queryResultLimit is less than 1 the method returns all related entities) + * * @return a executable query object */ - public Query getQueryForAnchorAndCpsPath(final AnchorEntity anchorEntity, final CpsPathQuery cpsPathQuery) { + public Query getQueryForAnchorAndCpsPath(final AnchorEntity anchorEntity, + final CpsPathQuery cpsPathQuery, + final int queryResultLimit) { final StringBuilder sqlStringBuilder = new StringBuilder(); final Map<String, Object> queryParameters = new HashMap<>(); @@ -65,6 +70,7 @@ public class FragmentQueryBuilder { addWhereClauseForAnchor(anchorEntity, sqlStringBuilder, queryParameters); addNodeSearchConditions(cpsPathQuery, sqlStringBuilder, queryParameters, false); addSearchSuffix(cpsPathQuery, sqlStringBuilder, queryParameters); + addLimitClause(sqlStringBuilder, queryParameters, queryResultLimit); return getQuery(sqlStringBuilder.toString(), queryParameters, FragmentEntity.class); } @@ -219,6 +225,15 @@ public class FragmentQueryBuilder { } } + private static void addLimitClause(final StringBuilder sqlStringBuilder, + final Map<String, Object> queryParameters, + final int queryResultLimit) { + if (queryResultLimit > 0) { + sqlStringBuilder.append(" LIMIT :queryResultLimit"); + queryParameters.put("queryResultLimit", queryResultLimit); + } + } + private static Integer getTextValueAsInt(final CpsPathQuery cpsPathQuery) { try { return Integer.parseInt(cpsPathQuery.getTextFunctionConditionValue()); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java index 9c1929eaf7..4ee6555b79 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java @@ -29,7 +29,9 @@ import org.onap.cps.ri.models.DataspaceEntity; import org.onap.cps.ri.models.FragmentEntity; public interface FragmentRepositoryCpsPathQuery { - List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery); + + List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery, + int queryResultLimit); List<FragmentEntity> findByDataspaceAndCpsPath(DataspaceEntity dataspaceEntity, CpsPathQuery cpsPathQuery, List<Long> anchorIds); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java index e8c2725670..80fbe9b6cd 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java @@ -41,10 +41,15 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps @Override @Transactional public List<FragmentEntity> findByAnchorAndCpsPath(final AnchorEntity anchorEntity, - final CpsPathQuery cpsPathQuery) { - final Query query = fragmentQueryBuilder.getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery); + final CpsPathQuery cpsPathQuery, + final int queryResultLimit) { + final Query query = fragmentQueryBuilder + .getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery, queryResultLimit); final List<FragmentEntity> fragmentEntities = query.getResultList(); log.debug("Fetched {} fragment entities by anchor and cps path.", fragmentEntities.size()); + if (queryResultLimit > 0) { + log.debug("Result limited to {} entries", queryResultLimit); + } return fragmentEntities; } diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java b/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java index d783b9ed0e..3044fe0a90 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java @@ -32,6 +32,8 @@ import org.onap.cps.api.parameters.PaginationOption; */ public interface CpsQueryService { + public static int NO_LIMIT = 0; + /** * Get data nodes for the given dataspace and anchor by cps path. * @@ -45,6 +47,20 @@ public interface CpsQueryService { Collection<DataNode> queryDataNodes(String dataspaceName, String anchorName, String cpsPath, FetchDescendantsOption fetchDescendantsOption); + /** + * Retrieves a collection of data nodes based on the specified CPS path query. + * + * @param dataspaceName the name of the dataspace (must not be null or empty) + * @param anchorName the name of the anchor (must not be null or empty) + * @param cpsPath the CPS path used for querying (must not be null or empty) + * @param fetchDescendantsOption specifies whether to include descendant nodes in the output + * @param queryResultLimit the maximum number of data nodes to return; if less than 1, returns all matching nodes + * + * @return a collection of matching {@link DataNode} instances (can be empty if no nodes are found) + */ + Collection<DataNode> queryDataNodes(String dataspaceName, String anchorName, + String cpsPath, FetchDescendantsOption fetchDescendantsOption, + int queryResultLimit); /** * Get data leaf for the given dataspace and anchor by cps path. diff --git a/cps-service/src/main/java/org/onap/cps/impl/CpsQueryServiceImpl.java b/cps-service/src/main/java/org/onap/cps/impl/CpsQueryServiceImpl.java index 508a1b2449..f27445f7c9 100644 --- a/cps-service/src/main/java/org/onap/cps/impl/CpsQueryServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/impl/CpsQueryServiceImpl.java @@ -46,8 +46,22 @@ public class CpsQueryServiceImpl implements CpsQueryService { public Collection<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { - cpsValidator.validateNameCharacters(dataspaceName, anchorName); - return cpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption); + return queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption, NO_LIMIT); + } + + @Override + @Timed(value = "cps.data.service.datanode.query", + description = "Time taken to query data nodes with a limit on results") + public Collection<DataNode> queryDataNodes(final String dataSpaceName, final String anchorName, + final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption, + final int queryResultLimit) { + cpsValidator.validateNameCharacters(dataSpaceName, anchorName); + return cpsDataPersistenceService.queryDataNodes(dataSpaceName, + anchorName, + cpsPath, + fetchDescendantsOption, + queryResultLimit); } @Override diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index a4f05cdb53..5be5fb0481 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -186,6 +186,23 @@ public interface CpsDataPersistenceService { String cpsPath, FetchDescendantsOption fetchDescendantsOption); /** + * Get a datanode by cps path. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param cpsPath cps path + * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be + * included in the output + * @param queryResultLimit limits the number of returned entities (if less than 1 returns all) + * + * @return the data nodes found i.e. 0 or more data nodes + */ + List<DataNode> queryDataNodes(String dataspaceName, + String anchorName, + String cpsPath, FetchDescendantsOption fetchDescendantsOption, + int queryResultLimit); + + /** * Get data leaf for the given dataspace and anchor by cps path. * * @param dataspaceName dataspace name diff --git a/cps-service/src/test/groovy/org/onap/cps/impl/CpsQueryServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/impl/CpsQueryServiceImplSpec.groovy index b15ee72370..9db4aa4c3e 100644 --- a/cps-service/src/test/groovy/org/onap/cps/impl/CpsQueryServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/impl/CpsQueryServiceImplSpec.groovy @@ -21,7 +21,7 @@ package org.onap.cps.impl - +import org.onap.cps.api.CpsQueryService import org.onap.cps.utils.CpsValidator import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.api.parameters.FetchDescendantsOption @@ -42,7 +42,7 @@ class CpsQueryServiceImplSpec extends Specification { when: 'queryDataNodes is invoked' objectUnderTest.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption) then: 'the persistence service is called once with the correct parameters' - 1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption) + 1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption, CpsQueryService.NO_LIMIT) and: 'the CpsValidator is called on the dataspaceName, schemaSetName and anchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'all fetch descendants options are supported' @@ -50,6 +50,21 @@ class CpsQueryServiceImplSpec extends Specification { FetchDescendantsOption.DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)] } + def 'Query data nodes by cps path with limit.'() { + given: 'a dataspace name, an anchor name and a cps path' + def dataspaceName = 'some-dataspace' + def anchorName = 'some-anchor' + def cpsPath = '/cps-path' + def fetchDescendantsOption = FetchDescendantsOption.OMIT_DESCENDANTS + def myLimit = 123 + when: 'queryDataNodes (with limit) is invoked' + objectUnderTest.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption, myLimit) + then: 'the persistence service is called once with the correct parameters' + 1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption, myLimit) + and: 'the CpsValidator is called on the dataspaceName, schemaSetName and anchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + } + def 'Query data nodes across all anchors by cps path with #fetchDescendantsOption.'() { given: 'a dataspace name, an anchor name and a cps path' def dataspaceName = 'some-dataspace' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy index d1b445f5a4..42fb964d52 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy @@ -457,4 +457,17 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase { and: 'the queried nodes have expected bookstore names' assert result.anchorName.toSet() == [BOOKSTORE_ANCHOR_1, BOOKSTORE_ANCHOR_2].toSet() } + + def 'Query with a limit of #limit.' () { + when: + def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories', OMIT_DESCENDANTS, limit) + then: 'the expected number of nodes is returned' + assert countDataNodesInTree(result) == expectedNumberOfResults + where: 'the following parameters are used' + limit || expectedNumberOfResults + 1 || 1 + 2 || 2 + 0 || 5 + -1 || 5 + } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy index 364127f388..acc95cab8d 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy @@ -103,5 +103,4 @@ class QueryPerfTest extends CpsPerfTestBase { 'direct descendants' | DIRECT_CHILDREN_ONLY || 0.11 | 8 | 1 + OPENROADM_DEVICES_PER_ANCHOR 'all descendants' | INCLUDE_ALL_DESCENDANTS || 1.34 | 400 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE } - } |