Node API - Find root by anchor method performance 44/132044/7
authorsourabh_sourabh <sourabh.sourabh@est.tech>
Tue, 8 Nov 2022 11:46:05 +0000 (11:46 +0000)
committersourabh_sourabh <sourabh.sourabh@est.tech>
Fri, 11 Nov 2022 12:30:57 +0000 (12:30 +0000)
- Modified findFirstRootByAnchor
- Used fragment extract to build fragment entity to get root data node.

Reviewers : Toine, Priyank and Joe

Issue-ID: CPS-1171
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Change-Id: I843f270e4781f91ae496f39b976e2a7f2a14d55e
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
cps-ri/src/test/resources/data/fragment.sql

index 50187a4..27891c5 100644 (file)
@@ -57,6 +57,7 @@ public class FragmentEntityArranger {
         fragmentEntity.setAttributes(fragmentExtract.getAttributes());
         fragmentEntity.setParentId(fragmentExtract.getParentId());
         fragmentEntity.setChildFragments(new HashSet<>());
+        fragmentEntity.setDataspace(anchorEntity.getDataspace());
         return fragmentEntity;
     }
 
@@ -70,10 +71,7 @@ public class FragmentEntityArranger {
                 parentFragmentEntity.getChildFragments().add(fragmentEntity);
             }
         }
-        if (fragmentEntitiesWithoutParentInResultSet.iterator().hasNext()) {
-            return fragmentEntitiesWithoutParentInResultSet.iterator().next();
-        }
-        return null;
+        return fragmentEntitiesWithoutParentInResultSet.stream().findFirst().orElse(null);
     }
 
 }
index dc848e6..ac1be1c 100644 (file)
@@ -230,7 +230,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         if (isRootXpath(xpath)) {
-            return fragmentRepository.findFirstRootByDataspaceAndAnchor(dataspaceEntity, anchorEntity);
+            final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity,
+                    anchorEntity);
+            final FragmentEntity fragmentEntity = FragmentEntityArranger.toFragmentEntityTree(anchorEntity,
+                    fragmentExtracts);
+            return fragmentEntity;
         } else {
             final String normalizedXpath = getNormalizedXpath(xpath);
             final FragmentEntity fragmentEntity;
@@ -500,7 +504,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
                                 final boolean onlySupportListNodeDeletion) {
         final String parentNodeXpath;
         FragmentEntity parentFragmentEntity = null;
-        boolean targetDeleted = false;
+        boolean targetDeleted;
         if (isRootXpath(targetXpath)) {
             deleteDataNodes(dataspaceName, anchorName);
             targetDeleted = true;
index 112ebfd..2c25a61 100755 (executable)
@@ -1,6 +1,6 @@
 /*\r
  * ============LICENSE_START=======================================================\r
- * Copyright (C) 2020-2021 Nordix Foundation.\r
+ * Copyright (C) 2021-2022 Nordix Foundation.\r
  * Modifications Copyright (C) 2020-2021 Bell Canada.\r
  * Modifications Copyright (C) 2020-2021 Pantheon.tech.\r
  * ================================================================================\r
@@ -58,10 +58,26 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
     List<FragmentEntity> findRootsByDataspaceAndAnchor(@Param("dataspace") int dataspaceId,\r
                                                        @Param("anchor") int anchorId);\r
 \r
-    default FragmentEntity findFirstRootByDataspaceAndAnchor(@NonNull DataspaceEntity dataspaceEntity,\r
-                                                             @NonNull AnchorEntity anchorEntity) {\r
-        return findRootsByDataspaceAndAnchor(dataspaceEntity.getId(), anchorEntity.getId()).stream().findFirst()\r
-            .orElseThrow(() -> new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName()));\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
+            nativeQuery = true)\r
+    List<FragmentExtract> findRootsByAnchorId(@Param("anchorId") int anchorId);\r
+\r
+    /**\r
+     * find top level fragment by anchor.\r
+     *\r
+     * @param dataspaceEntity dataspace entity\r
+     * @param anchorEntity anchor entity\r
+     * @return FragmentEntity fragment entity\r
+     */\r
+    default List<FragmentExtract> getTopLevelFragments(DataspaceEntity dataspaceEntity,\r
+                                                       AnchorEntity anchorEntity) {\r
+        final List<FragmentExtract> fragmentExtracts = findRootsByAnchorId(anchorEntity.getId());\r
+        if (fragmentExtracts.isEmpty()) {\r
+            throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName());\r
+        }\r
+        return fragmentExtracts;\r
     }\r
 \r
     List<FragmentEntity> findAllByAnchorAndXpathIn(@NonNull AnchorEntity anchorEntity,\r
index a5e17cf..fbf414d 100755 (executable)
@@ -56,7 +56,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     static final int ANCHOR_3003_ID = 3003L
     static final long ID_DATA_NODE_WITH_DESCENDANTS = 4001
     static final String XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1'
-    static final String XPATH_DATA_NODE_WITH_LEAVES = '/parent-100'
+    static final String XPATH_DATA_NODE_WITH_LEAVES = '/parent-207'
     static final long DATA_NODE_202_FRAGMENT_ID = 4202L
     static final long CHILD_OF_DATA_NODE_202_FRAGMENT_ID = 4203L
     static final long LIST_DATA_NODE_PARENT201_FRAGMENT_ID = 4206L
@@ -69,10 +69,10 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     static DataNode existingChildDataNode
 
     def expectedLeavesByXpathMap = [
-            '/parent-100'                      : ['parent-leaf': 'parent-leaf value'],
-            '/parent-100/child-001'            : ['first-child-leaf': 'first-child-leaf value'],
-            '/parent-100/child-002'            : ['second-child-leaf': 'second-child-leaf value'],
-            '/parent-100/child-002/grand-child': ['grand-child-leaf': 'grand-child-leaf value']
+            '/parent-207'                      : ['parent-leaf': 'parent-leaf value'],
+            '/parent-207/child-001'            : ['first-child-leaf': 'first-child-leaf value'],
+            '/parent-207/child-002'            : ['second-child-leaf': 'second-child-leaf value'],
+            '/parent-207/child-002/grand-child': ['grand-child-leaf': 'grand-child-leaf value']
     ]
 
     static {
@@ -196,24 +196,24 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Add multiple list with a mix of existing and new elements'() {
         given: 'two new child list elements for an existing parent'
-            def existingDataNode = dataNodeBuilder.withXpath('/parent-100/child-001').withLeaves(['id': '001']).build()
-            def newDataNode1 = dataNodeBuilder.withXpath('/parent-100/child-new1').withLeaves(['id': 'new1']).build()
+            def existingDataNode = dataNodeBuilder.withXpath('/parent-207/child-001').withLeaves(['id': '001']).build()
+            def newDataNode1 = dataNodeBuilder.withXpath('/parent-207/child-new1').withLeaves(['id': 'new1']).build()
             def newDataNode2 = dataNodeBuilder.withXpath('/parent-200/child-new2').withLeaves(['id': 'new2']).build()
             def dataNodeList1 = [existingDataNode, newDataNode1]
             def dataNodeList2 = [newDataNode2]
         when: 'duplicate data node is requested to be added'
-            objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_NAME3, '/', [dataNodeList1,dataNodeList2])
+            objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT, '/', [dataNodeList1, dataNodeList2])
         then: 'already defined batch exception is thrown'
             def thrown = thrown(AlreadyDefinedExceptionBatch)
         and: 'it only contains the xpath(s) of the duplicated elements'
             assert thrown.alreadyDefinedXpaths.size() == 1
-            assert thrown.alreadyDefinedXpaths.contains('/parent-100/child-001')
+            assert thrown.alreadyDefinedXpaths.contains('/parent-207/child-001')
         and: 'it does NOT contains the xpaths of the new element that were not combined with existing elements'
-            assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1')
-            assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1')
+            assert !thrown.alreadyDefinedXpaths.contains('/parent-207/child-new1')
+            assert !thrown.alreadyDefinedXpaths.contains('/parent-207/child-new1')
         and: 'the new entity is inserted correctly'
             def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME)
-            def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3)
+            def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT)
             fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, '/parent-200/child-new2').isPresent()
     }
 
@@ -234,7 +234,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Get data node by xpath without descendants.'() {
         when: 'data node is requested'
-            def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES,
+            def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT,
                     inputXPath, OMIT_DESCENDANTS)
         then: 'data node is returned with no descendants'
             assert result.xpath == XPATH_DATA_NODE_WITH_LEAVES
@@ -243,7 +243,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
             assertLeavesMaps(result.leaves, expectedLeavesByXpathMap[XPATH_DATA_NODE_WITH_LEAVES])
         where: 'the following data is used'
             scenario      | inputXPath
-            'some xpath'  | '/parent-100'
+            'some xpath'  | '/parent-207'
             'root xpath'  | '/'
             'empty xpath' | ''
     }
@@ -260,20 +260,20 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Get data node by xpath with all descendants.'() {
         when: 'data node is requested with all descendants'
-            def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES,
+            def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT,
                     inputXPath, INCLUDE_ALL_DESCENDANTS)
             def mappedResult = treeToFlatMapByXpath(new HashMap<>(), result)
         then: 'data node is returned with all the descendants populated'
             assert mappedResult.size() == 4
             assert result.childDataNodes.size() == 2
-            assert mappedResult.get('/parent-100/child-001').childDataNodes.size() == 0
-            assert mappedResult.get('/parent-100/child-002').childDataNodes.size() == 1
+            assert mappedResult.get('/parent-207/child-001').childDataNodes.size() == 0
+            assert mappedResult.get('/parent-207/child-002').childDataNodes.size() == 1
         and: 'extracted leaves maps are matching expected'
             mappedResult.forEach(
                     (xPath, dataNode) -> assertLeavesMaps(dataNode.leaves, expectedLeavesByXpathMap[xPath]))
         where: 'the following data is used'
             scenario      | inputXPath
-            'some xpath'  | '/parent-100'
+            'some xpath'  | '/parent-207'
             'root xpath'  | '/'
             'empty xpath' | ''
     }
index d6f10d8..1ecad4e 100644 (file)
@@ -68,4 +68,5 @@ class CpsPersistenceSpecBase extends Specification {
     static final String ANCHOR_NAME3 = 'ANCHOR-003'
     static final String ANCHOR_FOR_DATA_NODES_WITH_LEAVES = 'ANCHOR-003'
     static final String ANCHOR_FOR_SHOP_EXAMPLE = 'ANCHOR-004'
+    static final String ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT = 'ANCHOR-005'
 }
index bc6e4e7..ad463cf 100755 (executable)
@@ -51,7 +51,8 @@ INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
     (3001, 'ANCHOR-001', 1001, 2001),
     (3003, 'ANCHOR-003', 1001, 2001),
-    (3004, 'ncmp-dmi-registry', 1002, 2001);
+    (3004, 'ncmp-dmi-registry', 1002, 2001),
+    (3005, 'ANCHOR-005', 1001, 2001);
 
 INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH) VALUES
     (4001, 1001, 3001, null, '/parent-1'),
@@ -62,10 +63,10 @@ INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH) VALUES
     (4006, 1001, 3001, 4004, '/parent-1/child-1/grandchild-1');
 
 INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (4101, 1001, 3003, null, '/parent-100', '{"parent-leaf": "parent-leaf value"}'),
-    (4102, 1001, 3003, 4101, '/parent-100/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
-    (4103, 1001, 3003, 4101, '/parent-100/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
-    (4104, 1001, 3003, 4103, '/parent-100/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
+    (5009, 1001, 3005, null, '/parent-207', '{"parent-leaf": "parent-leaf value"}'),
+    (5010, 1001, 3005, 5009, '/parent-207/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
+    (5011, 1001, 3005, 5009, '/parent-207/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
+    (5012, 1001, 3005, 5011, '/parent-207/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
 
 INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
     (4201, 1001, 3003, null, '/parent-200', '{"leaf-value": "original"}'),