diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/CacheConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/CacheConfig.java new file mode 100644 index 00000000..5386d966 --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/CacheConfig.java @@ -0,0 +1,14 @@ +package au.org.aodn.ogcapi.server.core.configuration; + +import au.org.aodn.ogcapi.server.core.service.CacheNoLandGeometry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CacheConfig { + + @Bean + public CacheNoLandGeometry createCacheNoLandGeometry() { + return new CacheNoLandGeometry(); + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java index 2254b1a6..3bdcabc3 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/configuration/ElasticSearchConfig.java @@ -1,5 +1,6 @@ package au.org.aodn.ogcapi.server.core.configuration; +import au.org.aodn.ogcapi.server.core.service.CacheNoLandGeometry; import au.org.aodn.ogcapi.server.core.service.ElasticSearch; import au.org.aodn.ogcapi.server.core.service.Search; import co.elastic.clients.elasticsearch.ElasticsearchClient; @@ -59,11 +60,12 @@ public ElasticsearchClient geoNetworkElasticsearchClient(RestClientTransport tra */ @Bean public Search createElasticSearch(ElasticsearchClient client, + CacheNoLandGeometry cacheNoLandGeometry, ObjectMapper mapper, @Value("${elasticsearch.index.name}") String indexName, @Value("${elasticsearch.index.pageSize:2200}") Integer pageSize, @Value("${elasticsearch.search_as_you_type.size:10}") Integer searchAsYouTypeSize) { - return new ElasticSearch(client, mapper, indexName, pageSize, searchAsYouTypeSize); + return new ElasticSearch(client, cacheNoLandGeometry, mapper, indexName, pageSize, searchAsYouTypeSize); } } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java index 1c6386a0..f1674f7b 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java @@ -55,6 +55,12 @@ public enum CQLFields implements CQLFieldsInterface { null, null ), + centroid_nocache( + StacSummeries.GeometryNoLand.searchField, + StacSummeries.GeometryNoLand.displayField, + null, + null + ), temporal( StacSummeries.Temporal.searchField, StacSummeries.Temporal.displayField, diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/CacheNoLandGeometry.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/CacheNoLandGeometry.java new file mode 100644 index 00000000..6d1a697c --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/CacheNoLandGeometry.java @@ -0,0 +1,62 @@ +package au.org.aodn.ogcapi.server.core.service; + +import au.org.aodn.ogcapi.server.core.model.StacCollectionModel; +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLFields; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Scheduled; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * This class is used to cache some of the search result which is very expensive to transfer. + */ +@Slf4j +public class CacheNoLandGeometry { + + @Lazy + @Autowired + ElasticSearch elasticSearch; + + @Autowired + @Lazy + private CacheNoLandGeometry self; + + /** + * Init cache after 1 second but then never call again due to Long.MAX_VALUE + * @return A Map include the uuid and the noloand geometry + */ + @Scheduled(initialDelay = 1000, fixedDelay = Long.MAX_VALUE) + @Cacheable("all_noland_geometry") + public Map getAllNoLandGeometry() { + ElasticSearchBase.SearchResult result = elasticSearch.searchCollectionBy( + null, + null, + null, + List.of(CQLFields.id.name(), CQLFields.centroid_nocache.name()), + null, + null, + null, + null); + + return result.collections + .stream() + .collect(Collectors.toMap(StacCollectionModel::getUuid, Function.identity())); + } + /** + * Refresh every 24 hrs + */ + @Scheduled(initialDelay = 86400000, fixedDelay = 86400000) + @CacheEvict(value = "all_noland_geometry", allEntries = true) + protected void refreshAllCache() { + // Refresh on Evict + log.info("Evict and refresh cache"); + self.getAllNoLandGeometry(); + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java index 2c58be83..9d374c8f 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearch.java @@ -59,6 +59,7 @@ public class ElasticSearch extends ElasticSearchBase implements Search { protected String searchAfterSplitRegex; public ElasticSearch(ElasticsearchClient client, + CacheNoLandGeometry cacheNoLandGeometry, ObjectMapper mapper, String indexName, Integer pageSize, @@ -69,6 +70,7 @@ public ElasticSearch(ElasticsearchClient client, this.setIndexName(indexName); this.setPageSize(pageSize); this.setSearchAsYouTypeSize(searchAsYouTypeSize); + this.setCacheNoLandGeometry(cacheNoLandGeometry); this.defaultElasticSetting = CQLToElasticFilterFactory.getDefaultSetting(); } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java index 0a656b29..571b3699 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/ElasticSearchBase.java @@ -42,6 +42,7 @@ abstract class ElasticSearchBase { protected Integer pageSize; protected ElasticsearchClient esClient; protected ObjectMapper mapper; + protected CacheNoLandGeometry cacheNoLandGeometry; @Getter @Setter @@ -88,7 +89,7 @@ protected SearchRequest buildSearchAsYouTypeRequest( List searchAsYouTypeQueries, List filters) { - // By default it is limited to 10 even not specify, we want to use a variable so that we can change it later if needed. + // By default, it is limited to 10 even not specify, we want to use a variable so that we can change it later if needed. return new SearchRequest.Builder() .size(searchAsYouTypeSize) .index(indexName) @@ -166,6 +167,8 @@ protected SearchResult searchCollectionBy(final List queries, // Convert the income field name to the real field name in STAC List fs = properties .stream() + // Centroid point is cached, so no need to set it in the query but need to back-fill it + .filter(f -> !f.equalsIgnoreCase(CQLFields.centroid.name())) .flatMap(v -> CQLFields.valueOf(v).getDisplayField().stream()) .filter(Objects::nonNull) .toList(); @@ -195,7 +198,20 @@ protected SearchResult searchCollectionBy(final List queries, List lastSortValue = null; for(Hit i : response) { if(i != null) { - result.collections.add(this.formatResult(i.source())); + StacCollectionModel model = this.formatResult(i.source()); + // Use cache value of noland_geometry if user request centroid + if(properties != null && properties.contains(CQLFields.centroid.name())) { + StacCollectionModel cache = cacheNoLandGeometry.getAllNoLandGeometry().get(model.getUuid()); + if(cache.getSummaries() != null) { + if(model.getSummaries() == null) { + model.setSummaries(cache.getSummaries()); + } + else { + model.getSummaries().setGeometryNoLand(cache.getSummaries().getGeometryNoLand()); + } + } + } + result.collections.add(model); lastSortValue = i.sort(); } }