4f056c8f6e2975cab1d319ea45297be6874043e8
[cps.git] / cps-ri / src / main / java / org / onap / cps / spi / repository / FragmentPrefetchRepositoryImpl.java
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2023 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.spi.repository;
22
23 import java.sql.Connection;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.function.Function;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31 import lombok.RequiredArgsConstructor;
32 import org.onap.cps.spi.FetchDescendantsOption;
33 import org.onap.cps.spi.entities.AnchorEntity;
34 import org.onap.cps.spi.entities.FragmentEntity;
35 import org.springframework.jdbc.core.JdbcTemplate;
36 import org.springframework.jdbc.core.PreparedStatementSetter;
37 import org.springframework.jdbc.core.RowMapper;
38 import org.springframework.stereotype.Repository;
39
40 @Repository
41 @RequiredArgsConstructor
42 public class FragmentPrefetchRepositoryImpl implements FragmentPrefetchRepository {
43
44     private final JdbcTemplate jdbcTemplate;
45
46     @Override
47     public Collection<FragmentEntity> prefetchDescendantsOfFragmentEntities(
48             final FetchDescendantsOption fetchDescendantsOption,
49             final Collection<FragmentEntity> proxiedFragmentEntities) {
50
51         if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) {
52             return proxiedFragmentEntities;
53         }
54
55         final List<Long> fragmentEntityIds = proxiedFragmentEntities.stream()
56                 .map(FragmentEntity::getId).collect(Collectors.toList());
57
58         final Map<Long, AnchorEntity> anchorEntityPerId = proxiedFragmentEntities.stream()
59                 .map(FragmentEntity::getAnchor)
60                 .collect(Collectors.toMap(AnchorEntity::getId, anchor -> anchor, (anchor1, anchor2) -> anchor1));
61
62         final int maxDepth = fetchDescendantsOption.equals(FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
63                 ? Integer.MAX_VALUE
64                 : fetchDescendantsOption.getDepth();
65         return findFragmentEntitiesWithDescendantsByIds(fragmentEntityIds, anchorEntityPerId, maxDepth);
66     }
67
68     private Collection<FragmentEntity> findFragmentEntitiesWithDescendantsByIds(
69             final Collection<Long> fragmentEntityIds,
70             final Map<Long, AnchorEntity> anchorEntityPerId,
71             final int maxDepth) {
72         final String sql
73                 = "WITH RECURSIVE parent_search AS ("
74                 + "    SELECT id, 0 AS depth "
75                 + "    FROM fragment "
76                 + "    WHERE id = ANY (?) "
77                 + "  UNION "
78                 + "    SELECT child.id, depth + 1 "
79                 + "    FROM fragment child INNER JOIN parent_search parent ON child.parent_id = parent.id"
80                 + "    WHERE depth < ?"
81                 + ") "
82                 + "SELECT fragment.id, anchor_id AS anchorId, xpath, parent_id AS parentId, "
83                 + "       CAST(attributes AS TEXT) AS attributes "
84                 + "FROM fragment INNER JOIN parent_search ON fragment.id = parent_search.id";
85
86         final PreparedStatementSetter preparedStatementSetter = preparedStatement -> {
87             final Connection connection = preparedStatement.getConnection();
88             final java.sql.Array idArray = connection.createArrayOf("bigint", fragmentEntityIds.toArray());
89             preparedStatement.setArray(1, idArray);
90             preparedStatement.setInt(2, maxDepth);
91         };
92
93         final RowMapper<FragmentEntity> fragmentEntityRowMapper = (resultSet, rowNum) -> {
94             final FragmentEntity fragmentEntity = new FragmentEntity();
95             fragmentEntity.setId(resultSet.getLong("id"));
96             fragmentEntity.setXpath(resultSet.getString("xpath"));
97             fragmentEntity.setParentId(resultSet.getLong("parentId"));
98             fragmentEntity.setAttributes(resultSet.getString("attributes"));
99             fragmentEntity.setAnchor(anchorEntityPerId.get(resultSet.getLong("anchorId")));
100             fragmentEntity.setChildFragments(new HashSet<>());
101             return fragmentEntity;
102         };
103
104         final Map<Long, FragmentEntity> fragmentEntityPerId;
105         try (final Stream<FragmentEntity> fragmentEntityStream = jdbcTemplate.queryForStream(sql,
106                 preparedStatementSetter, fragmentEntityRowMapper)) {
107             fragmentEntityPerId = fragmentEntityStream.collect(
108                     Collectors.toMap(FragmentEntity::getId, Function.identity()));
109         }
110         return reuniteChildrenWithTheirParents(fragmentEntityPerId);
111     }
112
113     private static Collection<FragmentEntity> reuniteChildrenWithTheirParents(
114             final Map<Long, FragmentEntity> fragmentEntityPerId) {
115         final Collection<FragmentEntity> fragmentEntitiesWithoutParent = new HashSet<>();
116         for (final FragmentEntity fragmentEntity : fragmentEntityPerId.values()) {
117             final FragmentEntity parentFragmentEntity = fragmentEntityPerId.get(fragmentEntity.getParentId());
118             if (parentFragmentEntity == null) {
119                 fragmentEntitiesWithoutParent.add(fragmentEntity);
120             } else {
121                 parentFragmentEntity.getChildFragments().add(fragmentEntity);
122             }
123         }
124         return fragmentEntitiesWithoutParent;
125     }
126
127 }