Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features/6092 improve request speed #173

Merged
merged 13 commits into from
Jan 13, 2025
5 changes: 5 additions & 0 deletions geonetwork4-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package au.org.aodn.esindexer.model;
package au.org.aodn.metadata.geonetwork;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public interface AppConstants {
String PORTAL_RECORDS_MAPPING_JSON_FILE = "portal_records_index_schema.json";
String DATASET_INDEX_MAPPING_JSON_FILE = "dataset_index_schema.json";
String DATASET_INDEX_MAPPING_JSON_FILE = "data_index_schema.json";

String FORMAT_XML = "xml";
String FORMAT_ISO19115_3_2018 = "iso19115-3.2018";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package au.org.aodn.esindexer.configuration;

import au.org.aodn.esindexer.service.DataAccessService;
import au.org.aodn.esindexer.service.DataAccessServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class DatasetAccessConfig {

@Bean(name = "DataAccessService")
@Bean
@ConditionalOnMissingBean(DataAccessService.class)
public DataAccessServiceImpl createDataAccessService(
@Value("${dataaccess.host:defaultForTesting}") String serverUrl
){
return new DataAccessServiceImpl(serverUrl);
@Value("${dataaccess.host:http://localhost:5000}") String serverUrl,
@Value("${dataaccess.baseUrl:/api/v1/das/}") String baseUrl,
@Autowired RestTemplate template){

return new DataAccessServiceImpl(serverUrl, baseUrl, template);
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package au.org.aodn.esindexer.configuration;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ObjectMapperConfig {
@Bean("indexerObjectMapper")
public static ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();

// Enable pretty printing for JSON output
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

// Ignore unknown properties during deserialization
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
ObjectMapper objectMapper = JsonMapper.builder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
// Enable pretty printing for JSON output
.enable(SerializationFeature.INDENT_OUTPUT)
// Ignore unknown properties during deserialization
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();

// Use a specific date format for serialization and deserialization (if needed)
// objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package au.org.aodn.esindexer.controller;

import au.org.aodn.esindexer.service.DataAccessService;
import au.org.aodn.esindexer.service.GeoNetworkService;
import au.org.aodn.esindexer.service.IndexerService;
import au.org.aodn.esindexer.model.TemporalExtent;
import au.org.aodn.esindexer.service.*;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -19,10 +18,9 @@
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping(value = "/api/v1/indexer/index")
Expand All @@ -31,7 +29,10 @@
public class IndexerController {

@Autowired
IndexerService indexerService;
IndexerMetadataService indexerMetadata;

@Autowired
IndexCloudOptimizedService indexCloudOptimizedData;

@Autowired
GeoNetworkService geonetworkResourceService;
Expand All @@ -51,7 +52,7 @@ public ResponseEntity<String> getMetadataRecordFromGeoNetworkByUUID(@PathVariabl
@Operation(description = "Get a document from portal index by UUID")
public ResponseEntity<ObjectNode> getDocumentByUUID(@PathVariable("uuid") String uuid) throws IOException {
log.info("getting a document form portal by UUID: {}", uuid);
ObjectNode response = indexerService.getDocumentByUUID(uuid).source();
ObjectNode response = indexerMetadata.getDocumentByUUID(uuid).source();
return ResponseEntity.status(HttpStatus.OK).body(response);
}
/**
Expand All @@ -69,13 +70,12 @@ public ResponseEntity<String> indexAllMetadataRecords(
@RequestParam(value = "confirm", defaultValue = "false") Boolean confirm,
@RequestParam(value = "beginWithUuid", required=false) String beginWithUuid) throws IOException {

List<BulkResponse> responses = indexerService.indexAllMetadataRecordsFromGeoNetwork(beginWithUuid, confirm, null);
List<BulkResponse> responses = indexerMetadata.indexAllMetadataRecordsFromGeoNetwork(beginWithUuid, confirm, null);
return ResponseEntity.ok(responses.toString());
}
/**
* Emit result to FE so it will not result in gateway time-out. You need to run it with postman or whatever tools
* support server side event, the content type needs to be text/event-stream in order to work
*
* Noted: There is a bug in postman desktop, so either you run postman using web-browser with agent directly
* or you need to have version 10.2 or above in order to get the emitted result
*
Expand All @@ -90,8 +90,65 @@ public SseEmitter indexAllMetadataRecordsAsync(
@RequestParam(value = "beginWithUuid", required=false) String beginWithUuid) {

final SseEmitter emitter = new SseEmitter(0L); // 0L means no timeout;
final IndexService.Callback callback = createCallback(emitter);

new Thread(() -> {
try {
indexerMetadata.indexAllMetadataRecordsFromGeoNetwork(beginWithUuid, confirm, callback);
}
catch(IOException e) {
emitter.completeWithError(e);
}
}).start();

return emitter;
}

@PostMapping(path="/{uuid}", produces = "application/json")
@Operation(security = { @SecurityRequirement(name = "X-API-Key") }, description = "Index a metadata record by UUID")
public ResponseEntity<String> addDocumentByUUID(@PathVariable("uuid") String uuid) throws IOException, FactoryException, JAXBException, TransformException {
String metadataValues = geonetworkResourceService.searchRecordBy(uuid);

CompletableFuture<ResponseEntity<String>> f = indexerMetadata.indexMetadata(metadataValues);
// Return when done make it back to sync instead of async
return f.join();
}

@DeleteMapping(path="/{uuid}", produces = "application/json")
@Operation(security = { @SecurityRequirement(name = "X-API-Key") }, description = "Delete a metadata record by UUID")
public ResponseEntity<String> deleteDocumentByUUID(@PathVariable("uuid") String uuid) throws IOException {
return indexerMetadata.deleteDocumentByUUID(uuid);
}

@PostMapping(path="/{uuid}/dataset", produces = "application/json")
@Operation(security = {@SecurityRequirement(name = "X-API-Key") }, description = "Index a dataset by UUID")
public SseEmitter indexDatasetByUUID(@PathVariable("uuid") String uuid) {

final SseEmitter emitter = new SseEmitter(0L); // 0L means no timeout;
final IndexService.Callback callback = createCallback(emitter);

IndexerService.Callback callback = new IndexerService.Callback() {
new Thread(() -> {
try {
List<TemporalExtent> temporalExtents = dataAccessService.getTemporalExtentOf(uuid);
if (!temporalExtents.isEmpty()) {
// Only first block works from dataservice api
LocalDate startDate = temporalExtents.get(0).getLocalStartDate();
LocalDate endDate = temporalExtents.get(0).getLocalEndDate();
log.info("Indexing dataset with UUID: {} from {} to {}", uuid, startDate, endDate);

indexCloudOptimizedData.indexCloudOptimizedData(uuid, startDate, endDate, callback);
}
}
finally {
emitter.complete();
}
}).start();

return emitter;
}

protected IndexerMetadataService.Callback createCallback(SseEmitter emitter) {
return new IndexService.Callback() {
@Override
public void onProgress(Object update) {
try {
Expand All @@ -102,8 +159,7 @@ public void onProgress(Object update) {
.name("Indexer update event");

emitter.send(event);
}
catch (IOException e) {
} catch (IOException e) {
// In case of fail, try close the stream, if it cannot be closed. (likely stream terminated
// already, the load error out and we need to result from a particular uuid.
emitter.completeWithError(e);
Expand All @@ -121,57 +177,10 @@ public void onComplete(Object result) {

emitter.send(event);
emitter.complete();
}
catch (IOException e) {
} catch (IOException e) {
emitter.completeWithError(e);
}
}
};

new Thread(() -> {
try {
indexerService.indexAllMetadataRecordsFromGeoNetwork(beginWithUuid, confirm, callback);
}
catch(IOException e) {
emitter.completeWithError(e);
}
}).start();

return emitter;
}

@PostMapping(path="/{uuid}", produces = "application/json")
@Operation(security = { @SecurityRequirement(name = "X-API-Key") }, description = "Index a metadata record by UUID")
public ResponseEntity<String> addDocumentByUUID(@PathVariable("uuid") String uuid) throws IOException, FactoryException, JAXBException, TransformException, ExecutionException, InterruptedException {
String metadataValues = geonetworkResourceService.searchRecordBy(uuid);

CompletableFuture<ResponseEntity<String>> f = indexerService.indexMetadata(metadataValues);
// Return when done make it back to sync instead of async
return f.join();
}

@DeleteMapping(path="/{uuid}", produces = "application/json")
@Operation(security = { @SecurityRequirement(name = "X-API-Key") }, description = "Delete a metadata record by UUID")
public ResponseEntity<String> deleteDocumentByUUID(@PathVariable("uuid") String uuid) throws IOException {
return indexerService.deleteDocumentByUUID(uuid);
}

@PostMapping(path="/{uuid}/dataset", produces = "application/json")
@Operation(security = {@SecurityRequirement(name = "X-API-Key") }, description = "Index a dataset by UUID")
public ResponseEntity<List<String>> indexDatasetByUUID(@PathVariable("uuid") String uuid) {

var temporalExtents = dataAccessService.getTemporalExtentOf(uuid);
var startDate = temporalExtents.getStartDate();
var endDate = temporalExtents.getEndDate();
log.info("Indexing dataset with UUID: {} from {} to {}", uuid, startDate, endDate);

var responses = indexerService.indexDataset(uuid, startDate, endDate);

List<String> result = new ArrayList<>();
for (BulkResponse response : responses) {
result.add(response.toString());
}

return ResponseEntity.ok(result);
}
}
Loading
Loading