Fix and refactor query across anchors (CPS-1664 #3) 74/134474/6
authordanielhanrahan <daniel.hanrahan@est.tech>
Tue, 2 May 2023 19:01:34 +0000 (20:01 +0100)
committerdanielhanrahan <daniel.hanrahan@est.tech>
Thu, 4 May 2023 15:19:00 +0000 (16:19 +0100)
- Fix CPS-1580: Query Across All Anchors Does NOT Filter on Dataspace
- Fix CPS-1582: NullPointerException in queryDataNodesAcrossAnchors
- Improve performance of queryDataNodesAcrossAnchors
- Refactor queryDataNodes
- Refactor FragmentQueryBuilder

Issue-ID: CPS-1580
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: I8b83d4c580280d8719ab2ac33f3914f53e798774

cps-ri/pom.xml
cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java
cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentExtract.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy

index c38c13b..ea1efcb 100644 (file)
@@ -33,7 +33,7 @@
     <artifactId>cps-ri</artifactId>\r
 \r
     <properties>\r
-        <minimum-coverage>0.80</minimum-coverage>\r
+        <minimum-coverage>0.79</minimum-coverage>\r
     </properties>\r
 \r
     <dependencies>\r
index 55d3c7e..49e2dd2 100644 (file)
@@ -47,6 +47,24 @@ public class FragmentEntityArranger {
         return reuniteChildrenWithTheirParents(fragmentEntityPerId);
     }
 
+    /**
+     * Convert a collection of (related) FragmentExtracts into  FragmentEntities (trees) with descendants.
+     *
+     * @param anchorEntityPerId the anchor(entities) the fragments belong to
+     * @param fragmentExtracts FragmentExtracts to convert
+     * @return a collection of FragmentEntities (trees) with descendants.
+     */
+    public static Collection<FragmentEntity> toFragmentEntityTreesAcrossAnchors(
+            final Map<Integer, AnchorEntity> anchorEntityPerId, final Collection<FragmentExtract> fragmentExtracts) {
+        final Map<Long, FragmentEntity> fragmentEntityPerId = new HashMap<>();
+        for (final FragmentExtract fragmentExtract : fragmentExtracts) {
+            final AnchorEntity anchorEntity = anchorEntityPerId.get(fragmentExtract.getAnchorId());
+            final FragmentEntity fragmentEntity = toFragmentEntity(anchorEntity, fragmentExtract);
+            fragmentEntityPerId.put(fragmentEntity.getId(), fragmentEntity);
+        }
+        return reuniteChildrenWithTheirParents(fragmentEntityPerId);
+    }
+
     private static FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity,
                                                    final FragmentExtract fragmentExtract) {
         final FragmentEntity fragmentEntity = new FragmentEntity();
index 52c1a60..3aa19e6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation.
+ * Copyright (C) 2022-2023 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ public interface FragmentExtract {
 
     Long getId();
 
-    Long getAnchorId();
+    Integer getAnchorId();
 
     String getXpath();
 
index 3a1588a..be5c66d 100644 (file)
@@ -36,6 +36,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -316,8 +317,9 @@ 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) {
-        final AnchorEntity anchorEntity = (Strings.isNullOrEmpty(anchorName)) ? ALL_ANCHORS
-                : getAnchorEntity(dataspaceName, anchorName);
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final AnchorEntity anchorEntity = Strings.isNullOrEmpty(anchorName) ? ALL_ANCHORS
+            : anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         final CpsPathQuery cpsPathQuery;
         try {
             cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath);
@@ -327,21 +329,30 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
         Collection<FragmentEntity> fragmentEntities;
         if (canUseRegexQuickFind(fetchDescendantsOption, cpsPathQuery)) {
-            return getDataNodesUsingRegexQuickFind(fetchDescendantsOption, anchorEntity, cpsPathQuery);
+            return getDataNodesUsingRegexQuickFind(fetchDescendantsOption, dataspaceEntity, anchorEntity, cpsPathQuery);
+        }
+
+        if (anchorEntity == ALL_ANCHORS) {
+            fragmentEntities = fragmentRepository.findByDataspaceAndCpsPath(dataspaceEntity, cpsPathQuery);
+        } else {
+            fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity, cpsPathQuery);
         }
-        fragmentEntities = (anchorEntity == ALL_ANCHORS) ? fragmentRepository.findByCpsPath(cpsPathQuery)
-                : fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
+
         if (cpsPathQuery.hasAncestorAxis()) {
             final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
-            fragmentEntities = (anchorEntity == ALL_ANCHORS) ? getAncestorFragmentEntitiesAcrossAnchors(cpsPathQuery,
-            fragmentEntities) : getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption);
+            if (anchorEntity == ALL_ANCHORS) {
+                fragmentEntities = fragmentRepository.findByDataspaceAndXpathIn(dataspaceEntity, ancestorXpaths);
+            } else {
+                fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
+            }
         }
+
         return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities);
     }
 
     @Override
     public List<DataNode> queryDataNodesAcrossAnchors(final String dataspaceName, final String cpsPath,
-                                         final FetchDescendantsOption fetchDescendantsOption) {
+                                                      final FetchDescendantsOption fetchDescendantsOption) {
         return queryDataNodes(dataspaceName, QUERY_ACROSS_ANCHORS, cpsPath, fetchDescendantsOption);
     }
 
@@ -355,21 +366,29 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private List<DataNode> getDataNodesUsingRegexQuickFind(final FetchDescendantsOption fetchDescendantsOption,
+                                                           final DataspaceEntity dataspaceEntity,
                                                            final AnchorEntity anchorEntity,
                                                            final CpsPathQuery cpsPathQuery) {
         final String xpathRegex = FragmentQueryBuilder.getXpathSqlRegexForQuickFindWithDescendants(cpsPathQuery);
         final List<FragmentExtract> fragmentExtracts = (anchorEntity == ALL_ANCHORS)
-            ? fragmentRepository.quickFindWithDescendantsAcrossAnchors(xpathRegex)
+            ? fragmentRepository.quickFindWithDescendantsAcrossAnchors(dataspaceEntity.getId(), xpathRegex)
             : fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex);
         final Collection<FragmentEntity> fragmentEntities =
-            FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts);
+            createFragmentEntitiesFromFragmentExtracts(anchorEntity, fragmentExtracts);
         return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
     }
 
-    private Collection<FragmentEntity> getAncestorFragmentEntitiesAcrossAnchors(final CpsPathQuery cpsPathQuery,
-        final Collection<FragmentEntity> fragmentEntities) {
-        final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
-        return ancestorXpaths.isEmpty() ? Collections.emptyList() : fragmentRepository.findAllByXpathIn(ancestorXpaths);
+    private Collection<FragmentEntity> createFragmentEntitiesFromFragmentExtracts(
+                    final AnchorEntity anchorEntity, final Collection<FragmentExtract> fragmentExtracts) {
+        if (anchorEntity == ALL_ANCHORS) {
+            final Collection<Integer> anchorIds = fragmentExtracts.stream()
+                .map(FragmentExtract::getAnchorId).collect(Collectors.toSet());
+            final List<AnchorEntity> anchorEntities = anchorRepository.findAllById(anchorIds);
+            final Map<Integer, AnchorEntity> anchorEntityPerId = anchorEntities.stream()
+                .collect(Collectors.toMap(AnchorEntity::getId, Function.identity()));
+            return FragmentEntityArranger.toFragmentEntityTreesAcrossAnchors(anchorEntityPerId, fragmentExtracts);
+        }
+        return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts);
     }
 
     private List<DataNode> createDataNodesFromProxiedFragmentEntities(
index 0134873..515bbd6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,6 +33,8 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.cpspath.parser.CpsPathPrefixType;
 import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.spi.entities.AnchorEntity;
+import org.onap.cps.spi.entities.DataspaceEntity;
 import org.onap.cps.spi.entities.FragmentEntity;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.stereotype.Component;
@@ -45,6 +47,7 @@ public class FragmentQueryBuilder {
     private static final String REGEX_DESCENDANT_PATH_PREFIX = "^.*\\/";
     private static final String REGEX_OPTIONAL_LIST_INDEX_POSTFIX = "(\\[@(?!.*\\[).*?])?$";
     private static final String REGEX_FOR_QUICK_FIND_WITH_DESCENDANTS = "(\\[@.*?])?(\\/.*)?$";
+    private static final AnchorEntity ACROSS_ALL_ANCHORS = null;
 
     @PersistenceContext
     private EntityManager entityManager;
@@ -54,28 +57,24 @@ public class FragmentQueryBuilder {
     /**
      * Create a sql query to retrieve by anchor(id) and cps path.
      *
-     * @param anchorId the id of the anchor
+     * @param anchorEntity the anchor
      * @param cpsPathQuery the cps path query to be transformed into a sql query
      * @return a executable query object
      */
-    public Query getQueryForAnchorAndCpsPath(final int anchorId, final CpsPathQuery cpsPathQuery) {
-        final StringBuilder sqlStringBuilder = new StringBuilder("SELECT * FROM FRAGMENT WHERE anchor_id = :anchorId");
-        final Map<String, Object> queryParameters = new HashMap<>();
-        queryParameters.put("anchorId", anchorId);
-        sqlStringBuilder.append(" AND xpath ~ :xpathRegex");
-        return getQuery(cpsPathQuery, sqlStringBuilder, queryParameters);
+    public Query getQueryForAnchorAndCpsPath(final AnchorEntity anchorEntity, final CpsPathQuery cpsPathQuery) {
+        return getQueryForDataspaceOrAnchorAndCpsPath(anchorEntity.getDataspace(), anchorEntity, cpsPathQuery);
     }
 
     /**
      * Create a sql query to retrieve by cps path.
      *
+     * @param dataspaceEntity the dataspace
      * @param cpsPathQuery the cps path query to be transformed into a sql query
      * @return a executable query object
      */
-    public Query getQueryForCpsPath(final CpsPathQuery cpsPathQuery) {
-        final StringBuilder sqlStringBuilder = new StringBuilder("SELECT * FROM FRAGMENT WHERE xpath ~ :xpathRegex");
-        final Map<String, Object> queryParameters = new HashMap<>();
-        return getQuery(cpsPathQuery, sqlStringBuilder, queryParameters);
+    public Query getQueryForDataspaceAndCpsPath(final DataspaceEntity dataspaceEntity,
+                                                final CpsPathQuery cpsPathQuery) {
+        return getQueryForDataspaceOrAnchorAndCpsPath(dataspaceEntity, ACROSS_ALL_ANCHORS, cpsPathQuery);
     }
 
     /**
@@ -103,31 +102,45 @@ public class FragmentQueryBuilder {
         return xpathRegexBuilder.toString();
     }
 
-    private Query getQuery(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder,
-                           final Map<String, Object> queryParameters) {
-        final String xpathRegex = getXpathSqlRegex(cpsPathQuery);
-        queryParameters.put("xpathRegex", xpathRegex);
-        final List<String> queryBooleanOperatorsType = cpsPathQuery.getBooleanOperatorsType();
-        if (cpsPathQuery.hasLeafConditions()) {
-            sqlStringBuilder.append(" AND (");
-            final Queue<String> booleanOperatorsQueue = (queryBooleanOperatorsType == null) ? null : new LinkedList<>(
-                queryBooleanOperatorsType);
-            cpsPathQuery.getLeavesData().entrySet().forEach(entry -> {
-                sqlStringBuilder.append(" attributes @> ");
-                sqlStringBuilder.append("'" + jsonObjectMapper.asJsonString(entry) + "'");
-                if (!(booleanOperatorsQueue == null || booleanOperatorsQueue.isEmpty())) {
-                    sqlStringBuilder.append(" " + booleanOperatorsQueue.poll() + " ");
-                }
-            });
-            sqlStringBuilder.append(")");
-        }
+    private Query getQueryForDataspaceOrAnchorAndCpsPath(final DataspaceEntity dataspaceEntity,
+                                                         final AnchorEntity anchorEntity,
+                                                         final CpsPathQuery cpsPathQuery) {
+        final StringBuilder sqlStringBuilder = new StringBuilder();
+        final Map<String, Object> queryParameters = new HashMap<>();
+
+        sqlStringBuilder.append("SELECT * FROM fragment WHERE ");
+        addDataspaceOrAnchor(sqlStringBuilder, queryParameters, dataspaceEntity, anchorEntity);
+        addXpathSearch(cpsPathQuery, sqlStringBuilder, queryParameters);
+        addLeafConditions(cpsPathQuery, sqlStringBuilder);
         addTextFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
         addContainsFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
+
         final Query query = entityManager.createNativeQuery(sqlStringBuilder.toString(), FragmentEntity.class);
         setQueryParameters(query, queryParameters);
         return query;
     }
 
+    private static void addDataspaceOrAnchor(final StringBuilder sqlStringBuilder,
+                                             final Map<String, Object> queryParameters,
+                                             final DataspaceEntity dataspaceEntity,
+                                             final AnchorEntity anchorEntity) {
+        if (anchorEntity == ACROSS_ALL_ANCHORS) {
+            sqlStringBuilder.append("dataspace_id = :dataspaceId");
+            queryParameters.put("dataspaceId", dataspaceEntity.getId());
+        } else {
+            sqlStringBuilder.append("anchor_id = :anchorId");
+            queryParameters.put("anchorId", anchorEntity.getId());
+        }
+    }
+
+    private static void addXpathSearch(final CpsPathQuery cpsPathQuery,
+                                       final StringBuilder sqlStringBuilder,
+                                       final Map<String, Object> queryParameters) {
+        sqlStringBuilder.append(" AND xpath ~ :xpathRegex");
+        final String xpathRegex = getXpathSqlRegex(cpsPathQuery);
+        queryParameters.put("xpathRegex", xpathRegex);
+    }
+
     private static StringBuilder getRegexStringBuilderWithPrefix(final CpsPathQuery cpsPathQuery) {
         final StringBuilder xpathRegexBuilder = new StringBuilder();
         if (CpsPathPrefixType.ABSOLUTE.equals(cpsPathQuery.getCpsPathPrefixType())) {
@@ -153,6 +166,27 @@ public class FragmentQueryBuilder {
         }
     }
 
+    private void addLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
+        if (cpsPathQuery.hasLeafConditions()) {
+            sqlStringBuilder.append(" AND (");
+            final List<String> queryBooleanOperatorsType = cpsPathQuery.getBooleanOperatorsType();
+            final Queue<String> booleanOperatorsQueue = (queryBooleanOperatorsType == null) ? null : new LinkedList<>(
+                queryBooleanOperatorsType);
+            cpsPathQuery.getLeavesData().entrySet().forEach(entry -> {
+                sqlStringBuilder.append(" attributes @> ");
+                sqlStringBuilder.append("'");
+                sqlStringBuilder.append(jsonObjectMapper.asJsonString(entry));
+                sqlStringBuilder.append("'");
+                if (!(booleanOperatorsQueue == null || booleanOperatorsQueue.isEmpty())) {
+                    sqlStringBuilder.append(" ");
+                    sqlStringBuilder.append(booleanOperatorsQueue.poll());
+                    sqlStringBuilder.append(" ");
+                }
+            });
+            sqlStringBuilder.append(")");
+        }
+    }
+
     private static void addTextFunctionCondition(final CpsPathQuery cpsPathQuery,
                                                  final StringBuilder sqlStringBuilder,
                                                  final Map<String, Object> queryParameters) {
index f389467..d20e4d3 100755 (executable)
@@ -27,6 +27,7 @@ import java.util.Collection;
 import java.util.List;\r
 import java.util.Optional;\r
 import org.onap.cps.spi.entities.AnchorEntity;\r
+import org.onap.cps.spi.entities.DataspaceEntity;\r
 import org.onap.cps.spi.entities.FragmentEntity;\r
 import org.onap.cps.spi.entities.FragmentExtract;\r
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;\r
@@ -46,18 +47,25 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
             new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath));\r
     }\r
 \r
-    boolean existsByAnchorId(int anchorId);\r
+    List<FragmentEntity> findByAnchorIdAndXpathIn(int anchorId, String[] xpaths);\r
 \r
-    @Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor")\r
-    List<FragmentExtract> findAllExtractsByAnchor(@Param("anchor") AnchorEntity anchorEntity);\r
+    default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity,\r
+                                                        final Collection<String> xpaths) {\r
+        return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0]));\r
+    }\r
 \r
-    @Query(value = "SELECT * FROM fragment WHERE xpath = ANY (:xpaths)", nativeQuery = true)\r
-    List<FragmentEntity> findAllByXpathIn(@Param("xpaths") String[] xpath);\r
+    List<FragmentEntity> findByDataspaceIdAndXpathIn(int dataspaceId, String[] xpaths);\r
 \r
-    default List<FragmentEntity> findAllByXpathIn(final Collection<String> xpaths) {\r
-        return findAllByXpathIn(xpaths.toArray(new String[0]));\r
+    default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity,\r
+                                                           final Collection<String> xpaths) {\r
+        return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths.toArray(new String[0]));\r
     }\r
 \r
+    boolean existsByAnchorId(int anchorId);\r
+\r
+    @Query("SELECT f FROM FragmentEntity f WHERE anchor = :anchor")\r
+    List<FragmentExtract> findAllExtractsByAnchor(@Param("anchor") AnchorEntity anchorEntity);\r
+\r
     @Modifying\r
     @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true)\r
     void deleteByAnchorIdIn(@Param("anchorIds") int[] anchorIds);\r
@@ -92,12 +100,18 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
 \r
     @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"\r
         + " CAST(attributes AS TEXT) AS attributes"\r
-        + " FROM FRAGMENT WHERE anchor_id = :anchorId"\r
-        + " AND xpath ~ :xpathRegex",\r
+        + " FROM FRAGMENT WHERE anchor_id = :anchorId AND xpath ~ :xpathRegex",\r
         nativeQuery = true)\r
     List<FragmentExtract> quickFindWithDescendants(@Param("anchorId") int anchorId,\r
                                                    @Param("xpathRegex") String xpathRegex);\r
 \r
+    @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"\r
+        + " CAST(attributes AS TEXT) AS attributes"\r
+        + " FROM FRAGMENT WHERE dataspace_id = :dataspaceId AND xpath ~ :xpathRegex",\r
+        nativeQuery = true)\r
+    List<FragmentExtract> quickFindWithDescendantsAcrossAnchors(@Param("dataspaceId") int dataspaceId,\r
+                                                                @Param("xpathRegex") String xpathRegex);\r
+\r
     @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",\r
         nativeQuery = true)\r
     List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") int anchorId,\r
@@ -136,9 +150,4 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
         return findExtractsWithDescendants(anchorId, xpaths.toArray(new String[0]), maxDepth);\r
     }\r
 \r
-    @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"\r
-            + " CAST(attributes AS TEXT) AS attributes"\r
-            + " FROM FRAGMENT WHERE xpath ~ :xpathRegex",\r
-            nativeQuery = true)\r
-    List<FragmentExtract> quickFindWithDescendantsAcrossAnchors(@Param("xpathRegex") String xpathRegex);\r
 }\r
index 32041e7..de0c060 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Nordix Foundation.
+ *  Copyright (C) 2021-2023 Nordix Foundation.
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,10 +23,12 @@ package org.onap.cps.spi.repository;
 
 import java.util.List;
 import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.spi.entities.AnchorEntity;
+import org.onap.cps.spi.entities.DataspaceEntity;
 import org.onap.cps.spi.entities.FragmentEntity;
 
 public interface FragmentRepositoryCpsPathQuery {
-    List<FragmentEntity> findByAnchorAndCpsPath(int anchorId, CpsPathQuery cpsPathQuery);
+    List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery);
 
-    List<FragmentEntity> findByCpsPath(CpsPathQuery cpsPathQuery);
+    List<FragmentEntity> findByDataspaceAndCpsPath(DataspaceEntity dataspaceEntity, CpsPathQuery cpsPathQuery);
 }
index b95491c..6cc6495 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation.
+ *  Copyright (C) 2021-2023 Nordix Foundation.
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,6 +29,8 @@ import javax.transaction.Transactional;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.cpspath.parser.CpsPathQuery;
+import org.onap.cps.spi.entities.AnchorEntity;
+import org.onap.cps.spi.entities.DataspaceEntity;
 import org.onap.cps.spi.entities.FragmentEntity;
 
 @RequiredArgsConstructor
@@ -42,8 +44,9 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps
 
     @Override
     @Transactional
-    public List<FragmentEntity> findByAnchorAndCpsPath(final int anchorId, final CpsPathQuery cpsPathQuery) {
-        final Query query = fragmentQueryBuilder.getQueryForAnchorAndCpsPath(anchorId, cpsPathQuery);
+    public List<FragmentEntity> findByAnchorAndCpsPath(final AnchorEntity anchorEntity,
+                                                       final CpsPathQuery cpsPathQuery) {
+        final Query query = fragmentQueryBuilder.getQueryForAnchorAndCpsPath(anchorEntity, cpsPathQuery);
         final List<FragmentEntity> fragmentEntities = query.getResultList();
         log.debug("Fetched {} fragment entities by anchor and cps path.", fragmentEntities.size());
         return fragmentEntities;
@@ -51,8 +54,9 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps
 
     @Override
     @Transactional
-    public List<FragmentEntity> findByCpsPath(final CpsPathQuery cpsPathQuery) {
-        final Query query = fragmentQueryBuilder.getQueryForCpsPath(cpsPathQuery);
+    public List<FragmentEntity> findByDataspaceAndCpsPath(final DataspaceEntity dataspaceEntity,
+                                                          final CpsPathQuery cpsPathQuery) {
+        final Query query = fragmentQueryBuilder.getQueryForDataspaceAndCpsPath(dataspaceEntity, cpsPathQuery);
         final List<FragmentEntity> fragmentEntities = query.getResultList();
         log.debug("Fetched {} fragment entities by cps path across all anchors.", fragmentEntities.size());
         return fragmentEntities;
index 4e596da..d64617c 100644 (file)
@@ -25,7 +25,6 @@ import org.onap.cps.api.CpsQueryService
 import org.onap.cps.integration.base.FunctionalSpecBase
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.CpsPathException
-import spock.lang.Ignore
 
 import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
@@ -235,7 +234,6 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
             thrown(CpsPathException)
     }
 
-    @Ignore
     def 'Cps Path query across anchors with #scenario.'() {
         when: 'a query is executed to get a data nodes across anchors by the given CpsPath'
             def result = objectUnderTest.queryDataNodesAcrossAnchors(FUNCTIONAL_TEST_DATASPACE_1, cpsPath, OMIT_DESCENDANTS)
@@ -261,7 +259,6 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
             'ancestor combined with text condition'     | '//books/title[text()="Matilda"]/ancestor::bookstore' || ["/bookstore"]
     }
 
-    @Ignore
     def 'Cps Path query across anchors with #scenario descendants.'() {
         when: 'a query is executed to get a data node by the given cps path'
             def result = objectUnderTest.queryDataNodesAcrossAnchors(FUNCTIONAL_TEST_DATASPACE_1, '/bookstore', fetchDescendantsOption)
@@ -276,7 +273,6 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
             'all'    | INCLUDE_ALL_DESCENDANTS || 17
     }
 
-    @Ignore
     def 'Cps Path query across anchors with ancestors and #scenario descendants.'() {
         when: 'a query is executed to get a data node by the given cps path'
             def result = objectUnderTest.queryDataNodesAcrossAnchors(FUNCTIONAL_TEST_DATASPACE_1, '//books/ancestor::bookstore', fetchDescendantsOption)
index 0653657..0af01ac 100644 (file)
@@ -63,8 +63,7 @@ class QueryPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Query across anchors ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario                     | cpspath                                                             || durationLimit | expectedNumberOfDataNodes
-            // FIXME Current implementation of queryDataNodesAcrossAnchors throws NullPointerException for next case. Uncomment after CPS-1582 is done.
-            // 'top element'                | '/openroadm-devices'                                                || 1             | 5 * (50 * 86 + 1)
+            'top element'                | '/openroadm-devices'                                                || 1             | 5 * (50 * 86 + 1)
             'leaf condition'             | '//openroadm-device[@ne-state="inservice"]'                         || 2500          | 5 * (50 * 86)
             'ancestors'                  | '//openroadm-device/ancestor::openroadm-devices'                    || 12000         | 5 * (50 * 86 + 1)
             'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 1000          | 5 * (50 * 86 + 1)