From 2cf90833c8963408cb5169f67c388d6993711741 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Sat, 6 Dec 2025 11:27:42 +0530 Subject: [PATCH 01/13] fix: ES Implementation-mapping, indexing and async records --- pom.xml | 10 + .../common/identity/config/AsyncConfig.java | 56 +++ .../identity/config/ElasticsearchConfig.java | 52 +++ .../elasticsearch/BeneficiaryDocument.java | 72 ++++ .../elasticsearch/ElasticsearchSyncJob.java | 95 +++++ .../com/iemr/common/identity/domain/User.java | 2 +- .../common/identity/repo/BenMappingRepo.java | 39 ++ .../repo/elasticsearch/SyncJobRepo.java | 43 +++ .../identity/service/IdentityService.java | 45 ++- .../AsyncElasticsearchSyncService.java | 334 ++++++++++++++++++ .../elasticsearch/BeneficiaryDataService.java | 149 ++++++++ .../OptimizedBeneficiaryDataService.java | 158 +++++++++ .../service/elasticsearch/SyncJobService.java | 145 ++++++++ .../TransactionalSyncWrapper.java | 67 ++++ .../identity/utils/JwtAuthenticationUtil.java | 2 +- src/main/resources/application.properties | 75 +++- 16 files changed, 1326 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/iemr/common/identity/config/AsyncConfig.java create mode 100644 src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java create mode 100644 src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java create mode 100644 src/main/java/com/iemr/common/identity/data/elasticsearch/ElasticsearchSyncJob.java create mode 100644 src/main/java/com/iemr/common/identity/repo/elasticsearch/SyncJobRepo.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java diff --git a/pom.xml b/pom.xml index a9499b72..658d21d0 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,16 @@ 1.3.2 + org.springframework.boot + spring-boot-starter-data-elasticsearch + + + co.elastic.clients + elasticsearch-java + 8.11.0 + + + org.springframework.boot spring-boot-devtools runtime diff --git a/src/main/java/com/iemr/common/identity/config/AsyncConfig.java b/src/main/java/com/iemr/common/identity/config/AsyncConfig.java new file mode 100644 index 00000000..e84cd1c4 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/config/AsyncConfig.java @@ -0,0 +1,56 @@ +package com.iemr.common.identity.config; + +import java.util.concurrent.Executor; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +/** + * Configuration for async processing and scheduling + */ +@Configuration +@EnableAsync +@EnableScheduling +public class AsyncConfig { + + /** + * Thread pool for Elasticsearch sync operations + * Configured for long-running background jobs + */ + @Bean(name = "elasticsearchSyncExecutor") + public Executor elasticsearchSyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + // Only 1-2 sync jobs should run at a time to avoid overwhelming DB/ES + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("es-sync-"); + executor.setKeepAliveSeconds(60); + + // Handle rejected tasks + executor.setRejectedExecutionHandler((r, executor1) -> { + throw new RuntimeException("Elasticsearch sync queue is full. Please wait for current job to complete."); + }); + + executor.initialize(); + return executor; + } + + /** + * General purpose async executor + */ + @Bean(name = "taskExecutor") + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("async-"); + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java b/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java new file mode 100644 index 00000000..2d0a0505 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java @@ -0,0 +1,52 @@ +package com.iemr.common.identity.config; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ElasticsearchConfig { + + @Value("${elasticsearch.host}") + private String esHost; + + @Value("${elasticsearch.port}") + private int esPort; + + @Value("${elasticsearch.username}") + private String esUsername; + + @Value("${elasticsearch.password}") + private String esPassword; + + @Bean + public ElasticsearchClient elasticsearchClient() { + BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(esUsername, esPassword) + ); + + RestClient restClient = RestClient.builder( + new HttpHost(esHost, esPort, "http") + ).setHttpClientConfigCallback(httpClientBuilder -> + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) + ).build(); + + ElasticsearchTransport transport = new RestClientTransport( + restClient, + new JacksonJsonpMapper() + ); + + return new ElasticsearchClient(transport); + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java new file mode 100644 index 00000000..1c53626e --- /dev/null +++ b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java @@ -0,0 +1,72 @@ +package com.iemr.common.identity.data.elasticsearch; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigInteger; + +import lombok.Data; + +@Data +public class BeneficiaryDocument { + + @JsonProperty("benId") + private String benId; + + @JsonProperty("benRegId") + private Long benRegId; + + @JsonProperty("phoneNum") + private String phoneNum; + + @JsonProperty("firstName") + private String firstName; + + @JsonProperty("lastName") + private String lastName; + + @JsonProperty("age") + private Integer age; + + @JsonProperty("gender") + private String gender; + + @JsonProperty("districtName") + private String districtName; + + @JsonProperty("villageName") + private String villageName; + + // Add other fields from BeneficiariesDTO that you need to search + + // Constructors + public BeneficiaryDocument() {} + + // Getters and Setters + public String getBenId() { return benId; } + public void setBenId(String benId) { this.benId = benId; } + + public Long getBenRegId() { return benRegId; } + public void setBenRegId(Long benRegId) { this.benRegId = benRegId; } + + public String getPhoneNum() { return phoneNum; } + public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } + + public String getFirstName() { return firstName; } + public void setFirstName(String firstName) { this.firstName = firstName; } + + public String getLastName() { return lastName; } + public void setLastName(String lastName) { this.lastName = lastName; } + + public Integer getAge() { return age; } + public void setAge(Integer age) { this.age = age; } + + public String getGender() { return gender; } + public void setGender(String gender) { this.gender = gender; } + + public String getDistrictName() { return districtName; } + public void setDistrictName(String districtName) { this.districtName = districtName; } + + public String getVillageName() { return villageName; } + public void setVillageName(String villageName) { this.villageName = villageName; } + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/data/elasticsearch/ElasticsearchSyncJob.java b/src/main/java/com/iemr/common/identity/data/elasticsearch/ElasticsearchSyncJob.java new file mode 100644 index 00000000..11a854bc --- /dev/null +++ b/src/main/java/com/iemr/common/identity/data/elasticsearch/ElasticsearchSyncJob.java @@ -0,0 +1,95 @@ +package com.iemr.common.identity.data.elasticsearch; + +import java.sql.Timestamp; +import java.math.BigInteger; +import jakarta.persistence.*; +import lombok.Data; + +/** + * Entity to track Elasticsearch sync job status + */ +@Entity +@Table(name = "t_elasticsearch_sync_job") +@Data +public class ElasticsearchSyncJob { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "job_id") + private Long jobId; + + @Column(name = "job_type", length = 50, nullable = false) + private String jobType; // FULL_SYNC, INCREMENTAL_SYNC, SINGLE_BENEFICIARY + + @Column(name = "status", length = 50, nullable = false) + private String status; // PENDING, RUNNING, COMPLETED, FAILED, CANCELLED + + @Column(name = "total_records") + private Long totalRecords; + + @Column(name = "processed_records") + private Long processedRecords; + + @Column(name = "success_count") + private Long successCount; + + @Column(name = "failure_count") + private Long failureCount; + + @Column(name = "current_offset") + private Integer currentOffset; + + @Column(name = "started_at") + private Timestamp startedAt; + + @Column(name = "completed_at") + private Timestamp completedAt; + + @Column(name = "error_message", columnDefinition = "TEXT") + private String errorMessage; + + @Column(name = "triggered_by", length = 100) + private String triggeredBy; // User who triggered the job + + @Column(name = "created_date", nullable = false, updatable = false) + private Timestamp createdDate; + + @Column(name = "last_updated") + private Timestamp lastUpdated; + + @Column(name = "estimated_time_remaining") + private Long estimatedTimeRemaining; // in seconds + + @Column(name = "processing_speed") + private Double processingSpeed; // records per second + + @PrePersist + protected void onCreate() { + createdDate = new Timestamp(System.currentTimeMillis()); + lastUpdated = createdDate; + } + + @PreUpdate + protected void onUpdate() { + lastUpdated = new Timestamp(System.currentTimeMillis()); + } + + /** + * Calculate progress percentage + */ + @Transient + public double getProgressPercentage() { + if (totalRecords == null || totalRecords == 0) { + return 0.0; + } + return (processedRecords * 100.0) / totalRecords; + } + + /** + * Check if job is active (running) + */ + @Transient + public boolean isActive() { + return "RUNNING".equals(status) || "PENDING".equals(status); + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/domain/User.java b/src/main/java/com/iemr/common/identity/domain/User.java index cf0f5053..f35a5607 100644 --- a/src/main/java/com/iemr/common/identity/domain/User.java +++ b/src/main/java/com/iemr/common/identity/domain/User.java @@ -15,7 +15,7 @@ import jakarta.persistence.Table; import lombok.Data; @Entity -@Table(name = "m_User") +@Table(schema = "db_iemr", name = "m_user") @JsonIgnoreProperties(ignoreUnknown = true) @Data public class User { diff --git a/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java b/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java index e3080a5d..2846bbba 100644 --- a/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java @@ -142,4 +142,43 @@ Long getBeneficiaryCountsByVillageIDAndLastModifyDate(@Param("villageIDs") List< @Query("SELECT a FROM MBeneficiarymapping a WHERE a.vanSerialNo =:vanSerialNo AND a.vanID =:vanID ") MBeneficiarymapping getWithVanSerialNoVanID(@Param("vanSerialNo") BigInteger vanSerialNo, @Param("vanID") Integer vanID); + + @Query("SELECT b.benRegId, b.benMapId FROM MBeneficiarymapping b where benRegId is not null and deleted = false") + List getAllBeneficiaryIds(); + + /** + * Get beneficiary IDs in batches for efficient processing + * @param offset Starting position + * @param limit Number of records to fetch + */ + @Query(value = "SELECT benRegId, benMapId FROM MBeneficiarymapping " + + "WHERE deleted = false ORDER BY benRegId LIMIT :limit OFFSET :offset") + List getBeneficiaryIdsBatch(@Param("offset") int offset, @Param("limit") int limit); + + /** + * Count total non-deleted beneficiaries + */ + @Query(value = "SELECT COUNT(*) FROM MBeneficiarymapping WHERE deleted = false") + long countActiveBeneficiaries(); + + + @Query("SELECT m FROM MBeneficiarymapping m " + + "LEFT JOIN FETCH m.mBeneficiarydetail " + + "LEFT JOIN FETCH m.mBeneficiarycontact " + + "LEFT JOIN FETCH m.mBeneficiaryaddress " + + "WHERE m.benRegId = :benRegId AND m.deleted = false") + MBeneficiarymapping findByBenRegIdWithDetails(@Param("benRegId") BigInteger benRegId); + + /** + * Simple find by benRegId + */ + @Query("SELECT m FROM MBeneficiarymapping m WHERE m.benRegId = :benRegId AND m.deleted = false") + MBeneficiarymapping findByBenRegId(@Param("benRegId") BigInteger benRegId); + + /** + * Check if beneficiary exists by benRegId + */ + @Query(value = "SELECT COUNT(*) > 0 FROM MBeneficiarymapping WHERE benRegId = :benRegId AND deleted = false") + boolean existsByBenRegId(@Param("benRegId") BigInteger benRegId); + } diff --git a/src/main/java/com/iemr/common/identity/repo/elasticsearch/SyncJobRepo.java b/src/main/java/com/iemr/common/identity/repo/elasticsearch/SyncJobRepo.java new file mode 100644 index 00000000..79bc0432 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/repo/elasticsearch/SyncJobRepo.java @@ -0,0 +1,43 @@ +package com.iemr.common.identity.repo.elasticsearch; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; + +@Repository +public interface SyncJobRepo extends JpaRepository { + + /** + * Find all active (running or pending) jobs + */ + @Query("SELECT j FROM ElasticsearchSyncJob j WHERE j.status IN ('RUNNING', 'PENDING') ORDER BY j.createdDate DESC") + List findActiveJobs(); + + /** + * Check if there's any active full sync job + */ + @Query("SELECT COUNT(j) > 0 FROM ElasticsearchSyncJob j WHERE j.jobType = 'FULL_SYNC' AND j.status IN ('RUNNING', 'PENDING')") + boolean hasActiveFullSyncJob(); + + /** + * Find latest job of a specific type + */ + @Query("SELECT j FROM ElasticsearchSyncJob j WHERE j.jobType = :jobType ORDER BY j.createdDate DESC") + List findLatestJobsByType(String jobType); + + /** + * Find recent jobs (last 10) + */ + @Query("SELECT j FROM ElasticsearchSyncJob j ORDER BY j.createdDate DESC") + List findRecentJobs(); + + /** + * Find job by ID + */ + Optional findByJobId(Long jobId); +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/IdentityService.java b/src/main/java/com/iemr/common/identity/service/IdentityService.java index a0b8761f..9796bf17 100644 --- a/src/main/java/com/iemr/common/identity/service/IdentityService.java +++ b/src/main/java/com/iemr/common/identity/service/IdentityService.java @@ -90,13 +90,17 @@ import com.iemr.common.identity.repo.MBeneficiaryImageRepo; import com.iemr.common.identity.repo.V_BenAdvanceSearchRepo; import com.iemr.common.identity.repo.rmnch.RMNCHBeneficiaryDetailsRmnchRepo; +import com.iemr.common.identity.service.elasticsearch.ElasticsearchService; import com.iemr.common.identity.utils.mapper.OutputMapper; import com.iemr.common.identity.utils.response.OutputResponse; +import org.springframework.beans.factory.annotation.Value; + +import com.iemr.common.identity.service.elasticsearch.RealtimeElasticsearchSyncService; + import jakarta.persistence.NoResultException; import jakarta.persistence.QueryTimeoutException; - @Service public class IdentityService { private static final Logger logger = LoggerFactory.getLogger(IdentityService.class); @@ -114,6 +118,12 @@ private JdbcTemplate getJdbcTemplate() { @Autowired private RMNCHBeneficiaryDetailsRmnchRepo rMNCHBeneficiaryDetailsRmnchRepo; + @Autowired + private ElasticsearchService elasticsearchService; + + @Autowired + private RealtimeElasticsearchSyncService realtimeSyncService; + @Autowired IdentityMapper mapper; @Autowired @@ -151,6 +161,9 @@ private JdbcTemplate getJdbcTemplate() { @Autowired private V_BenAdvanceSearchRepo v_BenAdvanceSearchRepo; + @Value("${elasticsearch.enabled:false}") + private boolean esEnabled; + public void getBenAdress() { logger.debug("Address count: " + addressRepo.count()); logger.debug( @@ -321,6 +334,21 @@ public List getBeneficiariesByBenId(BigInteger benId) logger.info("IdentityService.getBeneficiariesByBenId - start, beneficiaryID : " + benId); List list = new ArrayList(); + // Try Elasticsearch first if enabled + if (esEnabled) { + try { + list = elasticsearchService.searchByBenId(benId); + if (!list.isEmpty()) { + logger.info("Found " + list.size() + " results from Elasticsearch for BenId: " + benId); + return list; + } + logger.info("No results from Elasticsearch, falling back to database"); + } catch (Exception e) { + logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); + } + } + + MBeneficiaryregidmapping regId = regIdRepo.findByBeneficiaryID(benId); if (regId != null && regId.getBenRegId() != null) { List benMapObjArr = mappingRepo.getBenMappingByRegID(regId.getBenRegId()); @@ -382,6 +410,21 @@ public List getBeneficiariesByPhoneNum(String phoneNum) throws NoResultException, QueryTimeoutException { // new logic, 27-09-2018 List list = new ArrayList<>(); + + // Try Elasticsearch first if enabled + if (esEnabled) { + try { + list = elasticsearchService.searchByPhoneNum(phoneNum); + if (!list.isEmpty()) { + logger.info("Found " + list.size() + " results from Elasticsearch for PhoneNum: " + phoneNum); + return list; + } + logger.info("No results from Elasticsearch, falling back to database"); + } catch (Exception e) { + logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); + } + } + try { List benContact = contactRepo.findByAnyPhoneNum(phoneNum); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java new file mode 100644 index 00000000..2edb935d --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java @@ -0,0 +1,334 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.BulkRequest; +import co.elastic.clients.elasticsearch.core.BulkResponse; +import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem; + +import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; +import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.repo.elasticsearch.SyncJobRepo; +import org.springframework.beans.factory.annotation.Value; + +/** + * Async service for Elasticsearch sync operations + * Runs sync jobs in background without blocking API calls + */ +@Service +public class AsyncElasticsearchSyncService { + + private static final Logger logger = LoggerFactory.getLogger(AsyncElasticsearchSyncService.class); + private static final int BATCH_SIZE = 100; + private static final int ES_BULK_SIZE = 50; + private static final int STATUS_UPDATE_FREQUENCY = 10; // Update status every 10 batches + + @Autowired + private ElasticsearchClient esClient; + + @Autowired + private TransactionalSyncWrapper transactionalWrapper; + + @Autowired + private BeneficiaryDataService beneficiaryDataService; + + @Autowired + private OptimizedBeneficiaryDataService optimizedDataService; + + @Autowired + private SyncJobRepo syncJobRepository; + + @Value("${elasticsearch.index.beneficiary}") + private String beneficiaryIndex; + + /** + * Start async full sync job + * Runs in background thread from elasticsearchSyncExecutor pool + */ + @Async("elasticsearchSyncExecutor") + public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { + logger.info("========================================"); + logger.info("Starting ASYNC full sync job: jobId={}", jobId); + logger.info("========================================"); + + ElasticsearchSyncJob job = syncJobRepository.findByJobId(jobId) + .orElseThrow(() -> new RuntimeException("Job not found: " + jobId)); + + try { + // Update job status to RUNNING + job.setStatus("RUNNING"); + job.setStartedAt(new Timestamp(System.currentTimeMillis())); + syncJobRepository.save(job); + + // Get total count + long totalCount = transactionalWrapper.countActiveBeneficiaries(); + job.setTotalRecords(totalCount); + syncJobRepository.save(job); + + logger.info("Total beneficiaries to sync: {}", totalCount); + + if (totalCount == 0) { + job.setStatus("COMPLETED"); + job.setCompletedAt(new Timestamp(System.currentTimeMillis())); + job.setErrorMessage("No beneficiaries found to sync"); + syncJobRepository.save(job); + return; + } + + int offset = job.getCurrentOffset() != null ? job.getCurrentOffset() : 0; + long processedCount = job.getProcessedRecords() != null ? job.getProcessedRecords() : 0; + long successCount = job.getSuccessCount() != null ? job.getSuccessCount() : 0; + long failureCount = job.getFailureCount() != null ? job.getFailureCount() : 0; + + List esBatch = new ArrayList<>(); + int batchCounter = 0; + long startTime = System.currentTimeMillis(); + + // Process in batches + while (offset < totalCount) { + try { + List batchIds = transactionalWrapper.getBeneficiaryIdsBatch(offset, BATCH_SIZE); + + if (batchIds == null || batchIds.isEmpty()) { + break; + } + + // Extract benRegIds for batch fetch + List benRegIds = batchIds.stream() + .map(arr -> (BigInteger) arr[0]) + .collect(java.util.stream.Collectors.toList()); + + // Fetch ALL beneficiaries in this batch in ONE query + List benDTOs = optimizedDataService.getBeneficiariesBatch(benRegIds); + + logger.debug("Fetched {} beneficiaries in single batch query", benDTOs.size()); + + // Process each beneficiary + for (BeneficiariesDTO benDTO : benDTOs) { + try { + if (benDTO != null) { + BeneficiaryDocument doc = convertToDocument(benDTO); + if (doc != null && doc.getBenId() != null) { + esBatch.add(doc); + + if (esBatch.size() >= ES_BULK_SIZE) { + int indexed = bulkIndexDocuments(esBatch); + successCount += indexed; + failureCount += (esBatch.size() - indexed); + processedCount += esBatch.size(); + esBatch.clear(); + } + } else { + failureCount++; + processedCount++; + } + } else { + failureCount++; + processedCount++; + } + } catch (Exception e) { + logger.error("Error processing beneficiary: {}", e.getMessage()); + failureCount++; + processedCount++; + } + } + + // Account for any beneficiaries that weren't returned + int notFetched = batchIds.size() - benDTOs.size(); + if (notFetched > 0) { + failureCount += notFetched; + processedCount += notFetched; + logger.warn("{} beneficiaries not fetched from database", notFetched); + } + + offset += BATCH_SIZE; + batchCounter++; + + // Update job status periodically + if (batchCounter % STATUS_UPDATE_FREQUENCY == 0) { + updateJobProgress(job, processedCount, successCount, failureCount, + offset, totalCount, startTime); + } + + // Small pause every 10 batches (not every 5) + if (batchCounter % 10 == 0) { + Thread.sleep(500); // Reduced to 500ms + } + + } catch (Exception e) { + logger.error("Error processing batch at offset {}: {}", offset, e.getMessage()); + + // Save current progress before potentially failing + job.setCurrentOffset(offset); + job.setProcessedRecords(processedCount); + job.setSuccessCount(successCount); + job.setFailureCount(failureCount); + syncJobRepository.save(job); + + // Wait before retrying + Thread.sleep(2000); + } + } + + // Index remaining documents + if (!esBatch.isEmpty()) { + int indexed = bulkIndexDocuments(esBatch); + successCount += indexed; + failureCount += (esBatch.size() - indexed); + processedCount += esBatch.size(); + } + + // Mark job as completed + job.setStatus("COMPLETED"); + job.setCompletedAt(new Timestamp(System.currentTimeMillis())); + job.setProcessedRecords(processedCount); + job.setSuccessCount(successCount); + job.setFailureCount(failureCount); + job.setCurrentOffset((int) totalCount); + + long duration = System.currentTimeMillis() - startTime; + job.setProcessingSpeed(processedCount / (duration / 1000.0)); + + syncJobRepository.save(job); + + logger.info("========================================"); + logger.info("Async sync job COMPLETED: jobId={}", jobId); + logger.info("Processed: {}, Success: {}, Failed: {}", processedCount, successCount, failureCount); + logger.info("========================================"); + + } catch (Exception e) { + logger.error("========================================"); + logger.error("CRITICAL ERROR in async sync job: jobId={}, error={}", jobId, e.getMessage(), e); + logger.error("========================================"); + + job.setStatus("FAILED"); + job.setCompletedAt(new Timestamp(System.currentTimeMillis())); + job.setErrorMessage(e.getMessage()); + syncJobRepository.save(job); + } + } + + /** + * Update job progress with estimated time remaining + */ + private void updateJobProgress(ElasticsearchSyncJob job, long processed, long success, + long failure, int offset, long total, long startTime) { + job.setProcessedRecords(processed); + job.setSuccessCount(success); + job.setFailureCount(failure); + job.setCurrentOffset(offset); + + long elapsedTime = System.currentTimeMillis() - startTime; + double speed = processed / (elapsedTime / 1000.0); + job.setProcessingSpeed(speed); + + if (speed > 0) { + long remaining = total - processed; + long estimatedSeconds = (long) (remaining / speed); + job.setEstimatedTimeRemaining(estimatedSeconds); + } + + syncJobRepository.save(job); + + logger.info("Progress: {}/{} ({:.2f}%) - Speed: {:.2f} rec/sec - ETA: {} sec", + processed, total, (processed * 100.0) / total, speed, job.getEstimatedTimeRemaining()); + } + + /** + * Bulk index documents + */ + private int bulkIndexDocuments(List documents) { + if (documents == null || documents.isEmpty()) { + return 0; + } + + try { + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (BeneficiaryDocument doc : documents) { + if (doc.getBenId() != null) { + br.operations(op -> op + .index(idx -> idx + .index(beneficiaryIndex) + .id(doc.getBenId()) + .document(doc) + ) + ); + } + } + + BulkResponse result = esClient.bulk(br.build()); + int successCount = 0; + + if (result.errors()) { + for (BulkResponseItem item : result.items()) { + if (item.error() == null) { + successCount++; + } + } + } else { + successCount = documents.size(); + } + + return successCount; + + } catch (Exception e) { + logger.error("Error in bulk indexing: {}", e.getMessage()); + return 0; + } + } + + /** + * Convert DTO to Document + */ + private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { + if (dto == null) { + return null; + } + + try { + BeneficiaryDocument doc = new BeneficiaryDocument(); + + if (dto.getBenRegId() != null) { + BigInteger benRegId = (BigInteger) dto.getBenRegId(); + doc.setBenId(benRegId.toString()); + doc.setBenRegId(benRegId.longValue()); + } else if (dto.getBenId() != null) { + doc.setBenId(dto.getBenId().toString()); + if (dto.getBenId() instanceof BigInteger) { + doc.setBenRegId(((BigInteger) dto.getBenId()).longValue()); + } + } else { + return null; + } + + doc.setPhoneNum(dto.getPreferredPhoneNum()); + + if (dto.getBeneficiaryDetails() != null) { + BenDetailDTO benDetails = dto.getBeneficiaryDetails(); + doc.setFirstName(benDetails.getFirstName()); + doc.setLastName(benDetails.getLastName()); + doc.setAge(benDetails.getBeneficiaryAge()); + doc.setGender(benDetails.getGender()); + } + + return doc; + + } catch (Exception e) { + logger.error("Error converting DTO: {}", e.getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java new file mode 100644 index 00000000..2e9ca773 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java @@ -0,0 +1,149 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.iemr.common.identity.domain.MBeneficiarymapping; +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.repo.BenMappingRepo; + +/** + * Service to fetch beneficiary data directly from database + * Used for Elasticsearch sync to avoid circular dependencies + */ +@Service +public class BeneficiaryDataService { + + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryDataService.class); + + @Autowired + private BenMappingRepo mappingRepo; + + /** + * Fetch beneficiary data directly from database by benRegId + * This bypasses any Elasticsearch caching to get fresh database data + * + * @param benRegId The beneficiary registration ID + * @return BeneficiariesDTO or null if not found + */ + public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { + int maxRetries = 3; + int retryCount = 0; + + while (retryCount < maxRetries) { + try { + logger.debug("Fetching beneficiary from database: benRegId={}, attempt={}", benRegId, retryCount + 1); + + // Fetch with details + MBeneficiarymapping mapping = mappingRepo.findByBenRegIdWithDetails(benRegId); + + if (mapping == null) { + logger.warn("Beneficiary mapping not found: benRegId={}", benRegId); + return null; + } + + // Convert to DTO + BeneficiariesDTO dto = convertToDTO(mapping); + + logger.debug("Successfully fetched beneficiary: benRegId={}", benRegId); + return dto; + + } catch (org.springframework.orm.jpa.JpaSystemException e) { + retryCount++; + logger.warn("Database connection error for benRegId={}, attempt {}/{}: {}", + benRegId, retryCount, maxRetries, e.getMessage()); + + if (retryCount >= maxRetries) { + logger.error("Max retries reached for benRegId={}", benRegId); + return null; + } + + // Wait before retry + try { + Thread.sleep(1000 * retryCount); // Exponential backoff + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return null; + } + + } catch (Exception e) { + logger.error("Error fetching beneficiary from database: benRegId={}, error={}", + benRegId, e.getMessage(), e); + return null; + } + } + + return null; + } + + /** + * Convert MBeneficiarymapping entity to BeneficiariesDTO + */ + private BeneficiariesDTO convertToDTO(MBeneficiarymapping mapping) { + BeneficiariesDTO dto = new BeneficiariesDTO(); + + try { + // Basic IDs + dto.setBenRegId(mapping.getBenRegId()); + dto.setBenMapId(mapping.getBenMapId()); + + // Use benRegId as benId if benId is not available + if (mapping.getBenRegId() != null) { + dto.setBenId(mapping.getBenRegId()); + } + + // Phone number from contact + if (mapping.getMBeneficiarycontact() != null) { + dto.setPreferredPhoneNum(mapping.getMBeneficiarycontact().getPreferredPhoneNum()); + } + + // Beneficiary details + if (mapping.getMBeneficiarydetail() != null) { + BenDetailDTO detailDTO = new BenDetailDTO(); + + detailDTO.setFirstName(mapping.getMBeneficiarydetail().getFirstName()); + detailDTO.setLastName(mapping.getMBeneficiarydetail().getLastName()); + // detailDTO.setGender(mapping.getMBeneficiarydetail().getGenderName()); + + // Calculate age if DOB is available + if (mapping.getMBeneficiarydetail().getDob() != null) { + detailDTO.setBeneficiaryAge(calculateAge(mapping.getMBeneficiarydetail().getDob())); + } + + dto.setBeneficiaryDetails(detailDTO); + } + + logger.debug("Successfully converted mapping to DTO: benRegId={}", mapping.getBenRegId()); + + } catch (Exception e) { + logger.error("Error converting mapping to DTO: {}", e.getMessage(), e); + } + + return dto; + } + + /** + * Calculate age from date of birth + */ + private Integer calculateAge(java.sql.Timestamp dob) { + try { + if (dob == null) { + return null; + } + + java.time.LocalDate birthDate = dob.toLocalDateTime().toLocalDate(); + java.time.LocalDate now = java.time.LocalDate.now(); + + return java.time.Period.between(birthDate, now).getYears(); + + } catch (Exception e) { + logger.error("Error calculating age: {}", e.getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java new file mode 100644 index 00000000..036214be --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java @@ -0,0 +1,158 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; + +/** + * Optimized service to fetch beneficiary data in bulk + * Uses native SQL with joins for maximum performance + */ +@Service +public class OptimizedBeneficiaryDataService { + + private static final Logger logger = LoggerFactory.getLogger(OptimizedBeneficiaryDataService.class); + + @PersistenceContext + private EntityManager entityManager; + + /** + * Fetch multiple beneficiaries in one query using native SQL + * This is MUCH faster than fetching one by one + */ + @Transactional(readOnly = true, timeout = 30) + public List getBeneficiariesBatch(List benRegIds) { + if (benRegIds == null || benRegIds.isEmpty()) { + return new ArrayList<>(); + } + + try { + // CORRECTED: Use exact column names from your tables + String sql = + "SELECT " + + " m.BenRegId, " + + " m.BenMapId, " + + " d.FirstName, " + + " d.LastName, " + + " d.Gender, " + // Changed from Gender to GenderName + " d.DOB, " + + " c.PreferredPhoneNum " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarydetails d ON m.BenDetailsId = d.BeneficiaryDetailsId " + + "LEFT JOIN i_beneficiarycontacts c ON m.BenContactsId = c.BenContactsID " + + "WHERE m.BenRegId IN :benRegIds " + + " AND m.deleted = false"; + + Query query = entityManager.createNativeQuery(sql); + query.setParameter("benRegIds", benRegIds); + + @SuppressWarnings("unchecked") + List results = query.getResultList(); + + logger.info("Fetched {} beneficiary records from database", results.size()); + + List beneficiaries = new ArrayList<>(); + + for (Object[] row : results) { + try { + BeneficiariesDTO dto = new BeneficiariesDTO(); + + // BenRegId (column 0) - Handle both Long and BigInteger + if (row[0] != null) { + BigInteger benRegId = convertToBigInteger(row[0]); + dto.setBenRegId(benRegId); + dto.setBenId(benRegId); // Use benRegId as benId + } + + // BenMapId (column 1) - Handle both Long and BigInteger + if (row[1] != null) { + dto.setBenMapId(convertToBigInteger(row[1])); + } + + // Create BenDetailDTO + BenDetailDTO detailDTO = new BenDetailDTO(); + detailDTO.setFirstName(row[2] != null ? row[2].toString() : null); + detailDTO.setLastName(row[3] != null ? row[3].toString() : null); + detailDTO.setGender(row[4] != null ? row[4].toString() : null); + + // Calculate age from DOB (column 5) + if (row[5] != null) { + try { + java.sql.Timestamp dob = (java.sql.Timestamp) row[5]; + java.time.LocalDate birthDate = dob.toLocalDateTime().toLocalDate(); + java.time.LocalDate now = java.time.LocalDate.now(); + int age = java.time.Period.between(birthDate, now).getYears(); + detailDTO.setBeneficiaryAge(age); + } catch (Exception e) { + logger.debug("Error calculating age: {}", e.getMessage()); + } + } + + dto.setBeneficiaryDetails(detailDTO); + + // Phone number (column 6) + dto.setPreferredPhoneNum(row[6] != null ? row[6].toString() : null); + + beneficiaries.add(dto); + + } catch (Exception e) { + logger.error("Error parsing row: {}", e.getMessage(), e); + } + } + + logger.debug("Converted {} beneficiaries to DTOs", beneficiaries.size()); + return beneficiaries; + + } catch (Exception e) { + logger.error("Error fetching beneficiaries batch: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Single beneficiary fetch (for backward compatibility) + */ + @Transactional(readOnly = true, timeout = 10) + public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { + List ids = new ArrayList<>(); + ids.add(benRegId); + + List results = getBeneficiariesBatch(ids); + return results.isEmpty() ? null : results.get(0); + } + + /** + * Helper method to convert Object to BigInteger + * Handles both Long and BigInteger types from database + */ + private BigInteger convertToBigInteger(Object value) { + if (value == null) { + return null; + } + + if (value instanceof BigInteger) { + return (BigInteger) value; + } else if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } else if (value instanceof Integer) { + return BigInteger.valueOf((Integer) value); + } else if (value instanceof String) { + return new BigInteger((String) value); + } else { + logger.warn("Unexpected type for BigInteger conversion: {}", value.getClass().getName()); + return BigInteger.valueOf(((Number) value).longValue()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java new file mode 100644 index 00000000..06e4d283 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java @@ -0,0 +1,145 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; +import com.iemr.common.identity.repo.elasticsearch.SyncJobRepo; + +/** + * Service to manage Elasticsearch sync jobs + */ +@Service +public class SyncJobService { + + private static final Logger logger = LoggerFactory.getLogger(SyncJobService.class); + + @Autowired + private SyncJobRepo syncJobRepository; + + @Autowired + private AsyncElasticsearchSyncService asyncSyncService; + + /** + * Start a new full sync job + * Returns immediately with job ID + */ + public ElasticsearchSyncJob startFullSyncJob(String triggeredBy) { + // Check if there's already an active full sync job + if (syncJobRepository.hasActiveFullSyncJob()) { + throw new RuntimeException("A full sync job is already running. Please wait for it to complete."); + } + + // Create new job + ElasticsearchSyncJob job = new ElasticsearchSyncJob(); + job.setJobType("FULL_SYNC"); + job.setStatus("PENDING"); + job.setTriggeredBy(triggeredBy); + job.setProcessedRecords(0L); + job.setSuccessCount(0L); + job.setFailureCount(0L); + job.setCurrentOffset(0); + + // Save job to database + job = syncJobRepository.save(job); + + logger.info("Created new full sync job: jobId={}, triggeredBy={}", job.getJobId(), triggeredBy); + + // Start async processing + asyncSyncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); + + return job; + } + + /** + * Resume a failed job from where it left off + */ + public ElasticsearchSyncJob resumeJob(Long jobId, String triggeredBy) { + ElasticsearchSyncJob job = syncJobRepository.findByJobId(jobId) + .orElseThrow(() -> new RuntimeException("Job not found: " + jobId)); + + if (!"FAILED".equals(job.getStatus())) { + throw new RuntimeException("Can only resume FAILED jobs. Current status: " + job.getStatus()); + } + + logger.info("Resuming job: jobId={}, from offset={}", jobId, job.getCurrentOffset()); + + job.setStatus("PENDING"); + job.setTriggeredBy(triggeredBy); + job = syncJobRepository.save(job); + + // Restart async processing from last offset + asyncSyncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); + + return job; + } + + /** + * Cancel a running job + */ + public boolean cancelJob(Long jobId) { + Optional jobOpt = syncJobRepository.findByJobId(jobId); + + if (jobOpt.isEmpty()) { + return false; + } + + ElasticsearchSyncJob job = jobOpt.get(); + + if (!job.isActive()) { + logger.warn("Cannot cancel job that is not active: jobId={}, status={}", jobId, job.getStatus()); + return false; + } + + // Mark as cancelled (the async thread will check this periodically) + job.setStatus("CANCELLED"); + job.setCompletedAt(new Timestamp(System.currentTimeMillis())); + syncJobRepository.save(job); + + logger.info("Job cancelled: jobId={}", jobId); + return true; + } + + /** + * Get job status by ID + */ + public ElasticsearchSyncJob getJobStatus(Long jobId) { + return syncJobRepository.findByJobId(jobId) + .orElseThrow(() -> new RuntimeException("Job not found: " + jobId)); + } + + /** + * Get all active jobs + */ + public List getActiveJobs() { + return syncJobRepository.findActiveJobs(); + } + + /** + * Get recent jobs (last 10) + */ + public List getRecentJobs() { + return syncJobRepository.findRecentJobs(); + } + + /** + * Check if any full sync is currently running + */ + public boolean isFullSyncRunning() { + return syncJobRepository.hasActiveFullSyncJob(); + } + + /** + * Get latest job of specific type + */ + public ElasticsearchSyncJob getLatestJobByType(String jobType) { + List jobs = syncJobRepository.findLatestJobsByType(jobType); + return jobs.isEmpty() ? null : jobs.get(0); + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java new file mode 100644 index 00000000..dbcf0f3d --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java @@ -0,0 +1,67 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import com.iemr.common.identity.repo.BenMappingRepo; + +/** + * Wrapper service to handle database operations with proper transaction management + * This prevents connection timeout issues during long-running sync operations + */ +@Service +public class TransactionalSyncWrapper { + + private static final Logger logger = LoggerFactory.getLogger(TransactionalSyncWrapper.class); + + @Autowired + private BenMappingRepo mappingRepo; + + /** + * Get beneficiary IDs in a new transaction + * This ensures connection is fresh for each batch + */ + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true, timeout = 30) + public List getBeneficiaryIdsBatch(int offset, int limit) { + try { + return mappingRepo.getBeneficiaryIdsBatch(offset, limit); + } catch (Exception e) { + logger.error("Error fetching batch: offset={}, limit={}, error={}", + offset, limit, e.getMessage()); + throw e; + } + } + + /** + * Count beneficiaries in a new transaction + */ + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true, timeout = 30) + public long countActiveBeneficiaries() { + try { + return mappingRepo.countActiveBeneficiaries(); + } catch (Exception e) { + logger.error("Error counting beneficiaries: {}", e.getMessage()); + throw e; + } + } + + /** + * Check if beneficiary exists in a new transaction + */ + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true, timeout = 10) + public boolean existsByBenRegId(BigInteger benRegId) { + try { + return mappingRepo.existsByBenRegId(benRegId); + } catch (Exception e) { + logger.error("Error checking existence for benRegId={}: {}", benRegId, e.getMessage()); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/utils/JwtAuthenticationUtil.java b/src/main/java/com/iemr/common/identity/utils/JwtAuthenticationUtil.java index 49e48265..af2539ff 100644 --- a/src/main/java/com/iemr/common/identity/utils/JwtAuthenticationUtil.java +++ b/src/main/java/com/iemr/common/identity/utils/JwtAuthenticationUtil.java @@ -109,7 +109,7 @@ private User getUserFromCache(String userId) { private User fetchUserFromDB(String userId) { String redisKey = "user_" + userId; // Redis key format List users = jdbcTemplate.query( - "SELECT * FROM m_user WHERE UserID = ? AND Deleted = false", + "SELECT * FROM db_iemr.m_user WHERE UserID = ? AND Deleted = false", new BeanPropertyRowMapper<>(User.class), userId); if (users.isEmpty()) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1780d8b6..8d9af903 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,8 +9,47 @@ spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.Ph # The SQL dialect makes Hibernate generate better SQL for the chosen database spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect -#Below lines are added for security reasons +spring.datasource.hikari.leak-detection-threshold=60000 + +# MySQL specific settings to prevent connection timeout +spring.datasource.hikari.data-source-properties.cachePrepStmts=true +spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250 +spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048 +spring.datasource.hikari.data-source-properties.useServerPrepStmts=true +spring.datasource.hikari.data-source-properties.autoReconnect=true +spring.datasource.hikari.data-source-properties.tcpKeepAlive=true +spring.datasource.hikari.test-on-borrow=true + +# Reduce batch size for faster feedback +spring.jpa.properties.hibernate.jdbc.batch_size=50 +spring.jpa.properties.hibernate.order_inserts=true +spring.jpa.properties.hibernate.order_updates=true + +# Query timeout +spring.jpa.properties.hibernate.query.timeout=30000 +# Connection pool - CRITICAL +spring.datasource.hikari.maximum-pool-size=20 +spring.datasource.hikari.minimum-idle=10 +spring.datasource.hikari.connection-timeout=30000 +spring.datasource.hikari.idle-timeout=300000 +spring.datasource.hikari.max-lifetime=600000 +spring.datasource.hikari.keepalive-time=120000 +spring.datasource.hikari.connection-test-query=SELECT 1 + + +# OLD Tomcat settings - REMOVE THESE (they conflict with HikariCP) +# spring.datasource.tomcat.initial-size=5 +# spring.datasource.tomcat.max-idle=15 +# spring.datasource.tomcat.max-active=30 +# spring.datasource.tomcat.min-idle=5 +# spring.datasource.tomcat.min-evictable-idle-time-millis=15000 +# spring.datasource.tomcat.remove-abandoned=true +# spring.datasource.tomcat.logAbandoned=true +# spring.datasource.continue-on-error=true +# spring.datasource.tomcat.remove-abandoned-timeout=600 + +#Below lines are added for security reasons spring.session.store-type=redis spring.redis.password= spring.redis.port=6379 @@ -26,16 +65,8 @@ logging.level.org.springframework.web=INFO logging.level.org.hibernate=INFO logging.level.com.iemr=DEBUG logging.level.org.springframework=INFO +logging.level.com.zaxxer.hikari=DEBUG -spring.datasource.tomcat.initial-size=5 -spring.datasource.tomcat.max-idle=15 -spring.datasource.tomcat.max-active=30 -spring.datasource.tomcat.min-idle=5 -spring.datasource.tomcat.min-evictable-idle-time-millis=15000 -spring.datasource.tomcat.remove-abandoned=true -spring.datasource.tomcat.logAbandoned=true -spring.datasource.continue-on-error=true -spring.datasource.tomcat.remove-abandoned-timeout=600 door-to-door-page-size=2 #Get-HRP-Status @@ -44,12 +75,26 @@ get-HRP-Status=ANC/getHRPStatus #Get Beneficiary ABHA getHealthID=healthID/getBenhealthID - -## commit miss error in ci - - spring.main.allow-bean-definition-overriding=true spring.main.allow-circular-references=true -#springfox.documentation.swagger-ui.enabled=true jwt.access.expiration=86400000 jwt.refresh.expiration=604800000 + +# Elasticsearch Configuration +elasticsearch.host=localhost +elasticsearch.port=9200 +elasticsearch.username=elastic +elasticsearch.password=piramalES +elasticsearch.index.beneficiary=beneficiary_index + +# Enable/Disable ES (for gradual rollout) +elasticsearch.enabled=true + +# Connection pool settings +elasticsearch.connection.timeout=5000 +elasticsearch.socket.timeout=60000 + +# Logging +logging.level.com.iemr.common.identity.service.elasticsearch=INFO +logging.level.org.elasticsearch=WARN + From 47aab374c56598768bf95e3b6edfeddcc10e8fbc Mon Sep 17 00:00:00 2001 From: Vanitha Date: Wed, 10 Dec 2025 12:15:37 +0530 Subject: [PATCH 02/13] fix: add service for ES Search --- .../controller/IdentityController.java | 2 +- .../controller/IdentityESController.java | 448 ++++++++++++++++++ .../identity/service/IdentityService.java | 242 +++++++++- .../elasticsearch/ElasticsearchService.java | 315 ++++++++++++ .../RealtimeElasticsearchSyncService.java | 129 +++++ src/main/resources/application.properties | 20 +- 6 files changed, 1151 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/iemr/common/identity/controller/IdentityESController.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityController.java b/src/main/java/com/iemr/common/identity/controller/IdentityController.java index 4ac603ae..18854dd3 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityController.java @@ -96,7 +96,7 @@ public String getBeneficiaries( JsonElement json = JsonParser.parseString(searchFilter); IdentitySearchDTO searchParams = InputMapper.getInstance().gson().fromJson(json, IdentitySearchDTO.class); - List list = svc.getBeneficiaries(searchParams); + List list = svc.getBeneficiarieswithES(searchParams); list.removeIf(Objects::isNull); Collections.sort(list); response = getSuccessResponseString(list, 200, "success", "getBeneficiariesByAdvanceSearch"); diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java new file mode 100644 index 00000000..08305875 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java @@ -0,0 +1,448 @@ +package com.iemr.common.identity.controller; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.dto.IdentitySearchDTO; +import com.iemr.common.identity.service.IdentityService; +import com.iemr.common.identity.mapper.InputMapper; + + +/** + * Enhanced Beneficiary Search Controller with Elasticsearch support + * Provides fast search across multiple fields + */ +@RestController +@RequestMapping("/beneficiary") +public class IdentityESController { + + private static final Logger logger = LoggerFactory.getLogger(IdentityESController.class); + + @Autowired + private IdentityService identityService; + + /** + * Search beneficiary by BeneficiaryID + * Uses Elasticsearch if enabled, falls back to MySQL + * + * Usage: GET /beneficiary/search/benId/{beneficiaryId} + * Example: GET /beneficiary/search/benId/123456 + */ + @GetMapping("/search/benId/{beneficiaryId}") + public ResponseEntity> searchByBenId(@PathVariable String beneficiaryId) { + logger.info("Search request received: beneficiaryId={}", beneficiaryId); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + BigInteger benId = new BigInteger(beneficiaryId); + List results = identityService.getBeneficiariesByBenId(benId); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (NumberFormatException e) { + response.put("status", "error"); + response.put("message", "Invalid beneficiary ID format"); + return ResponseEntity.badRequest().body(response); + + } catch (Exception e) { + logger.error("Error searching by beneficiaryId: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by BeneficiaryRegID + * + * Usage: GET /beneficiary/search/benRegId/{benRegId} + * Example: GET /beneficiary/search/benRegId/987654 + */ + @GetMapping("/search/benRegId/{benRegId}") + public ResponseEntity> searchByBenRegId(@PathVariable String benRegId) { + logger.info("Search request received: benRegId={}", benRegId); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + BigInteger benRegIdBig = new BigInteger(benRegId); + List results = identityService.getBeneficiariesByBenRegId(benRegIdBig); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (NumberFormatException e) { + response.put("status", "error"); + response.put("message", "Invalid beneficiary registration ID format"); + return ResponseEntity.badRequest().body(response); + + } catch (Exception e) { + logger.error("Error searching by benRegId: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by Phone Number + * + * Usage: GET /beneficiary/search/phone/{phoneNumber} + * Example: GET /beneficiary/search/phone/9876543210 + */ + @GetMapping("/search/phone/{phoneNumber}") + public ResponseEntity> searchByPhoneNumber(@PathVariable String phoneNumber) { + logger.info("Search request received: phoneNumber={}", phoneNumber); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + List results = identityService.getBeneficiariesByPhoneNum(phoneNumber); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by phone: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by ABHA Address / HealthID + * + * Usage: GET /beneficiary/search/healthId/{healthId} + * Example: GET /beneficiary/search/healthId/rajesh@abdm + */ + @GetMapping("/search/healthId/{healthId}") + public ResponseEntity> searchByHealthId(@PathVariable String healthId) { + logger.info("Search request received: healthId={}", healthId); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + List results = identityService.getBeneficiaryByHealthIDAbhaAddress(healthId); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by healthId: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by ABHA ID Number / HealthIDNo + * + * Usage: GET /beneficiary/search/healthIdNo/{healthIdNo} + * Example: GET /beneficiary/search/healthIdNo/12345678901234 + */ + @GetMapping("/search/healthIdNo/{healthIdNo}") + public ResponseEntity> searchByHealthIdNo(@PathVariable String healthIdNo) { + logger.info("Search request received: healthIdNo={}", healthIdNo); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + List results = identityService.getBeneficiaryByHealthIDNoAbhaIdNo(healthIdNo); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by healthIdNo: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by Family ID + * + * Usage: GET /beneficiary/search/familyId/{familyId} + * Example: GET /beneficiary/search/familyId/FAM12345 + */ + @GetMapping("/search/familyId/{familyId}") + public ResponseEntity> searchByFamilyId(@PathVariable String familyId) { + logger.info("Search request received: familyId={}", familyId); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + List results = identityService.searhBeneficiaryByFamilyId(familyId); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by familyId: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by Government Identity (Aadhaar, etc.) + * + * Usage: GET /beneficiary/search/govIdentity/{identityNo} + * Example: GET /beneficiary/search/govIdentity/123456789012 + */ + @GetMapping("/search/govIdentity/{identityNo}") + public ResponseEntity> searchByGovIdentity(@PathVariable String identityNo) { + logger.info("Search request received: govIdentity={}", identityNo); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + List results = identityService.searhBeneficiaryByGovIdentity(identityNo); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by govIdentity: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Advanced search with multiple criteria (with Elasticsearch support) + * Supports fuzzy name matching via Elasticsearch + * + * Usage: POST /beneficiary/search/advanced + * Body: IdentitySearchDTO JSON + */ + @PostMapping("/search/advanced") + public ResponseEntity> advancedSearch(@RequestBody String searchRequest) { + logger.info("Advanced search request received"); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + IdentitySearchDTO searchDTO = InputMapper.getInstance().gson() + .fromJson(searchRequest, IdentitySearchDTO.class); + + // Use the Elasticsearch-enabled method + List results = identityService.getBeneficiarieswithES(searchDTO); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + response.put("elasticsearchEnabled", true); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error in advanced search: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Search beneficiary by Name (supports fuzzy matching via Elasticsearch) + * + * Usage: GET /beneficiary/search/name?firstName=Rajesh&lastName=Kumar + */ + @GetMapping("/search/name") + public ResponseEntity> searchByName( + @RequestParam(required = false) String firstName, + @RequestParam(required = false) String lastName) { + + logger.info("Name search request: firstName={}, lastName={}", firstName, lastName); + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + if ((firstName == null || firstName.trim().isEmpty()) && + (lastName == null || lastName.trim().isEmpty())) { + response.put("status", "error"); + response.put("message", "At least one of firstName or lastName is required"); + return ResponseEntity.badRequest().body(response); + } + + // Create search DTO + IdentitySearchDTO searchDTO = new IdentitySearchDTO(); + searchDTO.setFirstName(firstName); + searchDTO.setLastName(lastName); + + // Use Elasticsearch-enabled search + List results = identityService.getBeneficiarieswithES(searchDTO); + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("count", results.size()); + response.put("data", results); + response.put("searchTime", timeTaken + "ms"); + response.put("fuzzySearchEnabled", true); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error searching by name: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Multi-field search endpoint + * Searches across multiple fields simultaneously + * + * Usage: GET /beneficiary/search/multi?query=rajesh&searchFields=name,phone + */ + @GetMapping("/search/multi") + public ResponseEntity> multiFieldSearch( + @RequestParam String query, + @RequestParam(required = false, defaultValue = "name,phone,benId") String searchFields) { + + logger.info("Multi-field search: query={}, fields={}", query, searchFields); + + Map response = new HashMap<>(); + Map> resultsByField = new HashMap<>(); + long startTime = System.currentTimeMillis(); + + try { + String[] fields = searchFields.split(","); + int totalResults = 0; + + for (String field : fields) { + field = field.trim().toLowerCase(); + List fieldResults = null; + + try { + switch (field) { + case "name": + IdentitySearchDTO nameSearch = new IdentitySearchDTO(); + nameSearch.setFirstName(query); + fieldResults = identityService.getBeneficiarieswithES(nameSearch); + break; + + case "phone": + fieldResults = identityService.getBeneficiariesByPhoneNum(query); + break; + + case "benid": + try { + BigInteger benId = new BigInteger(query); + fieldResults = identityService.getBeneficiariesByBenId(benId); + } catch (NumberFormatException e) { + // Skip if not a valid number + } + break; + + case "benregid": + try { + BigInteger benRegId = new BigInteger(query); + fieldResults = identityService.getBeneficiariesByBenRegId(benRegId); + } catch (NumberFormatException e) { + // Skip if not a valid number + } + break; + } + + if (fieldResults != null && !fieldResults.isEmpty()) { + resultsByField.put(field, fieldResults); + totalResults += fieldResults.size(); + } + + } catch (Exception e) { + logger.warn("Error searching field {}: {}", field, e.getMessage()); + } + } + + long timeTaken = System.currentTimeMillis() - startTime; + + response.put("status", "success"); + response.put("totalResults", totalResults); + response.put("resultsByField", resultsByField); + response.put("searchTime", timeTaken + "ms"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error in multi-field search: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/IdentityService.java b/src/main/java/com/iemr/common/identity/service/IdentityService.java index 9796bf17..8a154c7d 100644 --- a/src/main/java/com/iemr/common/identity/service/IdentityService.java +++ b/src/main/java/com/iemr/common/identity/service/IdentityService.java @@ -321,6 +321,230 @@ public List getBeneficiaries(IdentitySearchDTO searchDTO) return list; } + + + public List getBeneficiarieswithES(IdentitySearchDTO searchDTO) + throws NoResultException, QueryTimeoutException, Exception { + List list = new ArrayList(); + + /** + * Try Elasticsearch first if enabled + */ + if (esEnabled) { + try { + logger.info("Attempting Elasticsearch search"); + + // Search by beneficiary Id + if (searchDTO.getBeneficiaryId() != null) { + logger.info("Elasticsearch: searching by beneficiaryId: {}", searchDTO.getBeneficiaryId()); + list = elasticsearchService.searchByBenId(searchDTO.getBeneficiaryId()); + if (!list.isEmpty()) { + logger.info("Found {} results from Elasticsearch", list.size()); + return enrichBeneficiariesFromDatabase(list); + } + } + + // Search by beneficiary Reg Id + if (searchDTO.getBeneficiaryRegId() != null) { + logger.info("Elasticsearch: searching by beneficiaryRegId: {}", searchDTO.getBeneficiaryRegId()); + list = elasticsearchService.searchByBenRegId(searchDTO.getBeneficiaryRegId()); + if (!list.isEmpty()) { + logger.info("Found {} results from Elasticsearch", list.size()); + return enrichBeneficiariesFromDatabase(list); + } + } + + // Search by contact number + if (searchDTO.getContactNumber() != null) { + logger.info("Elasticsearch: searching by phoneNum: {}", searchDTO.getContactNumber()); + list = elasticsearchService.searchByPhoneNum(searchDTO.getContactNumber()); + if (!list.isEmpty()) { + logger.info("Found {} results from Elasticsearch", list.size()); + + // Apply D2D filters if needed + if (searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { + list = applyD2DFilters(list, searchDTO); + } + + return enrichBeneficiariesFromDatabase(list); + } + } + + // Advanced search (multiple criteria) + if (hasMultipleCriteria(searchDTO)) { + logger.info("Elasticsearch: advanced search"); + list = elasticsearchService.advancedSearch( + searchDTO.getFirstName(), + searchDTO.getLastName(), + searchDTO.getContactNumber(), + null, // gender from searchDTO if available + null // age from searchDTO if available + ); + if (!list.isEmpty()) { + logger.info("Found {} results from Elasticsearch", list.size()); + return enrichBeneficiariesFromDatabase(list); + } + } + + logger.info("No results from Elasticsearch, falling back to database"); + + } catch (Exception e) { + logger.error("Elasticsearch search failed, falling back to database: {}", e.getMessage()); + } + } + + /** + * Fallback to MySQL database search + */ + logger.info("Searching in MySQL database"); + + // if beneficiary Id present + if (searchDTO.getBeneficiaryId() != null) { + logger.info("getting beneficiaries by ID for " + searchDTO.getBeneficiaryId()); + return this.getBeneficiariesByBenId(searchDTO.getBeneficiaryId()); + } + + // if beneficiary Reg Id present + if (searchDTO.getBeneficiaryRegId() != null) { + logger.info("getting beneficiaries by reg ID for " + searchDTO.getBeneficiaryRegId()); + return this.getBeneficiariesByBenRegId(searchDTO.getBeneficiaryRegId()); + } + + // if beneficiary Reg Id present + if (searchDTO.getContactNumber() != null) { + logger.info("getting beneficiaries by contact no for " + searchDTO.getContactNumber()); + List list3 = this.getBeneficiariesByPhoneNum(searchDTO.getContactNumber()); + if (!list3.isEmpty() && searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { + list3 = applyD2DFilters(list3, searchDTO); + } + return list3; + } + + // Advanced search from database + List tmpList = mappingRepo.dynamicFilterSearchNew(searchDTO); + for (VBenAdvanceSearch obj : tmpList) { + list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew1(obj))); + logger.debug("benMapId: " + obj.getBenMapID()); + } + + return list; +} + +/** + * Check if search has multiple criteria (for advanced search) + */ +private boolean hasMultipleCriteria(IdentitySearchDTO searchDTO) { + int criteriaCount = 0; + + if (searchDTO.getFirstName() != null && !searchDTO.getFirstName().trim().isEmpty()) criteriaCount++; + if (searchDTO.getLastName() != null && !searchDTO.getLastName().trim().isEmpty()) criteriaCount++; + if (searchDTO.getContactNumber() != null && !searchDTO.getContactNumber().trim().isEmpty()) criteriaCount++; + + return criteriaCount >= 2; +} + +/** + * Enrich Elasticsearch results with complete data from database + * ES returns only basic fields, fetch complete data from DB + */ +private List enrichBeneficiariesFromDatabase(List esResults) { + List enrichedList = new ArrayList<>(); + + for (BeneficiariesDTO esResult : esResults) { + try { + if (esResult.getBenRegId() != null) { + // Fetch complete data from database + List fullData = this.getBeneficiariesByBenRegId(esResult.getBenRegId()); + if (!fullData.isEmpty()) { + enrichedList.add(fullData.get(0)); + } else { + // If not in DB anymore, return ES data + enrichedList.add(esResult); + } + } + } catch (Exception e) { + logger.error("Error enriching beneficiary data: {}", e.getMessage()); + // Return ES data if enrichment fails + enrichedList.add(esResult); + } + } + + return enrichedList; +} + +/** + * Apply D2D filters to search results + */ +private List applyD2DFilters(List list, IdentitySearchDTO searchDTO) { + List filtered = new ArrayList<>(); + + for (BeneficiariesDTO dto : list) { + boolean matches = true; + + if (searchDTO.getFirstName() != null) { + if (dto.getBeneficiaryDetails() == null || + dto.getBeneficiaryDetails().getFirstName() == null || + !dto.getBeneficiaryDetails().getFirstName().equalsIgnoreCase(searchDTO.getFirstName())) { + matches = false; + } + } + + if (matches && searchDTO.getLastName() != null) { + if (dto.getBeneficiaryDetails() == null || + dto.getBeneficiaryDetails().getLastName() == null || + !dto.getBeneficiaryDetails().getLastName().equalsIgnoreCase(searchDTO.getLastName())) { + matches = false; + } + } + + if (matches && searchDTO.getGenderId() != null) { + if (dto.getBeneficiaryDetails() == null || + dto.getBeneficiaryDetails().getGenderId() == null || + !dto.getBeneficiaryDetails().getGenderId().equals(searchDTO.getGenderId())) { + matches = false; + } + } + + if (matches && searchDTO.getHouseHoldID() != null) { + if (dto.getBeneficiaryDetails() == null || + dto.getBeneficiaryDetails().getHouseHoldID() == null || + !dto.getBeneficiaryDetails().getHouseHoldID().equals(searchDTO.getHouseHoldID())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getStateId() != null) { + if (dto.getCurrentAddress() == null || + dto.getCurrentAddress().getStateId() == null || + !dto.getCurrentAddress().getStateId().equals(searchDTO.getCurrentAddress().getStateId())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getDistrictId() != null) { + if (dto.getCurrentAddress() == null || + dto.getCurrentAddress().getDistrictId() == null || + !dto.getCurrentAddress().getDistrictId().equals(searchDTO.getCurrentAddress().getDistrictId())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getVillageId() != null) { + if (dto.getCurrentAddress() == null || + dto.getCurrentAddress().getVillageId() == null || + !dto.getCurrentAddress().getVillageId().equals(searchDTO.getCurrentAddress().getVillageId())) { + matches = false; + } + } + + if (matches) { + filtered.add(dto); + } + } + + return filtered; +} + /** * * Check which parameters available Get BenMapID based on the parameter/set of @@ -1021,6 +1245,11 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields imageRepo.save(beneficiaryImage); } +// Trigger async sync to Elasticsearch +if (identity.getBeneficiaryRegId() != null) { + logger.info("Triggering Elasticsearch sync for benRegId: {}", identity.getBeneficiaryRegId()); + realtimeSyncService.syncBeneficiaryAsync(identity.getBeneficiaryRegId()); +} logger.info("IdentityService.editIdentity - end. id = " + benMapping.getBenMapId()); } @@ -1342,8 +1571,19 @@ public BeneficiaryCreateResp createIdentity(IdentityDTO identity) { }); } + + // return partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); logger.info("IdentityService.createIdentity - end. id = " + benMapping.getBenMapId()); - return partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); + +BeneficiaryCreateResp response = partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); + +// Trigger async sync to Elasticsearch +if (regMap != null && regMap.getBenRegId() != null) { + logger.info("Triggering Elasticsearch sync for benRegId: {}", regMap.getBenRegId()); + realtimeSyncService.syncBeneficiaryAsync(regMap.getBenRegId()); +} + +return response; } private MBeneficiarydetail convertIdentityDTOToMBeneficiarydetail(IdentityDTO dto) { diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java new file mode 100644 index 00000000..8ff2df36 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -0,0 +1,315 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.SearchRequest; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.WildcardQuery; +import co.elastic.clients.elasticsearch._types.FieldValue; + +import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; + +/** + * Service for Elasticsearch search operations + * Provides fast search functionality for beneficiaries + */ +@Service +public class ElasticsearchService { + + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchService.class); + + @Autowired + private ElasticsearchClient esClient; + + @Value("${elasticsearch.index.beneficiary}") + private String beneficiaryIndex; + + @Value("${elasticsearch.enabled:false}") + private boolean esEnabled; + + /** + * Search beneficiary by BeneficiaryID (exact match) + */ + public List searchByBenId(BigInteger benId) { + if (!esEnabled || benId == null) { + return new ArrayList<>(); + } + + try { + logger.info("Searching Elasticsearch by benId: {}", benId); + + Query query = TermQuery.of(t -> t + .field("benId.keyword") + .value(FieldValue.of(benId.toString())) + )._toQuery(); + + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error searching by benId: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Search beneficiary by BeneficiaryRegID (exact match) + */ + public List searchByBenRegId(BigInteger benRegId) { + if (!esEnabled || benRegId == null) { + return new ArrayList<>(); + } + + try { + logger.info("Searching Elasticsearch by benRegId: {}", benRegId); + + Query query = TermQuery.of(t -> t + .field("benRegId") + .value(FieldValue.of(benRegId.longValue())) + )._toQuery(); + + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error searching by benRegId: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Search beneficiary by Phone Number (supports partial match) + */ + public List searchByPhoneNum(String phoneNum) { + if (!esEnabled || phoneNum == null || phoneNum.trim().isEmpty()) { + return new ArrayList<>(); + } + + try { + logger.info("Searching Elasticsearch by phoneNum: {}", phoneNum); + + // Clean phone number (remove +91, spaces, etc.) + String cleanedPhone = cleanPhoneNumber(phoneNum); + + Query query = WildcardQuery.of(w -> w + .field("phoneNum.keyword") + .value("*" + cleanedPhone + "*") + )._toQuery(); + + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error searching by phoneNum: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Search beneficiary by First Name (fuzzy match - handles typos) + */ + public List searchByFirstName(String firstName) { + if (!esEnabled || firstName == null || firstName.trim().isEmpty()) { + return new ArrayList<>(); + } + + try { + logger.info("Searching Elasticsearch by firstName: {}", firstName); + + Query query = MatchQuery.of(m -> m + .field("firstName") + .query(firstName) + .fuzziness("AUTO") // Tolerates 1-2 character differences + )._toQuery(); + + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error searching by firstName: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Search beneficiary by Last Name (fuzzy match) + */ + public List searchByLastName(String lastName) { + if (!esEnabled || lastName == null || lastName.trim().isEmpty()) { + return new ArrayList<>(); + } + + try { + logger.info("Searching Elasticsearch by lastName: {}", lastName); + + Query query = MatchQuery.of(m -> m + .field("lastName") + .query(lastName) + .fuzziness("AUTO") + )._toQuery(); + + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error searching by lastName: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Advanced search with multiple criteria (AND condition) + * Example: Search by firstName AND phoneNum + */ + public List advancedSearch(String firstName, String lastName, + String phoneNum, String gender, Integer age) { + if (!esEnabled) { + return new ArrayList<>(); + } + + try { + logger.info("Advanced search: firstName={}, lastName={}, phoneNum={}, gender={}, age={}", + firstName, lastName, phoneNum, gender, age); + + BoolQuery.Builder boolQuery = new BoolQuery.Builder(); + + // Add firstName filter (fuzzy) + if (firstName != null && !firstName.trim().isEmpty()) { + boolQuery.must(MatchQuery.of(m -> m + .field("firstName") + .query(firstName) + .fuzziness("AUTO") + )._toQuery()); + } + + // Add lastName filter (fuzzy) + if (lastName != null && !lastName.trim().isEmpty()) { + boolQuery.must(MatchQuery.of(m -> m + .field("lastName") + .query(lastName) + .fuzziness("AUTO") + )._toQuery()); + } + + // Add phoneNum filter (wildcard) + if (phoneNum != null && !phoneNum.trim().isEmpty()) { + String cleanedPhone = cleanPhoneNumber(phoneNum); + boolQuery.must(WildcardQuery.of(w -> w + .field("phoneNum.keyword") + .value("*" + cleanedPhone + "*") + )._toQuery()); + } + + // Add gender filter (exact) + if (gender != null && !gender.trim().isEmpty()) { + boolQuery.must(TermQuery.of(t -> t + .field("gender.keyword") + .value(FieldValue.of(gender)) + )._toQuery()); + } + + // Add age filter (exact) + if (age != null) { + boolQuery.must(TermQuery.of(t -> t + .field("age") + .value(FieldValue.of(age)) + )._toQuery()); + } + + Query query = boolQuery.build()._toQuery(); + return executeSearch(query); + + } catch (Exception e) { + logger.error("Error in advanced search: {}", e.getMessage(), e); + return new ArrayList<>(); + } + } + + /** + * Execute search query and convert results to DTOs + */ + private List executeSearch(Query query) { + try { + SearchRequest searchRequest = SearchRequest.of(s -> s + .index(beneficiaryIndex) + .query(query) + .size(100) // Limit to 100 results + ); + + SearchResponse response = esClient.search( + searchRequest, + BeneficiaryDocument.class + ); + + List results = new ArrayList<>(); + + for (Hit hit : response.hits().hits()) { + BeneficiaryDocument doc = hit.source(); + if (doc != null) { + results.add(convertToDTO(doc)); + } + } + + logger.info("Found {} results from Elasticsearch", results.size()); + return results; + + } catch (Exception e) { + logger.error("Error executing search: {}", e.getMessage(), e); + throw new RuntimeException("Elasticsearch search failed", e); + } + } + + /** + * Convert BeneficiaryDocument to BeneficiariesDTO + */ + private BeneficiariesDTO convertToDTO(BeneficiaryDocument doc) { + BeneficiariesDTO dto = new BeneficiariesDTO(); + + if (doc.getBenId() != null) { + dto.setBenId(new BigInteger(doc.getBenId())); + } + + if (doc.getBenRegId() != null) { + dto.setBenRegId(BigInteger.valueOf(doc.getBenRegId())); + } + + dto.setPreferredPhoneNum(doc.getPhoneNum()); + + BenDetailDTO detailDTO = new BenDetailDTO(); + detailDTO.setFirstName(doc.getFirstName()); + detailDTO.setLastName(doc.getLastName()); + detailDTO.setBeneficiaryAge(doc.getAge()); + detailDTO.setGender(doc.getGender()); + + dto.setBeneficiaryDetails(detailDTO); + + return dto; + } + + /** + * Clean phone number for searching + */ + private String cleanPhoneNumber(String phoneNumber) { + if (phoneNumber == null) { + return ""; + } + + // Remove +91, spaces, dashes + return phoneNumber.trim() + .replaceAll("\\+91", "") + .replaceAll("\\s+", "") + .replaceAll("-", ""); + } + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java new file mode 100644 index 00000000..5cf99bd5 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java @@ -0,0 +1,129 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.DeleteRequest; +import co.elastic.clients.elasticsearch.core.IndexRequest; + +import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; + +/** + * Service for real-time Elasticsearch synchronization + * Triggers automatically when beneficiaries are created/updated in database + */ +@Service +public class RealtimeElasticsearchSyncService { + + private static final Logger logger = LoggerFactory.getLogger(RealtimeElasticsearchSyncService.class); + + @Autowired + private ElasticsearchClient esClient; + + @Autowired + private OptimizedBeneficiaryDataService dataService; + + @Value("${elasticsearch.index.beneficiary}") + private String beneficiaryIndex; + + @Value("${elasticsearch.enabled:false}") + private boolean esEnabled; + + /** + * Async method to sync a single beneficiary to Elasticsearch + * Called after beneficiary is created/updated in database + */ + @Async("elasticsearchSyncExecutor") + public void syncBeneficiaryAsync(BigInteger benRegId) { + if (!esEnabled) { + logger.debug("Elasticsearch is disabled, skipping sync"); + return; + } + + try { + logger.info("Starting async sync for benRegId: {}", benRegId); + + // Fetch beneficiary from database + BeneficiariesDTO benDTO = dataService.getBeneficiaryFromDatabase(benRegId); + + if (benDTO == null) { + logger.warn("Beneficiary not found in database: {}", benRegId); + return; + } + + // Convert to Elasticsearch document + BeneficiaryDocument doc = convertToDocument(benDTO); + + if (doc == null || doc.getBenId() == null) { + logger.error("Failed to convert beneficiary to document: {}", benRegId); + return; + } + + // Index to Elasticsearch + IndexRequest request = IndexRequest.of(i -> i + .index(beneficiaryIndex) + .id(doc.getBenId()) + .document(doc) + ); + + esClient.index(request); + + logger.info("Successfully synced beneficiary to Elasticsearch: benRegId={}, benId={}", + benRegId, doc.getBenId()); + + } catch (Exception e) { + logger.error("Error syncing beneficiary {} to Elasticsearch: {}", benRegId, e.getMessage(), e); + } + } + + /** + * Convert DTO to Document + */ + private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { + if (dto == null) { + return null; + } + + try { + BeneficiaryDocument doc = new BeneficiaryDocument(); + + if (dto.getBenRegId() != null) { + BigInteger benRegId = (BigInteger) dto.getBenRegId(); + doc.setBenId(benRegId.toString()); + doc.setBenRegId(benRegId.longValue()); + } else if (dto.getBenId() != null) { + doc.setBenId(dto.getBenId().toString()); + if (dto.getBenId() instanceof BigInteger) { + doc.setBenRegId(((BigInteger) dto.getBenId()).longValue()); + } + } else { + return null; + } + + doc.setPhoneNum(dto.getPreferredPhoneNum()); + + if (dto.getBeneficiaryDetails() != null) { + BenDetailDTO benDetails = dto.getBeneficiaryDetails(); + doc.setFirstName(benDetails.getFirstName()); + doc.setLastName(benDetails.getLastName()); + doc.setAge(benDetails.getBeneficiaryAge()); + doc.setGender(benDetails.getGender()); + } + + return doc; + + } catch (Exception e) { + logger.error("Error converting DTO to document: {}", e.getMessage()); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8d9af903..5e723699 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,7 +18,7 @@ spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048 spring.datasource.hikari.data-source-properties.useServerPrepStmts=true spring.datasource.hikari.data-source-properties.autoReconnect=true spring.datasource.hikari.data-source-properties.tcpKeepAlive=true -spring.datasource.hikari.test-on-borrow=true +# spring.datasource.hikari.test-on-borrow=true # Reduce batch size for faster feedback spring.jpa.properties.hibernate.jdbc.batch_size=50 @@ -28,14 +28,28 @@ spring.jpa.properties.hibernate.order_updates=true # Query timeout spring.jpa.properties.hibernate.query.timeout=30000 -# Connection pool - CRITICAL spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=10 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=300000 spring.datasource.hikari.max-lifetime=600000 spring.datasource.hikari.keepalive-time=120000 -spring.datasource.hikari.connection-test-query=SELECT 1 +spring.datasource.hikari.validationTimeout=5000 + +# REMOVE CONNECTION TEST QUERY +# spring.datasource.hikari.connection-test-query=SELECT 1 + +# BETTER THAN autoReconnect + test-on-borrow +spring.datasource.hikari.health-check-properties.mysql5Validation=true + +spring.datasource.hikari.data-source-properties.cachePrepStmts=true +spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250 +spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048 +spring.datasource.hikari.data-source-properties.useServerPrepStmts=true +spring.datasource.hikari.data-source-properties.tcpKeepAlive=true + + +# spring.datasource.hikari.connection-test-query=SELECT 1 # OLD Tomcat settings - REMOVE THESE (they conflict with HikariCP) From 96e52ae10c6303edce8614e85c1503b16159510e Mon Sep 17 00:00:00 2001 From: Vanitha Date: Fri, 12 Dec 2025 21:32:05 +0530 Subject: [PATCH 03/13] fix: search implementation --- .../common/identity/ScheduledSyncJob.java | 76 ++ .../identity/config/ElasticsearchConfig.java | 48 ++ .../controller/IdentityESController.java | 655 +++++++---------- .../ElasticsearchSyncController.java | 347 +++++++++ .../elasticsearch/BeneficiaryDocument.java | 45 +- .../identity/dto/BeneficiariesESDTO.java | 140 ++++ .../identity/dto/IdentitySearchDTO.java | 1 + .../common/identity/repo/BenDetailRepo.java | 130 ++++ .../identity/service/IdentityService.java | 160 ++-- .../elasticsearch/ElasticsearchService.java | 691 +++++++++++------- .../ElasticsearchSyncService.java | 492 +++++++++++++ .../RealtimeElasticsearchSyncService.java | 66 +- src/main/resources/application.properties | 4 +- 13 files changed, 2070 insertions(+), 785 deletions(-) create mode 100644 src/main/java/com/iemr/common/identity/ScheduledSyncJob.java create mode 100644 src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java create mode 100644 src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java diff --git a/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java b/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java new file mode 100644 index 00000000..82120338 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java @@ -0,0 +1,76 @@ +package com.iemr.common.identity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; +import com.iemr.common.identity.service.elasticsearch.SyncJobService; + +/** + * Scheduled jobs for Elasticsearch sync + * + * To enable scheduled sync, set: + * elasticsearch.sync.scheduled.enabled=true in application.properties + */ +@Component +public class ScheduledSyncJob { + + private static final Logger logger = LoggerFactory.getLogger(ScheduledSyncJob.class); + + @Autowired + private SyncJobService syncJobService; + + @Value("${elasticsearch.sync.scheduled.enabled:true}") + private boolean scheduledSyncEnabled; + + /** + * Run full sync every day at 2 AM + * Cron: second, minute, hour, day, month, weekday + */ + @Scheduled(cron = "${elasticsearch.sync.scheduled.cron:0 0 2 * * ?}") + public void scheduledFullSync() { + if (!scheduledSyncEnabled) { + logger.debug("Scheduled sync is disabled"); + return; + } + + logger.info("========================================"); + logger.info("Starting scheduled full sync job"); + logger.info("========================================"); + + try { + // Check if there's already a sync running + if (syncJobService.isFullSyncRunning()) { + logger.warn("Full sync already running. Skipping scheduled sync."); + return; + } + + // Start async sync + ElasticsearchSyncJob job = syncJobService.startFullSyncJob("SCHEDULER"); + logger.info("Scheduled sync job started: jobId={}", job.getJobId()); + + } catch (Exception e) { + logger.error("Error starting scheduled sync: {}", e.getMessage(), e); + } + } + + /** + * Clean up old completed jobs (keep last 30 days) + * Runs every Sunday at 3 AM + */ + @Scheduled(cron = "0 0 3 * * SUN") + public void cleanupOldJobs() { + if (!scheduledSyncEnabled) { + return; + } + + logger.info("Running cleanup of old sync jobs..."); + + // TODO: Implement cleanup logic + // Delete jobs older than 30 days with status COMPLETED or FAILED + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java b/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java index 2d0a0505..a908b646 100644 --- a/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java +++ b/src/main/java/com/iemr/common/identity/config/ElasticsearchConfig.java @@ -1,9 +1,14 @@ package com.iemr.common.identity.config; +import java.io.IOException; + import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -16,6 +21,8 @@ @Configuration public class ElasticsearchConfig { + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchConfig.class); + @Value("${elasticsearch.host}") private String esHost; @@ -28,6 +35,9 @@ public class ElasticsearchConfig { @Value("${elasticsearch.password}") private String esPassword; + @Value("${elasticsearch.index.beneficiary}") + private String indexName; + @Bean public ElasticsearchClient elasticsearchClient() { BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); @@ -49,4 +59,42 @@ public ElasticsearchClient elasticsearchClient() { return new ElasticsearchClient(transport); } + + @Bean + public Boolean createIndexMapping(ElasticsearchClient client) throws IOException { + + // Check if index exists + boolean exists = client.indices().exists(e -> e.index(indexName)).value(); + + if (!exists) { + client.indices().create(c -> c + .index(indexName) + .mappings(m -> m + .properties("beneficiaryRegID", p -> p.keyword(k -> k)) + .properties("firstName", p -> p.text(t -> t + .fields("keyword", f -> f.keyword(k -> k)) + .analyzer("standard") + )) + .properties("lastName", p -> p.text(t -> t + .fields("keyword", f -> f.keyword(k -> k)) + .analyzer("standard") + )) + .properties("phoneNum", p -> p.keyword(k -> k)) + .properties("fatherName", p -> p.text(t -> t.analyzer("standard"))) + .properties("spouseName", p -> p.text(t -> t.analyzer("standard"))) + .properties("aadharNo", p -> p.keyword(k -> k)) + .properties("govtIdentityNo", p -> p.keyword(k -> k)) + ) + .settings(s -> s + .numberOfShards("3") + .numberOfReplicas("1") + .refreshInterval(t -> t.time("1s")) + ) + ); + + logger.info("Created Elasticsearch index with proper mappings"); + } + return true; + + } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java index 08305875..00d9fb37 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java @@ -2,9 +2,7 @@ import java.math.BigInteger; import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -14,13 +12,12 @@ import com.iemr.common.identity.dto.BeneficiariesDTO; import com.iemr.common.identity.dto.IdentitySearchDTO; -import com.iemr.common.identity.service.IdentityService; +import com.iemr.common.identity.service.elasticsearch.ElasticsearchService; import com.iemr.common.identity.mapper.InputMapper; - /** - * Enhanced Beneficiary Search Controller with Elasticsearch support - * Provides fast search across multiple fields + * Elasticsearch-enabled Beneficiary Search Controller + * All search endpoints with ES support */ @RestController @RequestMapping("/beneficiary") @@ -29,420 +26,326 @@ public class IdentityESController { private static final Logger logger = LoggerFactory.getLogger(IdentityESController.class); @Autowired - private IdentityService identityService; + private ElasticsearchService elasticsearchService; /** - * Search beneficiary by BeneficiaryID - * Uses Elasticsearch if enabled, falls back to MySQL + * MAIN UNIVERSAL SEARCH ENDPOINT + * Searches across all fields - name, phone, ID, etc. * - * Usage: GET /beneficiary/search/benId/{beneficiaryId} - * Example: GET /beneficiary/search/benId/123456 + * Usage: GET /beneficiary/search?q=vani + * Usage: GET /beneficiary/search?q=9876543210 + * Usage: GET /beneficiary/search?q=rajesh kumar */ - @GetMapping("/search/benId/{beneficiaryId}") - public ResponseEntity> searchByBenId(@PathVariable String beneficiaryId) { - logger.info("Search request received: beneficiaryId={}", beneficiaryId); - - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); - + @GetMapping("/search") + public ResponseEntity> search(@RequestParam String query) { try { - BigInteger benId = new BigInteger(beneficiaryId); - List results = identityService.getBeneficiariesByBenId(benId); + List> results = elasticsearchService.universalSearch(query); - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); + Map response = new HashMap<>(); response.put("data", results); - response.put("searchTime", timeTaken + "ms"); + response.put("statusCode", 200); + response.put("errorMessage", "Success"); + response.put("status", "Success"); return ResponseEntity.ok(response); - } catch (NumberFormatException e) { - response.put("status", "error"); - response.put("message", "Invalid beneficiary ID format"); - return ResponseEntity.badRequest().body(response); - } catch (Exception e) { - logger.error("Error searching by beneficiaryId: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + Map errorResponse = new HashMap<>(); + errorResponse.put("data", new ArrayList<>()); + errorResponse.put("statusCode", 500); + errorResponse.put("errorMessage", e.getMessage()); + errorResponse.put("status", "Error"); + + return ResponseEntity.status(500).body(errorResponse); } } - - /** - * Search beneficiary by BeneficiaryRegID - * - * Usage: GET /beneficiary/search/benRegId/{benRegId} - * Example: GET /beneficiary/search/benRegId/987654 - */ - @GetMapping("/search/benRegId/{benRegId}") - public ResponseEntity> searchByBenRegId(@PathVariable String benRegId) { - logger.info("Search request received: benRegId={}", benRegId); + + // @GetMapping("/search") + // public ResponseEntity> universalSearch(@RequestParam String q) { + // logger.info("Universal search request: query={}", q); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - BigInteger benRegIdBig = new BigInteger(benRegId); - List results = identityService.getBeneficiariesByBenRegId(benRegIdBig); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - - return ResponseEntity.ok(response); - - } catch (NumberFormatException e) { - response.put("status", "error"); - response.put("message", "Invalid beneficiary registration ID format"); - return ResponseEntity.badRequest().body(response); - - } catch (Exception e) { - logger.error("Error searching by benRegId: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // try { + // List results = elasticsearchService.universalSearch(q); + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + // response.put("query", q); + + // return ResponseEntity.ok(response); + + // } catch (Exception e) { + // logger.error("Universal search error: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } /** - * Search beneficiary by Phone Number + * Search by BeneficiaryID (exact match) * - * Usage: GET /beneficiary/search/phone/{phoneNumber} - * Example: GET /beneficiary/search/phone/9876543210 + * Usage: GET /beneficiary/search/benId/123456 */ - @GetMapping("/search/phone/{phoneNumber}") - public ResponseEntity> searchByPhoneNumber(@PathVariable String phoneNumber) { - logger.info("Search request received: phoneNumber={}", phoneNumber); + // @GetMapping("/search/benId/{beneficiaryId}") + // public ResponseEntity> searchByBenId(@PathVariable String beneficiaryId) { + // logger.info("Search by benId: {}", beneficiaryId); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - List results = identityService.getBeneficiariesByPhoneNum(phoneNumber); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - - return ResponseEntity.ok(response); - - } catch (Exception e) { - logger.error("Error searching by phone: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // try { + // BigInteger benId = new BigInteger(beneficiaryId); + // List results = elasticsearchService.searchByBenId(benId); + + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + + // return ResponseEntity.ok(response); + + // } catch (NumberFormatException e) { + // response.put("status", "error"); + // response.put("message", "Invalid beneficiary ID format"); + // return ResponseEntity.badRequest().body(response); + + // } catch (Exception e) { + // logger.error("Error searching by benId: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } - /** - * Search beneficiary by ABHA Address / HealthID - * - * Usage: GET /beneficiary/search/healthId/{healthId} - * Example: GET /beneficiary/search/healthId/rajesh@abdm - */ - @GetMapping("/search/healthId/{healthId}") - public ResponseEntity> searchByHealthId(@PathVariable String healthId) { - logger.info("Search request received: healthId={}", healthId); + // /** + // * Search by BeneficiaryRegID (exact match) + // * + // * Usage: GET /beneficiary/search/benRegId/987654 + // */ + // @GetMapping("/search/benRegId/{benRegId}") + // public ResponseEntity> searchByBenRegId(@PathVariable String benRegId) { + // logger.info("Search by benRegId: {}", benRegId); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - List results = identityService.getBeneficiaryByHealthIDAbhaAddress(healthId); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - - return ResponseEntity.ok(response); - - } catch (Exception e) { - logger.error("Error searching by healthId: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // try { + // BigInteger benRegIdBig = new BigInteger(benRegId); + // List results = elasticsearchService.searchByBenRegId(benRegIdBig); + + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + + // return ResponseEntity.ok(response); + + // } catch (NumberFormatException e) { + // response.put("status", "error"); + // response.put("message", "Invalid beneficiary registration ID format"); + // return ResponseEntity.badRequest().body(response); + + // } catch (Exception e) { + // logger.error("Error searching by benRegId: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } - /** - * Search beneficiary by ABHA ID Number / HealthIDNo - * - * Usage: GET /beneficiary/search/healthIdNo/{healthIdNo} - * Example: GET /beneficiary/search/healthIdNo/12345678901234 - */ - @GetMapping("/search/healthIdNo/{healthIdNo}") - public ResponseEntity> searchByHealthIdNo(@PathVariable String healthIdNo) { - logger.info("Search request received: healthIdNo={}", healthIdNo); + // /** + // * Search by Phone Number (supports partial match) + // * + // * Usage: GET /beneficiary/search/phone/9876543210 + // * Usage: GET /beneficiary/search/phone/987654 + // */ + // @GetMapping("/search/phone/{phoneNumber}") + // public ResponseEntity> searchByPhoneNumber(@PathVariable String phoneNumber) { + // logger.info("Search by phone: {}", phoneNumber); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - List results = identityService.getBeneficiaryByHealthIDNoAbhaIdNo(healthIdNo); + // try { + // List results = elasticsearchService.searchByPhoneNum(phoneNumber); - long timeTaken = System.currentTimeMillis() - startTime; + // long timeTaken = System.currentTimeMillis() - startTime; - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); - return ResponseEntity.ok(response); + // return ResponseEntity.ok(response); - } catch (Exception e) { - logger.error("Error searching by healthIdNo: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // } catch (Exception e) { + // logger.error("Error searching by phone: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } - /** - * Search beneficiary by Family ID - * - * Usage: GET /beneficiary/search/familyId/{familyId} - * Example: GET /beneficiary/search/familyId/FAM12345 - */ - @GetMapping("/search/familyId/{familyId}") - public ResponseEntity> searchByFamilyId(@PathVariable String familyId) { - logger.info("Search request received: familyId={}", familyId); + // /** + // * Search by First Name (fuzzy matching - handles typos) + // * + // * Usage: GET /beneficiary/search/firstName/vani + // * Usage: GET /beneficiary/search/firstName/rajesh + // */ + // @GetMapping("/search/firstName/{firstName}") + // public ResponseEntity> searchByFirstName(@PathVariable String firstName) { + // logger.info("Search by firstName: {}", firstName); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - List results = identityService.searhBeneficiaryByFamilyId(familyId); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - - return ResponseEntity.ok(response); - - } catch (Exception e) { - logger.error("Error searching by familyId: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // try { + // List results = elasticsearchService.searchByFirstName(firstName); + + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + // response.put("fuzzyMatchEnabled", true); + + // return ResponseEntity.ok(response); + + // } catch (Exception e) { + // logger.error("Error searching by firstName: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } - /** - * Search beneficiary by Government Identity (Aadhaar, etc.) - * - * Usage: GET /beneficiary/search/govIdentity/{identityNo} - * Example: GET /beneficiary/search/govIdentity/123456789012 - */ - @GetMapping("/search/govIdentity/{identityNo}") - public ResponseEntity> searchByGovIdentity(@PathVariable String identityNo) { - logger.info("Search request received: govIdentity={}", identityNo); + // /** + // * Search by Last Name (fuzzy matching) + // * + // * Usage: GET /beneficiary/search/lastName/kumar + // */ + // @GetMapping("/search/lastName/{lastName}") + // public ResponseEntity> searchByLastName(@PathVariable String lastName) { + // logger.info("Search by lastName: {}", lastName); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - try { - List results = identityService.searhBeneficiaryByGovIdentity(identityNo); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - - return ResponseEntity.ok(response); - - } catch (Exception e) { - logger.error("Error searching by govIdentity: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // try { + // List results = elasticsearchService.searchByLastName(lastName); + + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + // response.put("fuzzyMatchEnabled", true); + + // return ResponseEntity.ok(response); + + // } catch (Exception e) { + // logger.error("Error searching by lastName: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } - /** - * Advanced search with multiple criteria (with Elasticsearch support) - * Supports fuzzy name matching via Elasticsearch - * - * Usage: POST /beneficiary/search/advanced - * Body: IdentitySearchDTO JSON - */ - @PostMapping("/search/advanced") - public ResponseEntity> advancedSearch(@RequestBody String searchRequest) { - logger.info("Advanced search request received"); - - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // /** + // * Advanced multi-field search with query parameters + // * + // * Usage: GET /beneficiary/search/advanced?firstName=vani&phoneNum=9876 + // * Usage: GET /beneficiary/search/advanced?firstName=rajesh&lastName=kumar&gender=Male + // */ + // @GetMapping("/search/advanced") + // public ResponseEntity> advancedSearchGet( + // @RequestParam(required = false) String firstName, + // @RequestParam(required = false) String lastName, + // @RequestParam(required = false) String phoneNum, + // @RequestParam(required = false) String gender, + // @RequestParam(required = false) Integer age) { - try { - IdentitySearchDTO searchDTO = InputMapper.getInstance().gson() - .fromJson(searchRequest, IdentitySearchDTO.class); - - // Use the Elasticsearch-enabled method - List results = identityService.getBeneficiarieswithES(searchDTO); - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - response.put("elasticsearchEnabled", true); - - return ResponseEntity.ok(response); - - } catch (Exception e) { - logger.error("Error in advanced search: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } - - /** - * Search beneficiary by Name (supports fuzzy matching via Elasticsearch) - * - * Usage: GET /beneficiary/search/name?firstName=Rajesh&lastName=Kumar - */ - @GetMapping("/search/name") - public ResponseEntity> searchByName( - @RequestParam(required = false) String firstName, - @RequestParam(required = false) String lastName) { + // logger.info("Advanced search: firstName={}, lastName={}, phoneNum={}, gender={}, age={}", + // firstName, lastName, phoneNum, gender, age); - logger.info("Name search request: firstName={}, lastName={}", firstName, lastName); + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); - Map response = new HashMap<>(); - long startTime = System.currentTimeMillis(); + // try { + // List results = elasticsearchService.advancedSearch( + // firstName, lastName, phoneNum, gender, age + // ); + + // long timeTaken = System.currentTimeMillis() - startTime; + + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + // response.put("searchCriteria", Map.of( + // "firstName", firstName != null ? firstName : "N/A", + // "lastName", lastName != null ? lastName : "N/A", + // "phoneNum", phoneNum != null ? phoneNum : "N/A", + // "gender", gender != null ? gender : "N/A", + // "age", age != null ? age : "N/A" + // )); + + // return ResponseEntity.ok(response); + + // } catch (Exception e) { + // logger.error("Advanced search error: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } + + // /** + // * Advanced search with POST body (for complex queries) + // * + // * Usage: POST /beneficiary/search/advanced + // * Body: IdentitySearchDTO JSON + // */ + // @PostMapping("/search/advanced") + // public ResponseEntity> advancedSearchPost(@RequestBody String searchRequest) { + // logger.info("Advanced POST search request received"); - try { - if ((firstName == null || firstName.trim().isEmpty()) && - (lastName == null || lastName.trim().isEmpty())) { - response.put("status", "error"); - response.put("message", "At least one of firstName or lastName is required"); - return ResponseEntity.badRequest().body(response); - } - - // Create search DTO - IdentitySearchDTO searchDTO = new IdentitySearchDTO(); - searchDTO.setFirstName(firstName); - searchDTO.setLastName(lastName); - - // Use Elasticsearch-enabled search - List results = identityService.getBeneficiarieswithES(searchDTO); - - long timeTaken = System.currentTimeMillis() - startTime; + // Map response = new HashMap<>(); + // long startTime = System.currentTimeMillis(); + + // try { + // IdentitySearchDTO searchDTO = InputMapper.getInstance().gson() + // .fromJson(searchRequest, IdentitySearchDTO.class); - response.put("status", "success"); - response.put("count", results.size()); - response.put("data", results); - response.put("searchTime", timeTaken + "ms"); - response.put("fuzzySearchEnabled", true); + // List results = elasticsearchService.flexibleSearch(searchDTO); - return ResponseEntity.ok(response); + // long timeTaken = System.currentTimeMillis() - startTime; - } catch (Exception e) { - logger.error("Error searching by name: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } - - /** - * Multi-field search endpoint - * Searches across multiple fields simultaneously - * - * Usage: GET /beneficiary/search/multi?query=rajesh&searchFields=name,phone - */ - @GetMapping("/search/multi") - public ResponseEntity> multiFieldSearch( - @RequestParam String query, - @RequestParam(required = false, defaultValue = "name,phone,benId") String searchFields) { - - logger.info("Multi-field search: query={}, fields={}", query, searchFields); - - Map response = new HashMap<>(); - Map> resultsByField = new HashMap<>(); - long startTime = System.currentTimeMillis(); - - try { - String[] fields = searchFields.split(","); - int totalResults = 0; - - for (String field : fields) { - field = field.trim().toLowerCase(); - List fieldResults = null; - - try { - switch (field) { - case "name": - IdentitySearchDTO nameSearch = new IdentitySearchDTO(); - nameSearch.setFirstName(query); - fieldResults = identityService.getBeneficiarieswithES(nameSearch); - break; - - case "phone": - fieldResults = identityService.getBeneficiariesByPhoneNum(query); - break; - - case "benid": - try { - BigInteger benId = new BigInteger(query); - fieldResults = identityService.getBeneficiariesByBenId(benId); - } catch (NumberFormatException e) { - // Skip if not a valid number - } - break; - - case "benregid": - try { - BigInteger benRegId = new BigInteger(query); - fieldResults = identityService.getBeneficiariesByBenRegId(benRegId); - } catch (NumberFormatException e) { - // Skip if not a valid number - } - break; - } - - if (fieldResults != null && !fieldResults.isEmpty()) { - resultsByField.put(field, fieldResults); - totalResults += fieldResults.size(); - } - - } catch (Exception e) { - logger.warn("Error searching field {}: {}", field, e.getMessage()); - } - } - - long timeTaken = System.currentTimeMillis() - startTime; - - response.put("status", "success"); - response.put("totalResults", totalResults); - response.put("resultsByField", resultsByField); - response.put("searchTime", timeTaken + "ms"); + // response.put("status", "success"); + // response.put("count", results.size()); + // response.put("data", results); + // response.put("searchTime", timeTaken + "ms"); + // response.put("elasticsearchEnabled", true); - return ResponseEntity.ok(response); + // return ResponseEntity.ok(response); - } catch (Exception e) { - logger.error("Error in multi-field search: {}", e.getMessage(), e); - response.put("status", "error"); - response.put("message", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } + // } catch (Exception e) { + // logger.error("Advanced POST search error: {}", e.getMessage(), e); + // response.put("status", "error"); + // response.put("message", e.getMessage()); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + // } + // } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java new file mode 100644 index 00000000..9aa472f5 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java @@ -0,0 +1,347 @@ +package com.iemr.common.identity.controller.elasticsearch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; +import com.iemr.common.identity.repo.BenMappingRepo; +import com.iemr.common.identity.service.elasticsearch.ElasticsearchSyncService; +import com.iemr.common.identity.service.elasticsearch.SyncJobService; +import com.iemr.common.identity.service.elasticsearch.ElasticsearchSyncService.SyncStatus; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.iemr.common.identity.domain.MBeneficiarymapping; + +/** + * Controller to manage Elasticsearch synchronization operations + * Supports both synchronous and asynchronous sync jobs + */ +@RestController +@RequestMapping("/elasticsearch/sync") +public class ElasticsearchSyncController { + + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchSyncController.class); + + @Autowired + private ElasticsearchSyncService syncService; + + @Autowired + private SyncJobService syncJobService; + + @Autowired + private BenMappingRepo mappingRepo; + + /** + * Start async full sync (RECOMMENDED for millions of records) + * Returns immediately with job ID for tracking + * + * Usage: POST http://localhost:8080/elasticsearch/sync/async/start + */ + @PostMapping("/async/start") + public ResponseEntity> startAsyncFullSync( + @RequestParam(required = false, defaultValue = "API") String triggeredBy) { + + logger.info("Received request to start ASYNC full sync"); + + Map response = new HashMap<>(); + + try { + ElasticsearchSyncJob job = syncJobService.startFullSyncJob(triggeredBy); + + response.put("status", "success"); + response.put("message", "Sync job started in background"); + response.put("jobId", job.getJobId()); + response.put("jobStatus", job.getStatus()); + response.put("checkStatusUrl", "/elasticsearch/sync/async/status/" + job.getJobId()); + + return ResponseEntity.ok(response); + + } catch (RuntimeException e) { + logger.error("Error starting async sync: {}", e.getMessage()); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.CONFLICT).body(response); + + } catch (Exception e) { + logger.error("Unexpected error: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", "Unexpected error: " + e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Get job status by ID + * + * Usage: GET http://localhost:8080/elasticsearch/sync/async/status/1 + */ + @GetMapping("/async/status/{jobId}") + public ResponseEntity> getAsyncJobStatus(@PathVariable Long jobId) { + logger.info("Checking status for job: {}", jobId); + + try { + ElasticsearchSyncJob job = syncJobService.getJobStatus(jobId); + + Map response = new HashMap<>(); + response.put("jobId", job.getJobId()); + response.put("jobType", job.getJobType()); + response.put("status", job.getStatus()); + response.put("totalRecords", job.getTotalRecords()); + response.put("processedRecords", job.getProcessedRecords()); + response.put("successCount", job.getSuccessCount()); + response.put("failureCount", job.getFailureCount()); + response.put("progressPercentage", String.format("%.2f", job.getProgressPercentage())); + response.put("processingSpeed", job.getProcessingSpeed()); + response.put("estimatedTimeRemaining", job.getEstimatedTimeRemaining()); + response.put("startedAt", job.getStartedAt()); + response.put("completedAt", job.getCompletedAt()); + response.put("errorMessage", job.getErrorMessage()); + + return ResponseEntity.ok(response); + + } catch (RuntimeException e) { + Map response = new HashMap<>(); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + /** + * Get all active jobs + * + * Usage: GET http://localhost:8080/elasticsearch/sync/async/active + */ + @GetMapping("/async/active") + public ResponseEntity> getActiveJobs() { + logger.info("Fetching active jobs"); + return ResponseEntity.ok(syncJobService.getActiveJobs()); + } + + /** + * Get recent jobs + * + * Usage: GET http://localhost:8080/elasticsearch/sync/async/recent + */ + @GetMapping("/async/recent") + public ResponseEntity> getRecentJobs() { + logger.info("Fetching recent jobs"); + return ResponseEntity.ok(syncJobService.getRecentJobs()); + } + + /** + * Resume a failed job + * + * Usage: POST http://localhost:8080/elasticsearch/sync/async/resume/1 + */ + @PostMapping("/async/resume/{jobId}") + public ResponseEntity> resumeJob( + @PathVariable Long jobId, + @RequestParam(required = false, defaultValue = "API") String triggeredBy) { + + logger.info("Resuming job: {}", jobId); + + Map response = new HashMap<>(); + + try { + ElasticsearchSyncJob job = syncJobService.resumeJob(jobId, triggeredBy); + + response.put("status", "success"); + response.put("message", "Job resumed"); + response.put("jobId", job.getJobId()); + response.put("resumedFromOffset", job.getCurrentOffset()); + + return ResponseEntity.ok(response); + + } catch (RuntimeException e) { + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + + /** + * Cancel a running job + * + * Usage: POST http://localhost:8080/elasticsearch/sync/async/cancel/1 + */ + @PostMapping("/async/cancel/{jobId}") + public ResponseEntity> cancelJob(@PathVariable Long jobId) { + logger.info("Cancelling job: {}", jobId); + + Map response = new HashMap<>(); + boolean cancelled = syncJobService.cancelJob(jobId); + + if (cancelled) { + response.put("status", "success"); + response.put("message", "Job cancelled"); + return ResponseEntity.ok(response); + } else { + response.put("status", "error"); + response.put("message", "Could not cancel job. It may not be active."); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + + /** + * LEGACY: Synchronous full sync (NOT recommended for large datasets) + * Use /async/start instead + * + * Usage: POST http://localhost:8080/elasticsearch/sync/all + */ + @PostMapping("/all") + public ResponseEntity> syncAllBeneficiaries() { + logger.warn("LEGACY sync endpoint called. Consider using /async/start instead."); + logger.info("Received request to sync all beneficiaries (BLOCKING)"); + + Map response = new HashMap<>(); + + try { + ElasticsearchSyncService.SyncResult result = syncService.syncAllBeneficiaries(); + + response.put("status", "completed"); + response.put("successCount", result.getSuccessCount()); + response.put("failureCount", result.getFailureCount()); + response.put("error", result.getError()); + response.put("warning", "This is a blocking operation. For large datasets, use /async/start"); + + if (result.getError() != null) { + return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(response); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error in sync all endpoint: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Sync a single beneficiary by BenRegId + * + * Usage: POST http://localhost:8080/elasticsearch/sync/single/123456 + */ + @PostMapping("/single/{benRegId}") + public ResponseEntity> syncSingleBeneficiary( + @PathVariable String benRegId) { + logger.info("Received request to sync single beneficiary: {}", benRegId); + + Map response = new HashMap<>(); + + try { + boolean success = syncService.syncSingleBeneficiary(benRegId); + + response.put("status", success ? "success" : "failed"); + response.put("benRegId", benRegId); + response.put("synced", success); + + if (!success) { + response.put("message", "Beneficiary not found in database or sync failed. Check logs for details."); + } else { + response.put("message", "Beneficiary successfully synced to Elasticsearch"); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error syncing single beneficiary: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("benRegId", benRegId); + response.put("synced", false); + response.put("message", "Exception occurred: " + e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + /** + * Check sync status - compare DB count vs ES count + * + * Usage: GET http://localhost:8080/elasticsearch/sync/status + */ + @GetMapping("/status") + public ResponseEntity checkSyncStatus() { + logger.info("Received request to check sync status"); + + try { + SyncStatus status = syncService.checkSyncStatus(); + return ResponseEntity.ok(status); + + } catch (Exception e) { + logger.error("Error checking sync status: {}", e.getMessage(), e); + SyncStatus errorStatus = new SyncStatus(); + errorStatus.setError(e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorStatus); + } + } + + /** + * Health check endpoint + * + * Usage: GET http://localhost:8080/elasticsearch/sync/health + */ + @GetMapping("/health") + public ResponseEntity> healthCheck() { + Map response = new HashMap<>(); + response.put("status", "UP"); + response.put("service", "Elasticsearch Sync Service"); + response.put("asyncJobsRunning", syncJobService.isFullSyncRunning()); + response.put("activeJobs", syncJobService.getActiveJobs().size()); + return ResponseEntity.ok(response); + } + + /** + * Debug endpoint to check if a beneficiary exists in database + * + * Usage: GET http://localhost:8080/elasticsearch/sync/debug/check/123456 + */ + @GetMapping("/debug/check/{benRegId}") + public ResponseEntity> checkBeneficiaryExists( + @PathVariable String benRegId) { + logger.info("Checking if beneficiary exists: {}", benRegId); + + Map response = new HashMap<>(); + + try { + java.math.BigInteger benRegIdBig = new java.math.BigInteger(benRegId); + + boolean exists = mappingRepo.existsByBenRegId(benRegIdBig); + + response.put("benRegId", benRegId); + response.put("existsInDatabase", exists); + + if (exists) { + response.put("message", "Beneficiary found in database"); + + MBeneficiarymapping mapping = + mappingRepo.findByBenRegId(benRegIdBig); + + if (mapping != null) { + response.put("benMapId", mapping.getBenMapId()); + response.put("deleted", mapping.getDeleted()); + response.put("hasDetails", mapping.getMBeneficiarydetail() != null); + response.put("hasContact", mapping.getMBeneficiarycontact() != null); + } + } else { + response.put("message", "Beneficiary NOT found in database"); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error checking beneficiary: {}", e.getMessage(), e); + response.put("status", "error"); + response.put("message", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java index 1c53626e..25fcdb35 100644 --- a/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java +++ b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java @@ -1,9 +1,6 @@ package com.iemr.common.identity.data.elasticsearch; import com.fasterxml.jackson.annotation.JsonProperty; - -import java.math.BigInteger; - import lombok.Data; @Data @@ -24,6 +21,12 @@ public class BeneficiaryDocument { @JsonProperty("lastName") private String lastName; + @JsonProperty("fatherName") + private String fatherName; + + @JsonProperty("spouseName") + private String spouseName; + @JsonProperty("age") private Integer age; @@ -36,37 +39,9 @@ public class BeneficiaryDocument { @JsonProperty("villageName") private String villageName; - // Add other fields from BeneficiariesDTO that you need to search - - // Constructors - public BeneficiaryDocument() {} - - // Getters and Setters - public String getBenId() { return benId; } - public void setBenId(String benId) { this.benId = benId; } - - public Long getBenRegId() { return benRegId; } - public void setBenRegId(Long benRegId) { this.benRegId = benRegId; } + @JsonProperty("aadharNo") + private String aadharNo; - public String getPhoneNum() { return phoneNum; } - public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } - - public String getFirstName() { return firstName; } - public void setFirstName(String firstName) { this.firstName = firstName; } - - public String getLastName() { return lastName; } - public void setLastName(String lastName) { this.lastName = lastName; } - - public Integer getAge() { return age; } - public void setAge(Integer age) { this.age = age; } - - public String getGender() { return gender; } - public void setGender(String gender) { this.gender = gender; } - - public String getDistrictName() { return districtName; } - public void setDistrictName(String districtName) { this.districtName = districtName; } - - public String getVillageName() { return villageName; } - public void setVillageName(String villageName) { this.villageName = villageName; } - + @JsonProperty("govtIdentityNo") + private String govtIdentityNo; } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java b/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java new file mode 100644 index 00000000..a0e0da11 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java @@ -0,0 +1,140 @@ +package com.iemr.common.identity.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class BeneficiariesESDTO { + + // Basic fields + @JsonProperty("benRegId") + private Long benRegId; + + @JsonProperty("beneficiaryID") + private String beneficiaryID; + + @JsonProperty("firstName") + private String firstName; + + @JsonProperty("lastName") + private String lastName; + + @JsonProperty("genderID") + private Integer genderID; + + @JsonProperty("genderName") + private String genderName; + + @JsonProperty("dOB") + private Date dOB; + + @JsonProperty("age") + private Integer age; + + @JsonProperty("phoneNum") + private String phoneNum; + + @JsonProperty("aadharNo") + private String aadharNo; + + @JsonProperty("govtIdentityNo") + private String govtIdentityNo; + + @JsonProperty("fatherName") + private String fatherName; + + @JsonProperty("spouseName") + private String spouseName; + + @JsonProperty("createdBy") + private String createdBy; + + @JsonProperty("createdDate") + private Date createdDate; + + @JsonProperty("lastModDate") + private Long lastModDate; + + @JsonProperty("benAccountID") + private Long benAccountID; + + @JsonProperty("isHIVPos") + private String isHIVPos; + + // Demographics fields + @JsonProperty("stateID") + private Integer stateID; + + @JsonProperty("stateName") + private String stateName; + + @JsonProperty("stateCode") + private String stateCode; + + @JsonProperty("districtID") + private Integer districtID; + + @JsonProperty("districtName") + private String districtName; + + @JsonProperty("blockID") + private Integer blockID; + + @JsonProperty("blockName") + private String blockName; + + @JsonProperty("districtBranchID") + private Integer districtBranchID; + + @JsonProperty("districtBranchName") + private String districtBranchName; + + @JsonProperty("parkingPlaceID") + private Integer parkingPlaceID; + + @JsonProperty("servicePointID") + private Integer servicePointID; + + @JsonProperty("servicePointName") + private String servicePointName; + + @JsonProperty("pinCode") + private String pinCode; + + // Nested objects + @JsonProperty("phoneNumbers") + private List phoneNumbers; + + @Data + @NoArgsConstructor + @AllArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public static class PhoneNumberDTO { + @JsonProperty("benPhMapID") + private Long benPhMapID; + + @JsonProperty("phoneNo") + private String phoneNo; + + @JsonProperty("parentBenRegID") + private Long parentBenRegID; + + @JsonProperty("benRelationshipID") + private Integer benRelationshipID; + + @JsonProperty("benRelationshipType") + private String benRelationshipType; + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java b/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java index db4bf011..855f8dba 100644 --- a/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java +++ b/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java @@ -30,6 +30,7 @@ import lombok.Data; @Data +@lombok.Builder public class IdentitySearchDTO { private BigInteger beneficiaryId; diff --git a/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java b/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java index 3a8be24f..c7f0bbfa 100644 --- a/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java @@ -147,4 +147,134 @@ int untagFamily(@Param("modifiedBy") String modifiedBy, @Param("vanSerialNo") Bi @Query("SELECT b FROM MBeneficiarydetail b WHERE b.familyId =:familyid ") List searchByFamilyId(@Param("familyid") String familyid); + /** + * Find complete beneficiary data by IDs from Elasticsearch + */ + @Query(value = "SELECT " + + "m.BenRegId, " + // 0 + "d.BeneficiaryRegID, " + // 1 + "d.FirstName, " + // 2 + "d.LastName, " + // 3 + "d.GenderID, " + // 4 + "g.GenderName, " + // 5 + "d.DOB, " + // 6 + "TIMESTAMPDIFF(YEAR, d.DOB, CURDATE()) as Age, " + // 7 + "d.FatherName, " + // 8 + "d.SpouseName, " + // 9 + "d.IsHIVPositive, " + // 10 + "m.CreatedBy, " + // 11 + "m.CreatedDate, " + // 12 + "UNIX_TIMESTAMP(m.LastModDate) * 1000, " + // 13 + "m.BenAccountID, " + // 14 + "addr.CurrStateId, " + // 15 + "addr.CurrState, " + // 16 + "addr.CurrDistrictId, " + // 17 + "addr.CurrDistrict, " + // 18 + "addr.CurrSubDistrictId, " + // 19 + "addr.CurrSubDistrict, " + // 20 + "addr.CurrPinCode, " + // 21 + "addr.CurrServicePointId, " + // 22 + "addr.CurrServicePoint, " + // 23 + "addr.ParkingPlaceID, " + // 24 + "contact.PreferredPhoneNum " + // 25 + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarydetails d ON m.BenDetailsId = d.BeneficiaryDetailsID " + + "LEFT JOIN db_iemr.m_gender g ON d.GenderID = g.GenderID " + + "LEFT JOIN i_beneficiaryaddress addr ON m.BenAddressId = addr.BenAddressID " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId IN (:ids) AND m.Deleted = false", + nativeQuery = true) + List findCompleteDataByIds(@Param("ids") List ids); + + /** + * Direct search in database (fallback) + */ + @Query(value = "SELECT " + + "m.BenRegId, " + + "d.BeneficiaryRegID, " + + "d.FirstName, " + + "d.LastName, " + + "d.GenderID, " + + "g.GenderName, " + + "d.DOB, " + + "TIMESTAMPDIFF(YEAR, d.DOB, CURDATE()) as Age, " + + "d.FatherName, " + + "d.SpouseName, " + + "d.IsHIVPositive, " + + "m.CreatedBy, " + + "m.CreatedDate, " + + "UNIX_TIMESTAMP(m.LastModDate) * 1000, " + + "m.BenAccountID, " + + "addr.CurrStateId, " + + "addr.CurrState, " + + "addr.CurrDistrictId, " + + "addr.CurrDistrict, " + + "addr.CurrSubDistrictId, " + + "addr.CurrSubDistrict, " + + "addr.CurrPinCode, " + + "addr.CurrServicePointId, " + + "addr.CurrServicePoint, " + + "addr.ParkingPlaceID, " + + "contact.PreferredPhoneNum " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarydetails d ON m.BenDetailsId = d.BeneficiaryDetailsID " + + "LEFT JOIN db_iemr.m_gender g ON d.GenderID = g.GenderID " + + "LEFT JOIN i_beneficiaryaddress addr ON m.BenAddressId = addr.BenAddressID " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE (d.FirstName LIKE CONCAT('%', :query, '%') " + + " OR d.LastName LIKE CONCAT('%', :query, '%') " + + " OR d.FatherName LIKE CONCAT('%', :query, '%') " + + " OR d.BeneficiaryRegID = :query " + + " OR contact.PreferredPhoneNum = :query " + + " OR contact.PhoneNum1 = :query " + + " OR contact.PhoneNum2 = :query " + + " OR contact.PhoneNum3 = :query " + + " OR contact.PhoneNum4 = :query " + + " OR contact.PhoneNum5 = :query) " + + "AND m.Deleted = false " + + "LIMIT 20", + nativeQuery = true) + List searchBeneficiaries(@Param("query") String query); + + /** + * Get all phone numbers for a beneficiary + */ + @Query(value = "SELECT " + + "contact.PreferredPhoneNum as phoneNo, " + + "'Preferred' as phoneType, " + + "1 as priority " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PreferredPhoneNum IS NOT NULL " + + "UNION ALL " + + "SELECT contact.PhoneNum1, contact.PhoneTyp1, 2 " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PhoneNum1 IS NOT NULL " + + "UNION ALL " + + "SELECT contact.PhoneNum2, contact.PhoneTyp2, 3 " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PhoneNum2 IS NOT NULL " + + "UNION ALL " + + "SELECT contact.PhoneNum3, contact.PhoneTyp3, 4 " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PhoneNum3 IS NOT NULL " + + "UNION ALL " + + "SELECT contact.PhoneNum4, contact.PhoneTyp4, 5 " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PhoneNum4 IS NOT NULL " + + "UNION ALL " + + "SELECT contact.PhoneNum5, contact.PhoneTyp5, 6 " + + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "WHERE m.BenRegId = :beneficiaryId AND contact.PhoneNum5 IS NOT NULL " + + "ORDER BY priority", + nativeQuery = true) + List findPhoneNumbersByBeneficiaryId(@Param("beneficiaryId") Long beneficiaryId); + + + } diff --git a/src/main/java/com/iemr/common/identity/service/IdentityService.java b/src/main/java/com/iemr/common/identity/service/IdentityService.java index 8a154c7d..e0dc9c61 100644 --- a/src/main/java/com/iemr/common/identity/service/IdentityService.java +++ b/src/main/java/com/iemr/common/identity/service/IdentityService.java @@ -161,7 +161,7 @@ private JdbcTemplate getJdbcTemplate() { @Autowired private V_BenAdvanceSearchRepo v_BenAdvanceSearchRepo; - @Value("${elasticsearch.enabled:false}") + @Value("${elasticsearch.enabled}") private boolean esEnabled; public void getBenAdress() { @@ -330,68 +330,68 @@ public List getBeneficiarieswithES(IdentitySearchDTO searchDTO /** * Try Elasticsearch first if enabled */ - if (esEnabled) { - try { - logger.info("Attempting Elasticsearch search"); + // if (esEnabled) { + // try { + // logger.info("Attempting Elasticsearch search"); - // Search by beneficiary Id - if (searchDTO.getBeneficiaryId() != null) { - logger.info("Elasticsearch: searching by beneficiaryId: {}", searchDTO.getBeneficiaryId()); - list = elasticsearchService.searchByBenId(searchDTO.getBeneficiaryId()); - if (!list.isEmpty()) { - logger.info("Found {} results from Elasticsearch", list.size()); - return enrichBeneficiariesFromDatabase(list); - } - } + // // Search by beneficiary Id + // if (searchDTO.getBeneficiaryId() != null) { + // logger.info("Elasticsearch: searching by beneficiaryId: {}", searchDTO.getBeneficiaryId()); + // list = elasticsearchService.searchByBenId(searchDTO.getBeneficiaryId()); + // if (!list.isEmpty()) { + // logger.info("Found {} results from Elasticsearch", list.size()); + // return enrichBeneficiariesFromDatabase(list); + // } + // } - // Search by beneficiary Reg Id - if (searchDTO.getBeneficiaryRegId() != null) { - logger.info("Elasticsearch: searching by beneficiaryRegId: {}", searchDTO.getBeneficiaryRegId()); - list = elasticsearchService.searchByBenRegId(searchDTO.getBeneficiaryRegId()); - if (!list.isEmpty()) { - logger.info("Found {} results from Elasticsearch", list.size()); - return enrichBeneficiariesFromDatabase(list); - } - } + // // Search by beneficiary Reg Id + // if (searchDTO.getBeneficiaryRegId() != null) { + // logger.info("Elasticsearch: searching by beneficiaryRegId: {}", searchDTO.getBeneficiaryRegId()); + // list = elasticsearchService.searchByBenRegId(searchDTO.getBeneficiaryRegId()); + // if (!list.isEmpty()) { + // logger.info("Found {} results from Elasticsearch", list.size()); + // return enrichBeneficiariesFromDatabase(list); + // } + // } - // Search by contact number - if (searchDTO.getContactNumber() != null) { - logger.info("Elasticsearch: searching by phoneNum: {}", searchDTO.getContactNumber()); - list = elasticsearchService.searchByPhoneNum(searchDTO.getContactNumber()); - if (!list.isEmpty()) { - logger.info("Found {} results from Elasticsearch", list.size()); + // // Search by contact number + // if (searchDTO.getContactNumber() != null) { + // logger.info("Elasticsearch: searching by phoneNum: {}", searchDTO.getContactNumber()); + // list = elasticsearchService.searchByPhoneNum(searchDTO.getContactNumber()); + // if (!list.isEmpty()) { + // logger.info("Found {} results from Elasticsearch", list.size()); - // Apply D2D filters if needed - if (searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { - list = applyD2DFilters(list, searchDTO); - } + // // Apply D2D filters if needed + // if (searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { + // list = applyD2DFilters(list, searchDTO); + // } - return enrichBeneficiariesFromDatabase(list); - } - } + // return enrichBeneficiariesFromDatabase(list); + // } + // } - // Advanced search (multiple criteria) - if (hasMultipleCriteria(searchDTO)) { - logger.info("Elasticsearch: advanced search"); - list = elasticsearchService.advancedSearch( - searchDTO.getFirstName(), - searchDTO.getLastName(), - searchDTO.getContactNumber(), - null, // gender from searchDTO if available - null // age from searchDTO if available - ); - if (!list.isEmpty()) { - logger.info("Found {} results from Elasticsearch", list.size()); - return enrichBeneficiariesFromDatabase(list); - } - } + // // Advanced search (multiple criteria) + // if (hasMultipleCriteria(searchDTO)) { + // logger.info("Elasticsearch: advanced search"); + // list = elasticsearchService.advancedSearch( + // searchDTO.getFirstName(), + // searchDTO.getLastName(), + // searchDTO.getContactNumber(), + // null, // gender from searchDTO if available + // null // age from searchDTO if available + // ); + // if (!list.isEmpty()) { + // logger.info("Found {} results from Elasticsearch", list.size()); + // return enrichBeneficiariesFromDatabase(list); + // } + // } - logger.info("No results from Elasticsearch, falling back to database"); + // logger.info("No results from Elasticsearch, falling back to database"); - } catch (Exception e) { - logger.error("Elasticsearch search failed, falling back to database: {}", e.getMessage()); - } - } + // } catch (Exception e) { + // logger.error("Elasticsearch search failed, falling back to database: {}", e.getMessage()); + // } + // } /** * Fallback to MySQL database search @@ -558,19 +558,19 @@ public List getBeneficiariesByBenId(BigInteger benId) logger.info("IdentityService.getBeneficiariesByBenId - start, beneficiaryID : " + benId); List list = new ArrayList(); - // Try Elasticsearch first if enabled - if (esEnabled) { - try { - list = elasticsearchService.searchByBenId(benId); - if (!list.isEmpty()) { - logger.info("Found " + list.size() + " results from Elasticsearch for BenId: " + benId); - return list; - } - logger.info("No results from Elasticsearch, falling back to database"); - } catch (Exception e) { - logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); - } - } + // // Try Elasticsearch first if enabled + // if (esEnabled) { + // try { + // list = elasticsearchService.searchByBenId(benId); + // if (!list.isEmpty()) { + // logger.info("Found " + list.size() + " results from Elasticsearch for BenId: " + benId); + // return list; + // } + // logger.info("No results from Elasticsearch, falling back to database"); + // } catch (Exception e) { + // logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); + // } + // } MBeneficiaryregidmapping regId = regIdRepo.findByBeneficiaryID(benId); @@ -636,18 +636,18 @@ public List getBeneficiariesByPhoneNum(String phoneNum) List list = new ArrayList<>(); // Try Elasticsearch first if enabled - if (esEnabled) { - try { - list = elasticsearchService.searchByPhoneNum(phoneNum); - if (!list.isEmpty()) { - logger.info("Found " + list.size() + " results from Elasticsearch for PhoneNum: " + phoneNum); - return list; - } - logger.info("No results from Elasticsearch, falling back to database"); - } catch (Exception e) { - logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); - } - } + // if (esEnabled) { + // try { + // list = elasticsearchService.searchByPhoneNum(phoneNum); + // if (!list.isEmpty()) { + // logger.info("Found " + list.size() + " results from Elasticsearch for PhoneNum: " + phoneNum); + // return list; + // } + // logger.info("No results from Elasticsearch, falling back to database"); + // } catch (Exception e) { + // logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); + // } + // } try { List benContact = contactRepo.findByAnyPhoneNum(phoneNum); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index 8ff2df36..49cf087d 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -1,315 +1,486 @@ package com.iemr.common.identity.service.elasticsearch; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; - +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import com.iemr.common.identity.dto.BeneficiariesESDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import co.elastic.clients.elasticsearch.ElasticsearchClient; -import co.elastic.clients.elasticsearch.core.SearchRequest; -import co.elastic.clients.elasticsearch.core.SearchResponse; -import co.elastic.clients.elasticsearch.core.search.Hit; -import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; -import co.elastic.clients.elasticsearch._types.query_dsl.Query; -import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; -import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery; -import co.elastic.clients.elasticsearch._types.query_dsl.WildcardQuery; -import co.elastic.clients.elasticsearch._types.FieldValue; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; -import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; -import com.iemr.common.identity.dto.BenDetailDTO; -import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.repo.BenDetailRepo; -/** - * Service for Elasticsearch search operations - * Provides fast search functionality for beneficiaries - */ @Service public class ElasticsearchService { - + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchService.class); - + @Autowired private ElasticsearchClient esClient; - - @Value("${elasticsearch.index.beneficiary}") + + @Autowired + private BenDetailRepo benRegRepository; + + @Value("${elasticsearch.index.beneficiary:beneficiary_index_v4}") private String beneficiaryIndex; - - @Value("${elasticsearch.enabled:false}") - private boolean esEnabled; - - /** - * Search beneficiary by BeneficiaryID (exact match) - */ - public List searchByBenId(BigInteger benId) { - if (!esEnabled || benId == null) { - return new ArrayList<>(); - } - - try { - logger.info("Searching Elasticsearch by benId: {}", benId); - - Query query = TermQuery.of(t -> t - .field("benId.keyword") - .value(FieldValue.of(benId.toString())) - )._toQuery(); - - return executeSearch(query); - - } catch (Exception e) { - logger.error("Error searching by benId: {}", e.getMessage(), e); - return new ArrayList<>(); + + public List> universalSearch(String query) { + logger.info("Universal ES search for: {}", query); + + try { + boolean isNumeric = query.matches("\\d+"); + + SearchResponse response = esClient.search(s -> s + .index(beneficiaryIndex) + .query(q -> q + .bool(b -> { + // Fuzzy multi-match for text fields + b.should(s1 -> s1.multiMatch(mm -> mm + .query(query) + .fields("firstName^3", "lastName^2", "fatherName", "spouseName") + .type(TextQueryType.BestFields) + .fuzziness("AUTO") + )); + + // Exact match for phone number + b.should(s2 -> s2.term(t -> t + .field("phoneNum") + .value(query) + )); + + // Numeric fields (only if query is numeric) + if (isNumeric) { + try { + Long numericValue = Long.parseLong(query); + b.should(s3 -> s3.term(t -> t.field("benRegId").value(numericValue))); + } catch (NumberFormatException e) { + logger.debug("Could not parse as long: {}", query); + } + + b.should(s4 -> s4.term(t -> t.field("benId").value(query))); + } + + b.minimumShouldMatch("1"); + return b; + }) + ) + .size(20) + , BeneficiariesESDTO.class); + + logger.info("ES returned {} hits", response.hits().hits().size()); + + // Convert ES results directly to response format + List> results = response.hits().hits().stream() + .map(hit -> mapESResultToExpectedFormat(hit.source())) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (results.isEmpty()) { + logger.info("No results found in ES, falling back to database"); + return searchInDatabaseDirectly(query); } + + logger.info("Returning {} results from ES", results.size()); + return results; + + } catch (Exception e) { + logger.error("ES universal search failed: {}", e.getMessage(), e); + logger.info("Fallback: Searching in MySQL database"); + return searchInDatabaseDirectly(query); } +} - /** - * Search beneficiary by BeneficiaryRegID (exact match) - */ - public List searchByBenRegId(BigInteger benRegId) { - if (!esEnabled || benRegId == null) { - return new ArrayList<>(); - } - - try { - logger.info("Searching Elasticsearch by benRegId: {}", benRegId); - - Query query = TermQuery.of(t -> t - .field("benRegId") - .value(FieldValue.of(benRegId.longValue())) - )._toQuery(); - - return executeSearch(query); - - } catch (Exception e) { - logger.error("Error searching by benRegId: {}", e.getMessage(), e); - return new ArrayList<>(); - } +/** + * Map ES DTO directly to expected API format (NO DATABASE CALL) + */ +private Map mapESResultToExpectedFormat(BeneficiariesESDTO esData) { + if (esData == null) { + return null; } - - /** - * Search beneficiary by Phone Number (supports partial match) - */ - public List searchByPhoneNum(String phoneNum) { - if (!esEnabled || phoneNum == null || phoneNum.trim().isEmpty()) { - return new ArrayList<>(); - } - - try { - logger.info("Searching Elasticsearch by phoneNum: {}", phoneNum); - - // Clean phone number (remove +91, spaces, etc.) - String cleanedPhone = cleanPhoneNumber(phoneNum); - - Query query = WildcardQuery.of(w -> w - .field("phoneNum.keyword") - .value("*" + cleanedPhone + "*") - )._toQuery(); - - return executeSearch(query); - - } catch (Exception e) { - logger.error("Error searching by phoneNum: {}", e.getMessage(), e); - return new ArrayList<>(); + + Map result = new HashMap<>(); + + try { + // Basic fields from ES + result.put("beneficiaryRegID", esData.getBenRegId()); + result.put("beneficiaryID", esData.getBeneficiaryID()); + result.put("firstName", esData.getFirstName()); + result.put("lastName", esData.getLastName()); + result.put("genderID", esData.getGenderID()); + result.put("genderName", esData.getGenderName()); + // result.put("dOB", esData.getdOB()); + result.put("dob", esData.getDOB()); + result.put("age", esData.getAge()); + result.put("actualAge", esData.getAge()); + result.put("ageUnits", "Years"); + result.put("fatherName", esData.getFatherName() != null ? esData.getFatherName() : ""); + result.put("spouseName", esData.getSpouseName() != null ? esData.getSpouseName() : ""); + result.put("isHIVPos", ""); + result.put("createdBy", esData.getCreatedBy()); + result.put("createdDate", esData.getCreatedDate()); + result.put("lastModDate", esData.getLastModDate()); + result.put("benAccountID", esData.getBenAccountID()); + + // Gender object + Map mGender = new HashMap<>(); + mGender.put("genderID", esData.getGenderID()); + mGender.put("genderName", esData.getGenderName()); + result.put("m_gender", mGender); + + // Demographics object from ES + Map demographics = new HashMap<>(); + demographics.put("beneficiaryRegID", esData.getBenRegId()); + demographics.put("stateID", esData.getStateID()); + demographics.put("stateName", esData.getStateName()); + demographics.put("districtID", esData.getDistrictID()); + demographics.put("districtName", esData.getDistrictName()); + demographics.put("blockID", esData.getBlockID()); + demographics.put("blockName", esData.getBlockName()); + demographics.put("districtBranchID", null); + demographics.put("districtBranchName", null); + demographics.put("parkingPlaceID", esData.getParkingPlaceID()); + demographics.put("servicePointID", esData.getServicePointID()); + demographics.put("servicePointName", esData.getServicePointName()); + demographics.put("createdBy", esData.getCreatedBy()); + + // Nested m_state + Map mState = new HashMap<>(); + mState.put("stateID", esData.getStateID()); + mState.put("stateName", esData.getStateName()); + mState.put("stateCode", null); + mState.put("countryID", 1); + demographics.put("m_state", mState); + + // Nested m_district + Map mDistrict = new HashMap<>(); + mDistrict.put("districtID", esData.getDistrictID()); + mDistrict.put("districtName", esData.getDistrictName()); + mDistrict.put("stateID", esData.getStateID()); + demographics.put("m_district", mDistrict); + + // Nested m_districtblock + Map mBlock = new HashMap<>(); + mBlock.put("blockID", esData.getBlockID()); + mBlock.put("blockName", esData.getBlockName()); + mBlock.put("districtID", esData.getDistrictID()); + mBlock.put("stateID", esData.getStateID()); + demographics.put("m_districtblock", mBlock); + + // Nested m_districtbranchmapping + Map mBranch = new HashMap<>(); + mBranch.put("districtBranchID", null); + mBranch.put("blockID", esData.getBlockID()); + mBranch.put("villageName", null); + mBranch.put("pinCode", esData.getPinCode()); + demographics.put("m_districtbranchmapping", mBranch); + + result.put("i_bendemographics", demographics); + + // Phone numbers from ES + List> benPhoneMaps = new ArrayList<>(); + if (esData.getPhoneNum() != null && !esData.getPhoneNum().isEmpty()) { + Map phoneMap = new HashMap<>(); + phoneMap.put("benPhMapID", 1L); + phoneMap.put("benificiaryRegID", esData.getBenRegId()); + phoneMap.put("parentBenRegID", esData.getBenRegId()); + phoneMap.put("benRelationshipID", 1); + phoneMap.put("phoneNo", esData.getPhoneNum()); + + Map relationType = new HashMap<>(); + relationType.put("benRelationshipID", 1); + relationType.put("benRelationshipType", "Self"); + phoneMap.put("benRelationshipType", relationType); + + benPhoneMaps.add(phoneMap); } + result.put("benPhoneMaps", benPhoneMaps); + + // Default values + result.put("isConsent", false); + result.put("m_title", new HashMap<>()); + result.put("maritalStatus", new HashMap<>()); + result.put("changeInSelfDetails", false); + result.put("changeInAddress", false); + result.put("changeInContacts", false); + result.put("changeInIdentities", false); + result.put("changeInOtherDetails", false); + result.put("changeInFamilyDetails", false); + result.put("changeInAssociations", false); + result.put("changeInBankDetails", false); + result.put("changeInBenImage", false); + result.put("is1097", false); + result.put("emergencyRegistration", false); + result.put("passToNurse", false); + result.put("beneficiaryIdentities", new ArrayList<>()); + + } catch (Exception e) { + logger.error("Error mapping ES result: {}", e.getMessage(), e); + return null; } - + + return result; +} + /** - * Search beneficiary by First Name (fuzzy match - handles typos) + * Fetch complete beneficiary data from database using IDs from ES */ - public List searchByFirstName(String firstName) { - if (!esEnabled || firstName == null || firstName.trim().isEmpty()) { - return new ArrayList<>(); - } - + private List> fetchCompleteDataFromDatabase(List beneficiaryIds) { try { - logger.info("Searching Elasticsearch by firstName: {}", firstName); - - Query query = MatchQuery.of(m -> m - .field("firstName") - .query(firstName) - .fuzziness("AUTO") // Tolerates 1-2 character differences - )._toQuery(); - - return executeSearch(query); - + List results = benRegRepository.findCompleteDataByIds(beneficiaryIds); + + return results.stream() + .map(this::mapToExpectedFormat) + .collect(Collectors.toList()); + } catch (Exception e) { - logger.error("Error searching by firstName: {}", e.getMessage(), e); - return new ArrayList<>(); + logger.error("Failed to fetch complete data from database: {}", e.getMessage(), e); + return Collections.emptyList(); } } - + /** - * Search beneficiary by Last Name (fuzzy match) + * Direct database search as fallback */ - public List searchByLastName(String lastName) { - if (!esEnabled || lastName == null || lastName.trim().isEmpty()) { - return new ArrayList<>(); - } - + private List> searchInDatabaseDirectly(String query) { try { - logger.info("Searching Elasticsearch by lastName: {}", lastName); - - Query query = MatchQuery.of(m -> m - .field("lastName") - .query(lastName) - .fuzziness("AUTO") - )._toQuery(); - - return executeSearch(query); - + List results = benRegRepository.searchBeneficiaries(query); + + return results.stream() + .map(this::mapToExpectedFormat) + .collect(Collectors.toList()); + } catch (Exception e) { - logger.error("Error searching by lastName: {}", e.getMessage(), e); - return new ArrayList<>(); + logger.error("Database search failed: {}", e.getMessage(), e); + return Collections.emptyList(); } } - + /** - * Advanced search with multiple criteria (AND condition) - * Example: Search by firstName AND phoneNum + * Map database result to expected API format */ - public List advancedSearch(String firstName, String lastName, - String phoneNum, String gender, Integer age) { - if (!esEnabled) { - return new ArrayList<>(); - } - + private Map mapToExpectedFormat(Object[] row) { + Map result = new HashMap<>(); + try { - logger.info("Advanced search: firstName={}, lastName={}, phoneNum={}, gender={}, age={}", - firstName, lastName, phoneNum, gender, age); - - BoolQuery.Builder boolQuery = new BoolQuery.Builder(); - - // Add firstName filter (fuzzy) - if (firstName != null && !firstName.trim().isEmpty()) { - boolQuery.must(MatchQuery.of(m -> m - .field("firstName") - .query(firstName) - .fuzziness("AUTO") - )._toQuery()); - } - - // Add lastName filter (fuzzy) - if (lastName != null && !lastName.trim().isEmpty()) { - boolQuery.must(MatchQuery.of(m -> m - .field("lastName") - .query(lastName) - .fuzziness("AUTO") - )._toQuery()); - } - - // Add phoneNum filter (wildcard) - if (phoneNum != null && !phoneNum.trim().isEmpty()) { - String cleanedPhone = cleanPhoneNumber(phoneNum); - boolQuery.must(WildcardQuery.of(w -> w - .field("phoneNum.keyword") - .value("*" + cleanedPhone + "*") - )._toQuery()); - } - - // Add gender filter (exact) - if (gender != null && !gender.trim().isEmpty()) { - boolQuery.must(TermQuery.of(t -> t - .field("gender.keyword") - .value(FieldValue.of(gender)) - )._toQuery()); - } - - // Add age filter (exact) - if (age != null) { - boolQuery.must(TermQuery.of(t -> t - .field("age") - .value(FieldValue.of(age)) - )._toQuery()); - } - - Query query = boolQuery.build()._toQuery(); - return executeSearch(query); - + // Basic fields + Long beneficiaryRegID = getLong(row[0]); + String beneficiaryID = getString(row[1]); + String firstName = getString(row[2]); + String lastName = getString(row[3]); + Integer genderID = getInteger(row[4]); + String genderName = getString(row[5]); + Date dob = getDate(row[6]); + Integer age = getInteger(row[7]); + String fatherName = getString(row[8]); + String spouseName = getString(row[9]); + String isHIVPos = getString(row[10]); + String createdBy = getString(row[11]); + Date createdDate = getDate(row[12]); + Long lastModDate = getLong(row[13]); + Long benAccountID = getLong(row[14]); + + // Demographics + Integer stateID = getInteger(row[15]); + String stateName = getString(row[16]); + Integer districtID = getInteger(row[17]); + String districtName = getString(row[18]); + Integer blockID = getInteger(row[19]); + String blockName = getString(row[20]); + String pinCode = getString(row[21]); + Integer servicePointID = getInteger(row[22]); + String servicePointName = getString(row[23]); + Integer parkingPlaceID = getInteger(row[24]); + String phoneNum = getString(row[25]); + + // Build result + result.put("beneficiaryRegID", beneficiaryRegID); + result.put("beneficiaryID", beneficiaryID); + result.put("firstName", firstName); + result.put("lastName", lastName); + result.put("genderID", genderID); + result.put("genderName", genderName); + result.put("dOB", dob); + result.put("dob", dob); + result.put("age", age); + result.put("actualAge", age); + result.put("ageUnits", "Years"); + result.put("fatherName", fatherName != null ? fatherName : ""); + result.put("spouseName", spouseName != null ? spouseName : ""); + result.put("isHIVPos", isHIVPos != null ? isHIVPos : ""); + result.put("createdBy", createdBy); + result.put("createdDate", createdDate); + result.put("lastModDate", lastModDate); + result.put("benAccountID", benAccountID); + + // Gender object + Map mGender = new HashMap<>(); + mGender.put("genderID", genderID); + mGender.put("genderName", genderName); + result.put("m_gender", mGender); + + // Demographics object + Map demographics = new HashMap<>(); + demographics.put("beneficiaryRegID", beneficiaryRegID); + demographics.put("stateID", stateID); + demographics.put("stateName", stateName); + demographics.put("districtID", districtID); + demographics.put("districtName", districtName); + demographics.put("blockID", blockID); + demographics.put("blockName", blockName); + demographics.put("districtBranchID", null); // Not in your tables + demographics.put("districtBranchName", null); + demographics.put("parkingPlaceID", parkingPlaceID); + demographics.put("servicePointID", servicePointID); + demographics.put("servicePointName", servicePointName); + demographics.put("createdBy", createdBy); + + // Nested m_state + Map mState = new HashMap<>(); + mState.put("stateID", stateID); + mState.put("stateName", stateName); + mState.put("stateCode", null); // Not available in your tables + mState.put("countryID", 1); + demographics.put("m_state", mState); + + // Nested m_district + Map mDistrict = new HashMap<>(); + mDistrict.put("districtID", districtID); + mDistrict.put("districtName", districtName); + mDistrict.put("stateID", stateID); + demographics.put("m_district", mDistrict); + + // Nested m_districtblock + Map mBlock = new HashMap<>(); + mBlock.put("blockID", blockID); + mBlock.put("blockName", blockName); + mBlock.put("districtID", districtID); + mBlock.put("stateID", stateID); + demographics.put("m_districtblock", mBlock); + + // Nested m_districtbranchmapping + Map mBranch = new HashMap<>(); + mBranch.put("districtBranchID", null); + mBranch.put("blockID", blockID); + mBranch.put("villageName", null); + mBranch.put("pinCode", pinCode); + demographics.put("m_districtbranchmapping", mBranch); + + result.put("i_bendemographics", demographics); + + // Phone numbers + List> benPhoneMaps = fetchPhoneNumbers(beneficiaryRegID); + result.put("benPhoneMaps", benPhoneMaps); + + // Default values + result.put("isConsent", false); + result.put("m_title", new HashMap<>()); + result.put("maritalStatus", new HashMap<>()); + result.put("changeInSelfDetails", false); + result.put("changeInAddress", false); + result.put("changeInContacts", false); + result.put("changeInIdentities", false); + result.put("changeInOtherDetails", false); + result.put("changeInFamilyDetails", false); + result.put("changeInAssociations", false); + result.put("changeInBankDetails", false); + result.put("changeInBenImage", false); + result.put("is1097", false); + result.put("emergencyRegistration", false); + result.put("passToNurse", false); + result.put("beneficiaryIdentities", new ArrayList<>()); + } catch (Exception e) { - logger.error("Error in advanced search: {}", e.getMessage(), e); - return new ArrayList<>(); + logger.error("Error mapping result: {}", e.getMessage(), e); } + + return result; } - + /** - * Execute search query and convert results to DTOs + * Fetch phone numbers for a beneficiary */ - private List executeSearch(Query query) { + private List> fetchPhoneNumbers(Long beneficiaryRegID) { + List> phoneList = new ArrayList<>(); + try { - SearchRequest searchRequest = SearchRequest.of(s -> s - .index(beneficiaryIndex) - .query(query) - .size(100) // Limit to 100 results - ); - - SearchResponse response = esClient.search( - searchRequest, - BeneficiaryDocument.class - ); - - List results = new ArrayList<>(); + List phones = benRegRepository.findPhoneNumbersByBeneficiaryId(beneficiaryRegID); - for (Hit hit : response.hits().hits()) { - BeneficiaryDocument doc = hit.source(); - if (doc != null) { - results.add(convertToDTO(doc)); + int mapId = 1; + for (Object[] phone : phones) { + String phoneNo = getString(phone[0]); + String phoneType = getString(phone[1]); + + if (phoneNo != null && !phoneNo.isEmpty()) { + Map phoneMap = new HashMap<>(); + phoneMap.put("benPhMapID", (long) mapId++); + phoneMap.put("benificiaryRegID", beneficiaryRegID); + phoneMap.put("parentBenRegID", beneficiaryRegID); + phoneMap.put("benRelationshipID", 1); + phoneMap.put("phoneNo", phoneNo); + + Map relationType = new HashMap<>(); + relationType.put("benRelationshipID", 1); + relationType.put("benRelationshipType", phoneType != null ? phoneType : "Self"); + phoneMap.put("benRelationshipType", relationType); + + phoneList.add(phoneMap); } } - - logger.info("Found {} results from Elasticsearch", results.size()); - return results; - } catch (Exception e) { - logger.error("Error executing search: {}", e.getMessage(), e); - throw new RuntimeException("Elasticsearch search failed", e); + logger.error("Error fetching phone numbers: {}", e.getMessage(), e); } - } - - /** - * Convert BeneficiaryDocument to BeneficiariesDTO - */ - private BeneficiariesDTO convertToDTO(BeneficiaryDocument doc) { - BeneficiariesDTO dto = new BeneficiariesDTO(); - if (doc.getBenId() != null) { - dto.setBenId(new BigInteger(doc.getBenId())); - } - - if (doc.getBenRegId() != null) { - dto.setBenRegId(BigInteger.valueOf(doc.getBenRegId())); + return phoneList; + } + + // Helper methods + private String getString(Object value) { + if (value == null) return null; + return value.toString(); + } + + private Long getLong(Object value) { + if (value == null) return null; + if (value instanceof Long) return (Long) value; + if (value instanceof Integer) return ((Integer) value).longValue(); + if (value instanceof BigDecimal) return ((BigDecimal) value).longValue(); + if (value instanceof String) { + try { + return Long.parseLong((String) value); + } catch (NumberFormatException e) { + return null; + } } - - dto.setPreferredPhoneNum(doc.getPhoneNum()); - - BenDetailDTO detailDTO = new BenDetailDTO(); - detailDTO.setFirstName(doc.getFirstName()); - detailDTO.setLastName(doc.getLastName()); - detailDTO.setBeneficiaryAge(doc.getAge()); - detailDTO.setGender(doc.getGender()); - - dto.setBeneficiaryDetails(detailDTO); - - return dto; + return null; } - - /** - * Clean phone number for searching - */ - private String cleanPhoneNumber(String phoneNumber) { - if (phoneNumber == null) { - return ""; + + private Integer getInteger(Object value) { + if (value == null) return null; + if (value instanceof Integer) return (Integer) value; + if (value instanceof Long) return ((Long) value).intValue(); + if (value instanceof BigDecimal) return ((BigDecimal) value).intValue(); + if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } catch (NumberFormatException e) { + return null; + } } - - // Remove +91, spaces, dashes - return phoneNumber.trim() - .replaceAll("\\+91", "") - .replaceAll("\\s+", "") - .replaceAll("-", ""); + return null; } + private Date getDate(Object value) { + if (value == null) return null; + if (value instanceof Date) return (Date) value; + if (value instanceof Timestamp) return new Date(((Timestamp) value).getTime()); + if (value instanceof java.sql.Date) return new Date(((java.sql.Date) value).getTime()); + return null; + } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java new file mode 100644 index 00000000..21e96ad4 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java @@ -0,0 +1,492 @@ +package com.iemr.common.identity.service.elasticsearch; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.BulkRequest; +import co.elastic.clients.elasticsearch.core.BulkResponse; +import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem; + +import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; +import com.iemr.common.identity.dto.BenDetailDTO; +import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.repo.BenMappingRepo; +import com.iemr.common.identity.service.elasticsearch.BeneficiaryDataService; + +/** + * Service to synchronize beneficiary data from database to Elasticsearch + */ +@Service +public class ElasticsearchSyncService { + + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchSyncService.class); + private static final int BATCH_SIZE = 100; // Reduced to 100 for better connection management + private static final int ES_BULK_SIZE = 50; // Reduced to 50 for better performance + private static final int PAUSE_AFTER_BATCHES = 5; // Pause after every 5 batches + + @Autowired + private ElasticsearchClient esClient; + + @Autowired + private BenMappingRepo mappingRepo; + + @Autowired + private BeneficiaryDataService beneficiaryDataService; + + @Autowired + private TransactionalSyncWrapper transactionalWrapper; + + @Value("${elasticsearch.index.beneficiary}") + private String beneficiaryIndex; + + /** + * Sync all beneficiaries from database to Elasticsearch + * This should be run as a one-time operation or scheduled job + */ + public SyncResult syncAllBeneficiaries() { + logger.info("========================================"); + logger.info("Starting full beneficiary sync to Elasticsearch..."); + logger.info("========================================"); + + SyncResult result = new SyncResult(); + + try { + // Get total count using transactional wrapper + long totalCount = transactionalWrapper.countActiveBeneficiaries(); + logger.info("Total beneficiaries to sync: {}", totalCount); + + if (totalCount == 0) { + logger.warn("No beneficiaries found to sync!"); + return result; + } + + AtomicInteger processedCount = new AtomicInteger(0); + int offset = 0; + int batchCounter = 0; + List esBatch = new ArrayList<>(); + + // Process in batches + while (offset < totalCount) { + logger.info("Fetching batch: offset={}, limit={}", offset, BATCH_SIZE); + + List batchIds = null; + + try { + // Use transactional wrapper to get fresh connection for each batch + batchIds = transactionalWrapper.getBeneficiaryIdsBatch(offset, BATCH_SIZE); + } catch (Exception e) { + logger.error("Error fetching batch from database: {}", e.getMessage()); + // Wait and retry once + try { + Thread.sleep(2000); + batchIds = transactionalWrapper.getBeneficiaryIdsBatch(offset, BATCH_SIZE); + } catch (Exception e2) { + logger.error("Retry failed: {}", e2.getMessage()); + result.setError("Database connection error: " + e2.getMessage()); + break; + } + } + + if (batchIds == null || batchIds.isEmpty()) { + logger.info("No more records to process. Breaking loop."); + break; + } + + logger.info("Processing {} beneficiaries in current batch", batchIds.size()); + + for (Object[] benIdObj : batchIds) { + try { + BigInteger benRegId = (BigInteger) benIdObj[0]; + + // Fetch beneficiary details DIRECTLY from database + BeneficiariesDTO benDTO = beneficiaryDataService.getBeneficiaryFromDatabase(benRegId); + + if (benDTO != null) { + BeneficiaryDocument doc = convertToDocument(benDTO); + + if (doc != null && doc.getBenId() != null) { + esBatch.add(doc); + + // Send to ES when batch is full + if (esBatch.size() >= ES_BULK_SIZE) { + int indexed = bulkIndexDocuments(esBatch); + result.addSuccess(indexed); + result.addFailure(esBatch.size() - indexed); + + int current = processedCount.addAndGet(esBatch.size()); + logger.info("Progress: {}/{} ({} %) - Indexed: {}, Failed: {}", + current, totalCount, + String.format("%.2f", (current * 100.0) / totalCount), + indexed, esBatch.size() - indexed); + + esBatch.clear(); + } + } else { + logger.warn("Skipping beneficiary with null benId: benRegId={}", benRegId); + result.addFailure(); + } + } else { + logger.warn("No details found for benRegId: {}", benRegId); + result.addFailure(); + } + + } catch (Exception e) { + logger.error("Error processing beneficiary in batch: {}", e.getMessage(), e); + result.addFailure(); + } + } + + offset += BATCH_SIZE; + batchCounter++; + + // Pause after every N batches to let connections stabilize + if (batchCounter % PAUSE_AFTER_BATCHES == 0) { + logger.info("Completed {} batches. Pausing for 2 seconds...", batchCounter); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + logger.warn("Sleep interrupted: {}", e.getMessage()); + } + } + } + + // Index remaining documents + if (!esBatch.isEmpty()) { + logger.info("Indexing final batch of {} documents...", esBatch.size()); + int indexed = bulkIndexDocuments(esBatch); + result.addSuccess(indexed); + result.addFailure(esBatch.size() - indexed); + processedCount.addAndGet(esBatch.size()); + } + + logger.info("========================================"); + logger.info("Sync completed successfully!"); + logger.info("Total Processed: {}", processedCount.get()); + logger.info("Successfully Indexed: {}", result.getSuccessCount()); + logger.info("Failed: {}", result.getFailureCount()); + logger.info("========================================"); + + } catch (Exception e) { + logger.error("========================================"); + logger.error("CRITICAL ERROR during full sync: {}", e.getMessage(), e); + logger.error("========================================"); + result.setError(e.getMessage()); + } + + return result; + } + + /** + * Sync a single beneficiary by BenRegId + * Uses direct database access to avoid Elasticsearch circular dependency + */ + public boolean syncSingleBeneficiary(String benRegId) { + try { + logger.info("========================================"); + logger.info("Syncing single beneficiary with BenRegId: {}", benRegId); + + BigInteger benRegIdBig = new BigInteger(benRegId); + + // Check if beneficiary exists in database first using transactional wrapper + boolean exists = transactionalWrapper.existsByBenRegId(benRegIdBig); + if (!exists) { + logger.error("Beneficiary does not exist in database: BenRegId={}", benRegId); + logger.info("========================================"); + return false; + } + + logger.info("Beneficiary exists in database. Fetching details..."); + + // Get beneficiary DIRECTLY from database (not through IdentityService) + BeneficiariesDTO benDTO = beneficiaryDataService.getBeneficiaryFromDatabase(benRegIdBig); + + if (benDTO == null) { + logger.error("Failed to fetch beneficiary details from database: BenRegId={}", benRegId); + logger.info("========================================"); + return false; + } + + logger.info("Beneficiary details fetched successfully"); + logger.info("BenRegId: {}, Name: {} {}", + benDTO.getBenRegId(), + benDTO.getBeneficiaryDetails() != null ? benDTO.getBeneficiaryDetails().getFirstName() : "N/A", + benDTO.getBeneficiaryDetails() != null ? benDTO.getBeneficiaryDetails().getLastName() : "N/A"); + + // Convert to Elasticsearch document + BeneficiaryDocument doc = convertToDocument(benDTO); + + if (doc == null || doc.getBenId() == null) { + logger.error("Failed to convert beneficiary to document: BenRegId={}", benRegId); + logger.info("========================================"); + return false; + } + + logger.info("Document created. Indexing to Elasticsearch..."); + logger.info("Document ID: {}, Index: {}", doc.getBenId(), beneficiaryIndex); + + // Index to Elasticsearch + esClient.index(i -> i + .index(beneficiaryIndex) + .id(doc.getBenId()) + .document(doc) + ); + + logger.info("========================================"); + logger.info("SUCCESS! Beneficiary synced to Elasticsearch"); + logger.info("BenRegId: {}, BenId: {}", benRegId, doc.getBenId()); + logger.info("========================================"); + + return true; + + } catch (Exception e) { + logger.error("========================================"); + logger.error("ERROR syncing beneficiary {}: {}", benRegId, e.getMessage(), e); + logger.error("========================================"); + return false; + } + } + + /** + * Bulk index documents to Elasticsearch + */ + private int bulkIndexDocuments(List documents) { + if (documents == null || documents.isEmpty()) { + return 0; + } + + try { + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (BeneficiaryDocument doc : documents) { + if (doc.getBenId() != null) { + br.operations(op -> op + .index(idx -> idx + .index(beneficiaryIndex) + .id(doc.getBenId()) + .document(doc) + ) + ); + } + } + + BulkResponse result = esClient.bulk(br.build()); + + int successCount = 0; + + // Log errors if any + if (result.errors()) { + logger.warn("Bulk indexing had some errors"); + for (BulkResponseItem item : result.items()) { + if (item.error() != null) { + logger.error("Error indexing document {}: {}", + item.id(), item.error().reason()); + } else { + successCount++; + } + } + } else { + successCount = documents.size(); + } + + return successCount; + + } catch (Exception e) { + logger.error("Critical error in bulk indexing: {}", e.getMessage(), e); + return 0; + } + } + + /** + * Convert BeneficiariesDTO to BeneficiaryDocument + */ + private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { + if (dto == null) { + logger.warn("Cannot convert null DTO to document"); + return null; + } + + try { + BeneficiaryDocument doc = new BeneficiaryDocument(); + + // BenId (use benRegId as primary identifier) + if (dto.getBenRegId() != null) { + BigInteger benRegId = (BigInteger) dto.getBenRegId(); + doc.setBenId(benRegId.toString()); + doc.setBenRegId(benRegId.longValue()); + } else if (dto.getBenId() != null) { + doc.setBenId(dto.getBenId().toString()); + if (dto.getBenId() instanceof BigInteger) { + doc.setBenRegId(((BigInteger) dto.getBenId()).longValue()); + } + } else { + logger.warn("Beneficiary has no valid ID!"); + return null; + } + + // Phone number + doc.setPhoneNum(dto.getPreferredPhoneNum()); + + // Beneficiary Details (from nested DTO) + if (dto.getBeneficiaryDetails() != null) { + BenDetailDTO benDetails = dto.getBeneficiaryDetails(); + doc.setFirstName(benDetails.getFirstName()); + doc.setLastName(benDetails.getLastName()); + doc.setAge(benDetails.getBeneficiaryAge()); + doc.setGender(benDetails.getGender()); + } + + logger.debug("Successfully converted DTO to document: benId={}", doc.getBenId()); + return doc; + + } catch (Exception e) { + logger.error("Error converting DTO to document: {}", e.getMessage(), e); + return null; + } + } + + /** + * Verify sync by checking document count + */ + public SyncStatus checkSyncStatus() { + try { + long dbCount = transactionalWrapper.countActiveBeneficiaries(); + + long esCount = esClient.count(c -> c + .index(beneficiaryIndex) + ).count(); + + SyncStatus status = new SyncStatus(); + status.setDatabaseCount(dbCount); + status.setElasticsearchCount(esCount); + status.setSynced(dbCount == esCount); + status.setMissingCount(dbCount - esCount); + + logger.info("Sync Status - DB: {}, ES: {}, Missing: {}", dbCount, esCount, dbCount - esCount); + + return status; + + } catch (Exception e) { + logger.error("Error checking sync status: {}", e.getMessage(), e); + SyncStatus status = new SyncStatus(); + status.setError(e.getMessage()); + return status; + } + } + + /** + * Result class to track sync progress + */ + public static class SyncResult { + private int successCount = 0; + private int failureCount = 0; + private String error; + + public void addSuccess(int count) { + this.successCount += count; + } + + public void addFailure() { + this.failureCount++; + } + + public void addFailure(int count) { + this.failureCount += count; + } + + public int getSuccessCount() { + return successCount; + } + + public int getFailureCount() { + return failureCount; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + @Override + public String toString() { + return "SyncResult{" + + "successCount=" + successCount + + ", failureCount=" + failureCount + + ", error='" + error + '\'' + + '}'; + } + } + + /** + * Status class to track sync verification + */ + public static class SyncStatus { + private long databaseCount; + private long elasticsearchCount; + private boolean synced; + private long missingCount; + private String error; + + public long getDatabaseCount() { + return databaseCount; + } + + public void setDatabaseCount(long databaseCount) { + this.databaseCount = databaseCount; + } + + public long getElasticsearchCount() { + return elasticsearchCount; + } + + public void setElasticsearchCount(long elasticsearchCount) { + this.elasticsearchCount = elasticsearchCount; + } + + public boolean isSynced() { + return synced; + } + + public void setSynced(boolean synced) { + this.synced = synced; + } + + public long getMissingCount() { + return missingCount; + } + + public void setMissingCount(long missingCount) { + this.missingCount = missingCount; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + @Override + public String toString() { + return "SyncStatus{" + + "databaseCount=" + databaseCount + + ", elasticsearchCount=" + elasticsearchCount + + ", synced=" + synced + + ", missingCount=" + missingCount + + ", error='" + error + '\'' + + '}'; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java index 5cf99bd5..07a5609d 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java @@ -35,7 +35,7 @@ public class RealtimeElasticsearchSyncService { @Value("${elasticsearch.index.beneficiary}") private String beneficiaryIndex; - @Value("${elasticsearch.enabled:false}") + @Value("${elasticsearch.enabled}") private boolean esEnabled; /** @@ -88,42 +88,44 @@ public void syncBeneficiaryAsync(BigInteger benRegId) { /** * Convert DTO to Document */ - private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { - if (dto == null) { - return null; - } + private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { + if (dto == null) return null; - try { - BeneficiaryDocument doc = new BeneficiaryDocument(); - - if (dto.getBenRegId() != null) { - BigInteger benRegId = (BigInteger) dto.getBenRegId(); - doc.setBenId(benRegId.toString()); - doc.setBenRegId(benRegId.longValue()); - } else if (dto.getBenId() != null) { - doc.setBenId(dto.getBenId().toString()); - if (dto.getBenId() instanceof BigInteger) { - doc.setBenRegId(((BigInteger) dto.getBenId()).longValue()); - } - } else { - return null; - } + try { + BeneficiaryDocument doc = new BeneficiaryDocument(); + // IDs + doc.setBenId(dto.getBenId() != null ? dto.getBenId().toString() : null); + doc.setBenRegId(dto.getBenRegId() != null ? dto.getBenRegId().longValue() : null); + + // Phone + if (dto.getContacts() != null && !dto.getContacts().isEmpty()) { + doc.setPhoneNum(dto.getContacts().get(0).getPhoneNum()); + } else if (dto.getPreferredPhoneNum() != null) { doc.setPhoneNum(dto.getPreferredPhoneNum()); + } - if (dto.getBeneficiaryDetails() != null) { - BenDetailDTO benDetails = dto.getBeneficiaryDetails(); - doc.setFirstName(benDetails.getFirstName()); - doc.setLastName(benDetails.getLastName()); - doc.setAge(benDetails.getBeneficiaryAge()); - doc.setGender(benDetails.getGender()); - } + // Names + if (dto.getBeneficiaryDetails() != null) { + BenDetailDTO benDetails = dto.getBeneficiaryDetails(); + doc.setFirstName(benDetails.getFirstName()); + doc.setLastName(benDetails.getLastName()); + doc.setGender(benDetails.getGender()); + } - return doc; + // Age from DTO + doc.setAge(dto.getBeneficiaryAge()); - } catch (Exception e) { - logger.error("Error converting DTO to document: {}", e.getMessage()); - return null; - } + // You can add district, village if available + // doc.setDistrictName(...); + // doc.setVillageName(...); + + return doc; + + } catch (Exception e) { + logger.error("Error converting DTO to document: {}", e.getMessage()); + return null; } +} + } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5e723699..3e509531 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -33,7 +33,7 @@ spring.datasource.hikari.minimum-idle=10 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=300000 spring.datasource.hikari.max-lifetime=600000 -spring.datasource.hikari.keepalive-time=120000 +spring.datasource.hikari.keepalive-tquickime=120000 spring.datasource.hikari.validationTimeout=5000 # REMOVE CONNECTION TEST QUERY @@ -99,7 +99,7 @@ elasticsearch.host=localhost elasticsearch.port=9200 elasticsearch.username=elastic elasticsearch.password=piramalES -elasticsearch.index.beneficiary=beneficiary_index +elasticsearch.index.beneficiary=beneficiary_index_v4 # Enable/Disable ES (for gradual rollout) elasticsearch.enabled=true From b6bec79c21e9e4eb3863195d38e39a6485d7a657 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Sat, 13 Dec 2025 21:48:37 +0530 Subject: [PATCH 04/13] fix: add additional fields as per the requirement --- .../controller/IdentityESController.java | 313 +--------- .../ElasticsearchSyncController.java | 95 ++- .../elasticsearch/BeneficiaryDocument.java | 103 +++- .../identity/dto/BeneficiariesESDTO.java | 54 +- .../identity/mapper/BeneficiaryESMapper.java | 143 +++++ .../common/identity/repo/BenAddressRepo.java | 69 ++- .../common/identity/repo/BenMappingRepo.java | 88 ++- .../AsyncElasticsearchSyncService.java | 233 ++++---- .../ElasticsearchIndexingService.java | 132 +++++ .../elasticsearch/ElasticsearchService.java | 542 +++++++++++------- .../OptimizedBeneficiaryDataService.java | 239 ++++---- .../RealtimeElasticsearchSyncService.java | 66 +-- .../common/identity/utils/CookieUtil.java | 13 +- .../utils/JwtUserIdValidationFilter.java | 1 + src/main/resources/application.properties | 2 +- 15 files changed, 1295 insertions(+), 798 deletions(-) create mode 100644 src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java create mode 100644 src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java index 00d9fb37..f445aaa5 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java @@ -1,19 +1,19 @@ package com.iemr.common.identity.controller; -import java.math.BigInteger; + import java.util.HashMap; import java.util.*; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; - -import com.iemr.common.identity.dto.BeneficiariesDTO; -import com.iemr.common.identity.dto.IdentitySearchDTO; import com.iemr.common.identity.service.elasticsearch.ElasticsearchService; -import com.iemr.common.identity.mapper.InputMapper; +import com.iemr.common.identity.utils.CookieUtil; +import com.iemr.common.identity.utils.JwtUtil; + +import jakarta.servlet.http.HttpServletRequest; /** * Elasticsearch-enabled Beneficiary Search Controller @@ -28,6 +28,9 @@ public class IdentityESController { @Autowired private ElasticsearchService elasticsearchService; + @Autowired + private JwtUtil jwtUtil; + /** * MAIN UNIVERSAL SEARCH ENDPOINT * Searches across all fields - name, phone, ID, etc. @@ -37,9 +40,12 @@ public class IdentityESController { * Usage: GET /beneficiary/search?q=rajesh kumar */ @GetMapping("/search") - public ResponseEntity> search(@RequestParam String query) { + public ResponseEntity> search(@RequestParam String query, HttpServletRequest request) { try { - List> results = elasticsearchService.universalSearch(query); + String jwtToken = CookieUtil.getJwtTokenFromCookie(request); + String userId = jwtUtil.getUserIdFromToken(jwtToken); + int userID=Integer.parseInt(userId); + List> results = elasticsearchService.universalSearch(query, userID); Map response = new HashMap<>(); response.put("data", results); @@ -59,293 +65,6 @@ public ResponseEntity> search(@RequestParam String query) { return ResponseEntity.status(500).body(errorResponse); } } - - // @GetMapping("/search") - // public ResponseEntity> universalSearch(@RequestParam String q) { - // logger.info("Universal search request: query={}", q); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // List results = elasticsearchService.universalSearch(q); - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - // response.put("query", q); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Universal search error: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - /** - * Search by BeneficiaryID (exact match) - * - * Usage: GET /beneficiary/search/benId/123456 - */ - // @GetMapping("/search/benId/{beneficiaryId}") - // public ResponseEntity> searchByBenId(@PathVariable String beneficiaryId) { - // logger.info("Search by benId: {}", beneficiaryId); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // BigInteger benId = new BigInteger(beneficiaryId); - // List results = elasticsearchService.searchByBenId(benId); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - - // return ResponseEntity.ok(response); - - // } catch (NumberFormatException e) { - // response.put("status", "error"); - // response.put("message", "Invalid beneficiary ID format"); - // return ResponseEntity.badRequest().body(response); - - // } catch (Exception e) { - // logger.error("Error searching by benId: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Search by BeneficiaryRegID (exact match) - // * - // * Usage: GET /beneficiary/search/benRegId/987654 - // */ - // @GetMapping("/search/benRegId/{benRegId}") - // public ResponseEntity> searchByBenRegId(@PathVariable String benRegId) { - // logger.info("Search by benRegId: {}", benRegId); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // BigInteger benRegIdBig = new BigInteger(benRegId); - // List results = elasticsearchService.searchByBenRegId(benRegIdBig); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - - // return ResponseEntity.ok(response); - - // } catch (NumberFormatException e) { - // response.put("status", "error"); - // response.put("message", "Invalid beneficiary registration ID format"); - // return ResponseEntity.badRequest().body(response); - - // } catch (Exception e) { - // logger.error("Error searching by benRegId: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Search by Phone Number (supports partial match) - // * - // * Usage: GET /beneficiary/search/phone/9876543210 - // * Usage: GET /beneficiary/search/phone/987654 - // */ - // @GetMapping("/search/phone/{phoneNumber}") - // public ResponseEntity> searchByPhoneNumber(@PathVariable String phoneNumber) { - // logger.info("Search by phone: {}", phoneNumber); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // List results = elasticsearchService.searchByPhoneNum(phoneNumber); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Error searching by phone: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Search by First Name (fuzzy matching - handles typos) - // * - // * Usage: GET /beneficiary/search/firstName/vani - // * Usage: GET /beneficiary/search/firstName/rajesh - // */ - // @GetMapping("/search/firstName/{firstName}") - // public ResponseEntity> searchByFirstName(@PathVariable String firstName) { - // logger.info("Search by firstName: {}", firstName); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // List results = elasticsearchService.searchByFirstName(firstName); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - // response.put("fuzzyMatchEnabled", true); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Error searching by firstName: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Search by Last Name (fuzzy matching) - // * - // * Usage: GET /beneficiary/search/lastName/kumar - // */ - // @GetMapping("/search/lastName/{lastName}") - // public ResponseEntity> searchByLastName(@PathVariable String lastName) { - // logger.info("Search by lastName: {}", lastName); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // List results = elasticsearchService.searchByLastName(lastName); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - // response.put("fuzzyMatchEnabled", true); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Error searching by lastName: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Advanced multi-field search with query parameters - // * - // * Usage: GET /beneficiary/search/advanced?firstName=vani&phoneNum=9876 - // * Usage: GET /beneficiary/search/advanced?firstName=rajesh&lastName=kumar&gender=Male - // */ - // @GetMapping("/search/advanced") - // public ResponseEntity> advancedSearchGet( - // @RequestParam(required = false) String firstName, - // @RequestParam(required = false) String lastName, - // @RequestParam(required = false) String phoneNum, - // @RequestParam(required = false) String gender, - // @RequestParam(required = false) Integer age) { - - // logger.info("Advanced search: firstName={}, lastName={}, phoneNum={}, gender={}, age={}", - // firstName, lastName, phoneNum, gender, age); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // List results = elasticsearchService.advancedSearch( - // firstName, lastName, phoneNum, gender, age - // ); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - // response.put("searchCriteria", Map.of( - // "firstName", firstName != null ? firstName : "N/A", - // "lastName", lastName != null ? lastName : "N/A", - // "phoneNum", phoneNum != null ? phoneNum : "N/A", - // "gender", gender != null ? gender : "N/A", - // "age", age != null ? age : "N/A" - // )); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Advanced search error: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } - - // /** - // * Advanced search with POST body (for complex queries) - // * - // * Usage: POST /beneficiary/search/advanced - // * Body: IdentitySearchDTO JSON - // */ - // @PostMapping("/search/advanced") - // public ResponseEntity> advancedSearchPost(@RequestBody String searchRequest) { - // logger.info("Advanced POST search request received"); - - // Map response = new HashMap<>(); - // long startTime = System.currentTimeMillis(); - - // try { - // IdentitySearchDTO searchDTO = InputMapper.getInstance().gson() - // .fromJson(searchRequest, IdentitySearchDTO.class); - - // List results = elasticsearchService.flexibleSearch(searchDTO); - - // long timeTaken = System.currentTimeMillis() - startTime; - - // response.put("status", "success"); - // response.put("count", results.size()); - // response.put("data", results); - // response.put("searchTime", timeTaken + "ms"); - // response.put("elasticsearchEnabled", true); - - // return ResponseEntity.ok(response); - - // } catch (Exception e) { - // logger.error("Advanced POST search error: {}", e.getMessage(), e); - // response.put("status", "error"); - // response.put("message", e.getMessage()); - // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - // } - // } + + } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java index 9aa472f5..127a8fa5 100644 --- a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java +++ b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java @@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; @@ -12,19 +11,20 @@ import com.iemr.common.identity.service.elasticsearch.ElasticsearchSyncService; import com.iemr.common.identity.service.elasticsearch.SyncJobService; import com.iemr.common.identity.service.elasticsearch.ElasticsearchSyncService.SyncStatus; - import java.util.HashMap; import java.util.List; import java.util.Map; - +import org.springframework.http.ResponseEntity; +import com.iemr.common.identity.utils.response.OutputResponse; import com.iemr.common.identity.domain.MBeneficiarymapping; +import com.iemr.common.identity.service.elasticsearch.ElasticsearchIndexingService; /** * Controller to manage Elasticsearch synchronization operations * Supports both synchronous and asynchronous sync jobs */ @RestController -@RequestMapping("/elasticsearch/sync") +@RequestMapping("/elasticsearch") public class ElasticsearchSyncController { private static final Logger logger = LoggerFactory.getLogger(ElasticsearchSyncController.class); @@ -38,6 +38,9 @@ public class ElasticsearchSyncController { @Autowired private BenMappingRepo mappingRepo; + @Autowired + private ElasticsearchIndexingService indexingService; + /** * Start async full sync (RECOMMENDED for millions of records) * Returns immediately with job ID for tracking @@ -59,7 +62,7 @@ public ResponseEntity> startAsyncFullSync( response.put("message", "Sync job started in background"); response.put("jobId", job.getJobId()); response.put("jobStatus", job.getStatus()); - response.put("checkStatusUrl", "/elasticsearch/sync/async/status/" + job.getJobId()); + response.put("checkStatusUrl", "/elasticsearch/async/status/" + job.getJobId()); return ResponseEntity.ok(response); @@ -344,4 +347,86 @@ public ResponseEntity> checkBeneficiaryExists( return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); } } + + /** + * Create or recreate the Elasticsearch index with proper mapping + * This will DELETE the existing index and create a new one + * + * POST /elasticsearch/index/create + */ + @PostMapping("/index/create") + public ResponseEntity createIndex() { + logger.info("API: Create Elasticsearch index request received"); + OutputResponse response = new OutputResponse(); + + try { + indexingService.createIndexWithMapping(); + + response.setResponse("Index created successfully. Ready for data sync."); + logger.info("Index created successfully"); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error creating index: {}", e.getMessage(), e); + response.setError(5000, "Error creating index: " + e.getMessage()); + return ResponseEntity.status(500).body(response); + } + } + + /** + * Recreate index and immediately start syncing data + * This is a convenience endpoint that does both operations + * + * POST /elasticsearch/index/recreate-and-sync + */ + @PostMapping("/index/recreate-and-sync") + public ResponseEntity recreateAndSync() { + logger.info("API: Recreate index and sync request received"); + OutputResponse response = new OutputResponse(); + + try { + // Step 1: Recreate index + logger.info("Step 1: Recreating index..."); + indexingService.createIndexWithMapping(); + logger.info("Index recreated successfully"); + + // Step 2: Start sync + logger.info("Step 2: Starting data sync..."); + Map syncResult = indexingService.indexAllBeneficiaries(); + + response.setResponse("Index recreated and sync started. Success: " + + syncResult.get("success") + ", Failed: " + syncResult.get("failed")); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error in recreate and sync: {}", e.getMessage(), e); + response.setError(5000, "Error: " + e.getMessage()); + return ResponseEntity.status(500).body(response); + } + } + + /** + * Get information about the current index + * Shows mapping, document count, etc. + * + * GET /elasticsearch/index/info + */ + @GetMapping("/index/info") + public ResponseEntity getIndexInfo() { + logger.info("API: Get index info request received"); + OutputResponse response = new OutputResponse(); + + try { + // You can add code here to get index stats using esClient + response.setResponse("Index info endpoint - implementation pending"); + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("Error getting index info: {}", e.getMessage(), e); + response.setError(5000, "Error: " + e.getMessage()); + return ResponseEntity.status(500).body(response); + } + } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java index 25fcdb35..d8b3b01c 100644 --- a/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java +++ b/src/main/java/com/iemr/common/identity/data/elasticsearch/BeneficiaryDocument.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.util.Date; @Data public class BeneficiaryDocument { @@ -12,8 +13,8 @@ public class BeneficiaryDocument { @JsonProperty("benRegId") private Long benRegId; - @JsonProperty("phoneNum") - private String phoneNum; + @JsonProperty("beneficiaryID") + private String beneficiaryID; @JsonProperty("firstName") private String firstName; @@ -30,18 +31,108 @@ public class BeneficiaryDocument { @JsonProperty("age") private Integer age; + @JsonProperty("dOB") + private Date dOB; + @JsonProperty("gender") private String gender; - @JsonProperty("districtName") - private String districtName; + @JsonProperty("genderID") + private Integer genderID; - @JsonProperty("villageName") - private String villageName; + @JsonProperty("genderName") + private String genderName; + + @JsonProperty("phoneNum") + private String phoneNum; @JsonProperty("aadharNo") private String aadharNo; @JsonProperty("govtIdentityNo") private String govtIdentityNo; + + @JsonProperty("healthID") + private String healthID; + + @JsonProperty("abhaID") + private String abhaID; + + @JsonProperty("familyID") + private String familyID; + + @JsonProperty("stateID") + private Integer stateID; + + @JsonProperty("stateName") + private String stateName; + + @JsonProperty("districtID") + private Integer districtID; + + @JsonProperty("districtName") + private String districtName; + + @JsonProperty("blockID") + private Integer blockID; + + @JsonProperty("blockName") + private String blockName; + + @JsonProperty("villageID") + private Integer villageID; + + @JsonProperty("villageName") + private String villageName; + + @JsonProperty("pinCode") + private String pinCode; + + @JsonProperty("servicePointID") + private Integer servicePointID; + + @JsonProperty("servicePointName") + private String servicePointName; + + @JsonProperty("parkingPlaceID") + private Integer parkingPlaceID; + + @JsonProperty("permStateID") + private Integer permStateID; + + @JsonProperty("permStateName") + private String permStateName; + + @JsonProperty("permDistrictID") + private Integer permDistrictID; + + @JsonProperty("permDistrictName") + private String permDistrictName; + + @JsonProperty("permBlockID") + private Integer permBlockID; + + @JsonProperty("permBlockName") + private String permBlockName; + + @JsonProperty("permVillageID") + private Integer permVillageID; + + @JsonProperty("permVillageName") + private String permVillageName; + + @JsonProperty("createdBy") + private String createdBy; + + @JsonProperty("createdDate") + private Date createdDate; + + @JsonProperty("lastModDate") + private Long lastModDate; + + @JsonProperty("benAccountID") + private Long benAccountID; + + @JsonProperty("isHIVPos") + private String isHIVPos; } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java b/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java index a0e0da11..b8d92536 100644 --- a/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java +++ b/src/main/java/com/iemr/common/identity/dto/BeneficiariesESDTO.java @@ -1,15 +1,11 @@ package com.iemr.common.identity.dto; import com.fasterxml.jackson.annotation.JsonProperty; - import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; - import java.util.Date; import java.util.List; -import java.util.Map; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Data @@ -18,7 +14,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class BeneficiariesESDTO { - // Basic fields @JsonProperty("benRegId") private Long benRegId; @@ -46,11 +41,11 @@ public class BeneficiariesESDTO { @JsonProperty("phoneNum") private String phoneNum; - @JsonProperty("aadharNo") - private String aadharNo; + // @JsonProperty("aadharNo") + // private String aadharNo; - @JsonProperty("govtIdentityNo") - private String govtIdentityNo; + // @JsonProperty("govtIdentityNo") + // private String govtIdentityNo; @JsonProperty("fatherName") private String fatherName; @@ -73,7 +68,15 @@ public class BeneficiariesESDTO { @JsonProperty("isHIVPos") private String isHIVPos; - // Demographics fields + @JsonProperty("healthID") + private String healthID; + + @JsonProperty("abhaID") + private String abhaID; + + @JsonProperty("familyID") + private String familyID; + @JsonProperty("stateID") private Integer stateID; @@ -95,6 +98,12 @@ public class BeneficiariesESDTO { @JsonProperty("blockName") private String blockName; + @JsonProperty("villageID") + private Integer villageID; + + @JsonProperty("villageName") + private String villageName; + @JsonProperty("districtBranchID") private Integer districtBranchID; @@ -113,7 +122,30 @@ public class BeneficiariesESDTO { @JsonProperty("pinCode") private String pinCode; - // Nested objects + @JsonProperty("permStateID") + private Integer permStateID; + + @JsonProperty("permStateName") + private String permStateName; + + @JsonProperty("permDistrictID") + private Integer permDistrictID; + + @JsonProperty("permDistrictName") + private String permDistrictName; + + @JsonProperty("permBlockID") + private Integer permBlockID; + + @JsonProperty("permBlockName") + private String permBlockName; + + @JsonProperty("permVillageID") + private Integer permVillageID; + + @JsonProperty("permVillageName") + private String permVillageName; + @JsonProperty("phoneNumbers") private List phoneNumbers; diff --git a/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java b/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java new file mode 100644 index 00000000..3233aa74 --- /dev/null +++ b/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java @@ -0,0 +1,143 @@ +package com.iemr.common.identity.mapper; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +public class BeneficiaryESMapper { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public Map transformESResponse(List> esResults) { + List> transformedData = new ArrayList<>(); + + for (Map esDoc : esResults) { + Map beneficiary = new HashMap<>(); + + // Basic beneficiary fields + beneficiary.put("beneficiaryRegID", esDoc.get("beneficiaryRegID")); + beneficiary.put("beneficiaryID", esDoc.get("beneficiaryID")); + beneficiary.put("firstName", esDoc.get("firstName")); + beneficiary.put("lastName", esDoc.get("lastName")); + beneficiary.put("genderID", esDoc.get("genderID")); + beneficiary.put("genderName", esDoc.get("genderName")); + beneficiary.put("dOB", esDoc.get("dOB")); + beneficiary.put("dob", esDoc.get("dOB")); + beneficiary.put("age", esDoc.get("age")); + beneficiary.put("actualAge", esDoc.get("age")); + beneficiary.put("ageUnits", "Years"); + beneficiary.put("fatherName", esDoc.getOrDefault("fatherName", "")); + beneficiary.put("spouseName", esDoc.getOrDefault("spouseName", "")); + beneficiary.put("isHIVPos", esDoc.getOrDefault("isHIVPos", "")); + beneficiary.put("createdBy", esDoc.get("createdBy")); + beneficiary.put("createdDate", esDoc.get("createdDate")); + beneficiary.put("lastModDate", esDoc.get("lastModDate")); + beneficiary.put("benAccountID", esDoc.get("benAccountID")); + + // Gender object + Map mGender = new HashMap<>(); + mGender.put("genderID", esDoc.get("genderID")); + mGender.put("genderName", esDoc.get("genderName")); + beneficiary.put("m_gender", mGender); + + // Demographics + Map demographics = (Map) esDoc.get("demographics"); + if (demographics != null) { + Map benDemographics = new HashMap<>(demographics); + benDemographics.put("beneficiaryRegID", esDoc.get("beneficiaryRegID")); + + // Add nested objects for demographics + benDemographics.put("m_state", createStateObject(demographics)); + benDemographics.put("m_district", createDistrictObject(demographics)); + benDemographics.put("m_districtblock", createBlockObject(demographics)); + benDemographics.put("m_districtbranchmapping", createBranchObject(demographics)); + + beneficiary.put("i_bendemographics", benDemographics); + } + + // Phone mappings + List> phoneMaps = new ArrayList<>(); + List> phoneNumbers = (List>) esDoc.get("phoneNumbers"); + if (phoneNumbers != null && !phoneNumbers.isEmpty()) { + for (Map phone : phoneNumbers) { + Map phoneMap = new HashMap<>(phone); + phoneMap.put("benificiaryRegID", esDoc.get("beneficiaryRegID")); + + // Add relationship type object + Map relationType = new HashMap<>(); + relationType.put("benRelationshipID", phone.get("benRelationshipID")); + relationType.put("benRelationshipType", phone.get("benRelationshipType")); + phoneMap.put("benRelationshipType", relationType); + + phoneMaps.add(phoneMap); + } + } + beneficiary.put("benPhoneMaps", phoneMaps); + + // Default values + beneficiary.put("isConsent", false); + beneficiary.put("m_title", new HashMap<>()); + beneficiary.put("maritalStatus", new HashMap<>()); + beneficiary.put("changeInSelfDetails", false); + beneficiary.put("changeInAddress", false); + beneficiary.put("changeInContacts", false); + beneficiary.put("changeInIdentities", false); + beneficiary.put("changeInOtherDetails", false); + beneficiary.put("changeInFamilyDetails", false); + beneficiary.put("changeInAssociations", false); + beneficiary.put("changeInBankDetails", false); + beneficiary.put("changeInBenImage", false); + beneficiary.put("is1097", false); + beneficiary.put("emergencyRegistration", false); + beneficiary.put("passToNurse", false); + beneficiary.put("beneficiaryIdentities", new ArrayList<>()); + + transformedData.add(beneficiary); + } + + Map response = new HashMap<>(); + response.put("data", transformedData); + response.put("statusCode", 200); + response.put("errorMessage", "Success"); + response.put("status", "Success"); + + return response; + } + + private Map createStateObject(Map demographics) { + Map state = new HashMap<>(); + state.put("stateID", demographics.get("stateID")); + state.put("stateName", demographics.get("stateName")); + state.put("stateCode", demographics.get("stateCode")); + state.put("countryID", 1); + return state; + } + + private Map createDistrictObject(Map demographics) { + Map district = new HashMap<>(); + district.put("districtID", demographics.get("districtID")); + district.put("districtName", demographics.get("districtName")); + district.put("stateID", demographics.get("stateID")); + return district; + } + + private Map createBlockObject(Map demographics) { + Map block = new HashMap<>(); + block.put("blockID", demographics.get("blockID")); + block.put("blockName", demographics.get("blockName")); + block.put("districtID", demographics.get("districtID")); + block.put("stateID", demographics.get("stateID")); + return block; + } + + private Map createBranchObject(Map demographics) { + Map branch = new HashMap<>(); + branch.put("districtBranchID", demographics.get("districtBranchID")); + branch.put("blockID", demographics.get("blockID")); + branch.put("villageName", demographics.get("districtBranchName")); + branch.put("pinCode", demographics.get("pinCode")); + return branch; + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/repo/BenAddressRepo.java b/src/main/java/com/iemr/common/identity/repo/BenAddressRepo.java index d9339ab2..5d79f8ae 100644 --- a/src/main/java/com/iemr/common/identity/repo/BenAddressRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/BenAddressRepo.java @@ -18,7 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/. -*/ + */ package com.iemr.common.identity.repo; import java.math.BigInteger; @@ -36,42 +36,59 @@ @Repository public interface BenAddressRepo extends CrudRepository { - List findByBenAddressIDOrderByBenAddressIDAsc(BigInteger benAddressID); - List findByCurrPinCodeOrderByBenAddressIDAsc(String currPinCode); + List findByBenAddressIDOrderByBenAddressIDAsc(BigInteger benAddressID); - List findByPermPinCodeOrderByBenAddressIDAsc(String permPinCode); + List findByCurrPinCodeOrderByBenAddressIDAsc(String currPinCode); - List findByCurrStateAndCurrDistrictOrderByBenAddressIDAsc(String currState, String currDist); + List findByPermPinCodeOrderByBenAddressIDAsc(String permPinCode); - List findByCurrStateIdAndCurrDistrictIdOrderByBenAddressIDAsc(Integer currStateID, - Integer currDistID); + List findByCurrStateAndCurrDistrictOrderByBenAddressIDAsc(String currState, String currDist); - List findByPermStateAndPermDistrictOrderByBenAddressIDAsc(String permState, String permDist); + List findByCurrStateIdAndCurrDistrictIdOrderByBenAddressIDAsc(Integer currStateID, + Integer currDistID); - List findByPermStateIdAndPermDistrictIdOrderByBenAddressIDAsc(Integer permStateID, - Integer permDistID); + List findByPermStateAndPermDistrictOrderByBenAddressIDAsc(String permState, String permDist); - List findByEmerStateAndEmerDistrictOrderByBenAddressIDAsc(String emerState, String emerDist); + List findByPermStateIdAndPermDistrictIdOrderByBenAddressIDAsc(Integer permStateID, + Integer permDistID); - List findByEmerStateIdAndEmerDistrictIdOrderByBenAddressIDAsc(Integer emerStateID, - Integer emerDistID); + List findByEmerStateAndEmerDistrictOrderByBenAddressIDAsc(String emerState, String emerDist); - List findByCreatedDateBetweenOrderByBenAddressIDAsc(Timestamp fromDate, Timestamp toDate); + List findByEmerStateIdAndEmerDistrictIdOrderByBenAddressIDAsc(Integer emerStateID, + Integer emerDistID); - @Query("select a from MBeneficiaryaddress a where a.currAddressValue = :address or " - + "a.emerAddressValue = :address or a.permAddressValue = :address order by a.benAddressID asc") - List findByAddress(String address); + List findByCreatedDateBetweenOrderByBenAddressIDAsc(Timestamp fromDate, Timestamp toDate); - @Transactional - @Modifying - @Query(" UPDATE MBeneficiaryaddress set vanSerialNo = :benAddressID WHERE benAddressID = :benAddressID") - int updateVanSerialNo(@Param("benAddressID") BigInteger benAddressID); + @Query("select a from MBeneficiaryaddress a where a.currAddressValue = :address or " + + "a.emerAddressValue = :address or a.permAddressValue = :address order by a.benAddressID asc") + List findByAddress(String address); - @Query("SELECT benAddressID FROM MBeneficiaryaddress WHERE vanSerialNo =:vanSerialNo AND vanID =:vanID ") - BigInteger findIdByVanSerialNoAndVanID(@Param("vanSerialNo") BigInteger vanSerialNo, @Param("vanID") Integer vanID); + @Transactional + @Modifying + @Query(" UPDATE MBeneficiaryaddress set vanSerialNo = :benAddressID WHERE benAddressID = :benAddressID") + int updateVanSerialNo(@Param("benAddressID") BigInteger benAddressID); + + @Query("SELECT benAddressID FROM MBeneficiaryaddress WHERE vanSerialNo =:vanSerialNo AND vanID =:vanID ") + BigInteger findIdByVanSerialNoAndVanID(@Param("vanSerialNo") BigInteger vanSerialNo, @Param("vanID") Integer vanID); + + @Query("SELECT a FROM MBeneficiaryaddress a WHERE a.vanSerialNo =:vanSerialNo AND a.vanID =:vanID ") + MBeneficiaryaddress getWithVanSerialNoVanID(@Param("vanSerialNo") BigInteger vanSerialNo, + @Param("vanID") Integer vanID); + + /** + * Get user location details from m_userservicerolemapping + */ + @Query(value = "SELECT " + + "ProviderServiceMapID, " + + "Blockid, " + + "Villageid, " + + "WorkingLocationID " + + "FROM db_iemr.m_userservicerolemapping " + + "WHERE UserID = :userId " + + "AND Deleted = false " + + "LIMIT 1", + nativeQuery = true) + List getUserLocation(@Param("userId") Integer userId); - @Query("SELECT a FROM MBeneficiaryaddress a WHERE a.vanSerialNo =:vanSerialNo AND a.vanID =:vanID ") - MBeneficiaryaddress getWithVanSerialNoVanID(@Param("vanSerialNo") BigInteger vanSerialNo, - @Param("vanID") Integer vanID); } diff --git a/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java b/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java index 2846bbba..58d32861 100644 --- a/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/BenMappingRepo.java @@ -143,26 +143,85 @@ Long getBeneficiaryCountsByVillageIDAndLastModifyDate(@Param("villageIDs") List< MBeneficiarymapping getWithVanSerialNoVanID(@Param("vanSerialNo") BigInteger vanSerialNo, @Param("vanID") Integer vanID); - @Query("SELECT b.benRegId, b.benMapId FROM MBeneficiarymapping b where benRegId is not null and deleted = false") - List getAllBeneficiaryIds(); - - /** + @Query("SELECT b.benRegId, b.benMapId FROM MBeneficiarymapping b WHERE benRegId IS NOT NULL AND deleted = false") + List getAllBeneficiaryIds(); + + /** * Get beneficiary IDs in batches for efficient processing - * @param offset Starting position - * @param limit Number of records to fetch */ - @Query(value = "SELECT benRegId, benMapId FROM MBeneficiarymapping " + - "WHERE deleted = false ORDER BY benRegId LIMIT :limit OFFSET :offset") + @Query(value = "SELECT BenRegId FROM i_beneficiarymapping " + + "WHERE Deleted = false ORDER BY BenRegId LIMIT :limit OFFSET :offset", + nativeQuery = true) List getBeneficiaryIdsBatch(@Param("offset") int offset, @Param("limit") int limit); /** * Count total non-deleted beneficiaries */ - @Query(value = "SELECT COUNT(*) FROM MBeneficiarymapping WHERE deleted = false") + @Query(value = "SELECT COUNT(*) FROM i_beneficiarymapping WHERE Deleted = false", nativeQuery = true) long countActiveBeneficiaries(); - - - @Query("SELECT m FROM MBeneficiarymapping m " + + /** + * COMPLETE DATA FETCH - Single query with all joins + * This is the key query that fetches everything needed for ES indexing + */ + @Query(value = "SELECT " + + "m.BenRegId, " + // 0 + "d.BeneficiaryRegID, " + // 1 + "d.FirstName, " + // 2 + "d.LastName, " + // 3 + "d.GenderID, " + // 4 + "g.GenderName, " + // 5 + "d.DOB, " + // 6 + "TIMESTAMPDIFF(YEAR, d.DOB, CURDATE()), " + // 7 - age + "d.FatherName, " + // 8 + "d.SpouseName, " + // 9 + "d.IsHIVPositive, " + // 10 + "m.CreatedBy, " + // 11 + "m.CreatedDate, " + // 12 + "UNIX_TIMESTAMP(m.LastModDate) * 1000, " + // 13 + "m.BenAccountID, " + // 14 + "contact.PreferredPhoneNum, " + // 15 + "h.HealthID, " + "h.HealthIDNumber, " + "fam.BenFamilyMapId, " + + "addr.CurrStateId, " + // 19 + "addr.CurrState, " + // 20 + "addr.CurrDistrictId, " + // 21 + "addr.CurrDistrict, " + // 22 + "addr.CurrSubDistrictId, " + // 23 + "addr.CurrSubDistrict, " + // 24 + "addr.CurrVillageId, " + // 25 + "addr.CurrVillage, " + // 26 + "addr.CurrPinCode, " + // 27 + "addr.CurrServicePointId, " + // 28 + "addr.CurrServicePoint, " + // 29 + "addr.ParkingPlaceID, " + // 30 + "addr.PermStateId, " + // 31 + "addr.PermState, " + // 32 + "addr.PermDistrictId, " + // 33 + "addr.PermDistrict, " + // 34 + "addr.PermSubDistrictId, " + // 35 + "addr.PermSubDistrict, " + // 36 + "addr.PermVillageId, " + // 37 + "addr.PermVillage " + // 38 + // "id.GovtIdentityNo, " + // 39 - Aadhar/Govt ID + // "id.IdentityNo " + // 40 - Another identity + "FROM i_beneficiarymapping m " + + "LEFT JOIN i_beneficiarydetails d ON m.BenDetailsId = d.BeneficiaryDetailsID " + + "LEFT JOIN db_iemr.m_gender g ON d.GenderID = g.GenderID " + + "LEFT JOIN i_beneficiaryaddress addr ON m.BenAddressId = addr.BenAddressID " + + "LEFT JOIN i_beneficiarycontacts contact ON m.BenContactsId = contact.BenContactsID " + + "LEFT JOIN db_iemr.m_benhealthidmapping h ON m.BenRegId = h.BeneficiaryRegID " + + "LEFT JOIN i_beneficiaryfamilymapping fam " + + " ON m.BenRegId = fam.AssociatedBenRegID " + + " AND fam.Deleted = false " + + "WHERE m.BenRegId IN :benRegIds " + + "AND m.Deleted = false", + nativeQuery = true) + + List findCompleteDataByBenRegIds(@Param("benRegIds") List benRegIds); + + /** + * Find with all details (JPA approach - for single beneficiary) + */ + @Query("SELECT m FROM MBeneficiarymapping m " + "LEFT JOIN FETCH m.mBeneficiarydetail " + "LEFT JOIN FETCH m.mBeneficiarycontact " + "LEFT JOIN FETCH m.mBeneficiaryaddress " + @@ -176,9 +235,10 @@ MBeneficiarymapping getWithVanSerialNoVanID(@Param("vanSerialNo") BigInteger van MBeneficiarymapping findByBenRegId(@Param("benRegId") BigInteger benRegId); /** - * Check if beneficiary exists by benRegId + * Check if beneficiary exists */ - @Query(value = "SELECT COUNT(*) > 0 FROM MBeneficiarymapping WHERE benRegId = :benRegId AND deleted = false") + @Query(value = "SELECT COUNT(*) > 0 FROM i_beneficiarymapping WHERE BenRegId = :benRegId AND Deleted = false", nativeQuery = true) boolean existsByBenRegId(@Param("benRegId") BigInteger benRegId); + } diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java index 2edb935d..1e6fabee 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java @@ -4,10 +4,13 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -18,22 +21,15 @@ import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; import com.iemr.common.identity.data.elasticsearch.ElasticsearchSyncJob; -import com.iemr.common.identity.dto.BenDetailDTO; -import com.iemr.common.identity.dto.BeneficiariesDTO; import com.iemr.common.identity.repo.elasticsearch.SyncJobRepo; -import org.springframework.beans.factory.annotation.Value; -/** - * Async service for Elasticsearch sync operations - * Runs sync jobs in background without blocking API calls - */ @Service public class AsyncElasticsearchSyncService { private static final Logger logger = LoggerFactory.getLogger(AsyncElasticsearchSyncService.class); private static final int BATCH_SIZE = 100; private static final int ES_BULK_SIZE = 50; - private static final int STATUS_UPDATE_FREQUENCY = 10; // Update status every 10 batches + private static final int STATUS_UPDATE_FREQUENCY = 10; @Autowired private ElasticsearchClient esClient; @@ -41,9 +37,6 @@ public class AsyncElasticsearchSyncService { @Autowired private TransactionalSyncWrapper transactionalWrapper; - @Autowired - private BeneficiaryDataService beneficiaryDataService; - @Autowired private OptimizedBeneficiaryDataService optimizedDataService; @@ -54,25 +47,20 @@ public class AsyncElasticsearchSyncService { private String beneficiaryIndex; /** - * Start async full sync job - * Runs in background thread from elasticsearchSyncExecutor pool + * Start async full sync job with COMPLETE 38+ field data */ @Async("elasticsearchSyncExecutor") public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { - logger.info("========================================"); - logger.info("Starting ASYNC full sync job: jobId={}", jobId); - logger.info("========================================"); + logger.info("Starting ASYNC full sync with COMPLETE data: jobId={}", jobId); ElasticsearchSyncJob job = syncJobRepository.findByJobId(jobId) .orElseThrow(() -> new RuntimeException("Job not found: " + jobId)); try { - // Update job status to RUNNING job.setStatus("RUNNING"); job.setStartedAt(new Timestamp(System.currentTimeMillis())); syncJobRepository.save(job); - // Get total count long totalCount = transactionalWrapper.countActiveBeneficiaries(); job.setTotalRecords(totalCount); syncJobRepository.save(job); @@ -99,54 +87,101 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { // Process in batches while (offset < totalCount) { try { + logger.info("=== BATCH {} START: offset={} ===", batchCounter + 1, offset); + + // Get beneficiary IDs + logger.debug("Calling getBeneficiaryIdsBatch(offset={}, limit={})", offset, BATCH_SIZE); List batchIds = transactionalWrapper.getBeneficiaryIdsBatch(offset, BATCH_SIZE); + + logger.info("Retrieved {} IDs from database", batchIds != null ? batchIds.size() : 0); if (batchIds == null || batchIds.isEmpty()) { + logger.warn("No more batches to process at offset {}", offset); break; } - // Extract benRegIds for batch fetch + // Debug: Log first few IDs + if (batchIds.size() > 0) { + logger.debug("First ID type: {}, value: {}", + batchIds.get(0)[0].getClass().getName(), + batchIds.get(0)[0]); + } + + // FIXED: Convert Long to BigInteger properly + logger.debug("Converting {} IDs to BigInteger", batchIds.size()); List benRegIds = batchIds.stream() - .map(arr -> (BigInteger) arr[0]) - .collect(java.util.stream.Collectors.toList()); + .map(arr -> toBigInteger(arr[0])) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + logger.info("Converted {} valid BigInteger IDs", benRegIds.size()); + + if (benRegIds.isEmpty()) { + logger.error("No valid IDs in batch at offset {} - ALL IDs FAILED CONVERSION", offset); + offset += BATCH_SIZE; + continue; + } + + // Debug: Log first few converted IDs + if (benRegIds.size() > 0) { + logger.debug("First converted BigInteger: {}", benRegIds.get(0)); + } + + logger.info("Fetching complete data for {} beneficiaries...", benRegIds.size()); - // Fetch ALL beneficiaries in this batch in ONE query - List benDTOs = optimizedDataService.getBeneficiariesBatch(benRegIds); + // *** CRITICAL: This returns COMPLETE BeneficiaryDocument with 38+ fields *** + // NO CONVERSION NEEDED - documents are ready to index! + List documents = optimizedDataService.getBeneficiariesBatch(benRegIds); - logger.debug("Fetched {} beneficiaries in single batch query", benDTOs.size()); + logger.info("✓ Fetched {} complete documents for batch at offset {}", documents.size(), offset); + + // Debug: Log first document details + if (!documents.isEmpty() && documents.get(0) != null) { + BeneficiaryDocument firstDoc = documents.get(0); + logger.info("Sample doc - benId: {}, name: {} {}, district: {}, state: {}", + firstDoc.getBenId(), + firstDoc.getFirstName(), + firstDoc.getLastName(), + firstDoc.getDistrictName(), + firstDoc.getStateName()); + } - // Process each beneficiary - for (BeneficiariesDTO benDTO : benDTOs) { + // Add documents directly to ES batch (NO CONVERSION!) + int docsAddedInThisBatch = 0; + for (BeneficiaryDocument doc : documents) { try { - if (benDTO != null) { - BeneficiaryDocument doc = convertToDocument(benDTO); - if (doc != null && doc.getBenId() != null) { - esBatch.add(doc); - - if (esBatch.size() >= ES_BULK_SIZE) { - int indexed = bulkIndexDocuments(esBatch); - successCount += indexed; - failureCount += (esBatch.size() - indexed); - processedCount += esBatch.size(); - esBatch.clear(); - } - } else { - failureCount++; - processedCount++; + if (doc != null && doc.getBenId() != null) { + esBatch.add(doc); + docsAddedInThisBatch++; + + // Bulk index when batch is full + if (esBatch.size() >= ES_BULK_SIZE) { + logger.info("ES batch full ({} docs), indexing now...", esBatch.size()); + int indexed = bulkIndexDocuments(esBatch); + successCount += indexed; + failureCount += (esBatch.size() - indexed); + processedCount += esBatch.size(); + + logger.info("✓ Indexed {}/{} documents successfully", indexed, esBatch.size()); + esBatch.clear(); } } else { + logger.warn("Skipping document - doc null: {}, benId null: {}", + doc == null, doc != null ? (doc.getBenId() == null) : "N/A"); failureCount++; processedCount++; } } catch (Exception e) { - logger.error("Error processing beneficiary: {}", e.getMessage()); + logger.error("Error processing single document: {}", e.getMessage(), e); failureCount++; processedCount++; } } - // Account for any beneficiaries that weren't returned - int notFetched = batchIds.size() - benDTOs.size(); + logger.info("Added {} documents to ES batch in this iteration", docsAddedInThisBatch); + + // Account for any beneficiaries not fetched + int notFetched = benRegIds.size() - documents.size(); if (notFetched > 0) { failureCount += notFetched; processedCount += notFetched; @@ -155,35 +190,43 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { offset += BATCH_SIZE; batchCounter++; + + logger.info("=== BATCH {} END: Processed={}, Success={}, Failed={} ===", + batchCounter, processedCount, successCount, failureCount); // Update job status periodically if (batchCounter % STATUS_UPDATE_FREQUENCY == 0) { + logger.info("Updating job progress (every {} batches)...", STATUS_UPDATE_FREQUENCY); updateJobProgress(job, processedCount, successCount, failureCount, offset, totalCount, startTime); } - // Small pause every 10 batches (not every 5) + // Small pause every 10 batches if (batchCounter % 10 == 0) { - Thread.sleep(500); // Reduced to 500ms + logger.debug("Pausing for 500ms after {} batches", batchCounter); + Thread.sleep(500); } } catch (Exception e) { - logger.error("Error processing batch at offset {}: {}", offset, e.getMessage()); + logger.error("!!! ERROR in batch at offset {}: {} !!!", offset, e.getMessage(), e); + logger.error("Exception type: {}", e.getClass().getName()); + logger.error("Stack trace:", e); - // Save current progress before potentially failing job.setCurrentOffset(offset); job.setProcessedRecords(processedCount); job.setSuccessCount(successCount); job.setFailureCount(failureCount); syncJobRepository.save(job); - // Wait before retrying + // Skip this batch and continue + offset += BATCH_SIZE; Thread.sleep(2000); } } // Index remaining documents if (!esBatch.isEmpty()) { + logger.info("Indexing final batch of {} documents", esBatch.size()); int indexed = bulkIndexDocuments(esBatch); successCount += indexed; failureCount += (esBatch.size() - indexed); @@ -205,12 +248,14 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { logger.info("========================================"); logger.info("Async sync job COMPLETED: jobId={}", jobId); - logger.info("Processed: {}, Success: {}, Failed: {}", processedCount, successCount, failureCount); + logger.info("Total: {}, Processed: {}, Success: {}, Failed: {}", + totalCount, processedCount, successCount, failureCount); + logger.info("All 38+ beneficiary fields synced to Elasticsearch!"); logger.info("========================================"); } catch (Exception e) { logger.error("========================================"); - logger.error("CRITICAL ERROR in async sync job: jobId={}, error={}", jobId, e.getMessage(), e); + logger.error("CRITICAL ERROR in async sync: jobId={}, error={}", jobId, e.getMessage(), e); logger.error("========================================"); job.setStatus("FAILED"); @@ -221,8 +266,36 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { } /** - * Update job progress with estimated time remaining + * Helper method to safely convert various numeric types to BigInteger + * CRITICAL: Native SQL queries return Long, not BigInteger */ + private BigInteger toBigInteger(Object value) { + if (value == null) { + logger.warn("Attempted to convert null value to BigInteger"); + return null; + } + + try { + if (value instanceof BigInteger) { + return (BigInteger) value; + } + if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } + if (value instanceof Integer) { + return BigInteger.valueOf(((Integer) value).longValue()); + } + if (value instanceof Number) { + return BigInteger.valueOf(((Number) value).longValue()); + } + return new BigInteger(value.toString()); + } catch (NumberFormatException e) { + logger.error("Cannot convert '{}' (type: {}) to BigInteger: {}", + value, value.getClass().getName(), e.getMessage()); + return null; + } + } + private void updateJobProgress(ElasticsearchSyncJob job, long processed, long success, long failure, int offset, long total, long startTime) { job.setProcessedRecords(processed); @@ -231,7 +304,7 @@ private void updateJobProgress(ElasticsearchSyncJob job, long processed, long su job.setCurrentOffset(offset); long elapsedTime = System.currentTimeMillis() - startTime; - double speed = processed / (elapsedTime / 1000.0); + double speed = elapsedTime > 0 ? processed / (elapsedTime / 1000.0) : 0; job.setProcessingSpeed(speed); if (speed > 0) { @@ -243,12 +316,10 @@ private void updateJobProgress(ElasticsearchSyncJob job, long processed, long su syncJobRepository.save(job); logger.info("Progress: {}/{} ({:.2f}%) - Speed: {:.2f} rec/sec - ETA: {} sec", - processed, total, (processed * 100.0) / total, speed, job.getEstimatedTimeRemaining()); + processed, total, (processed * 100.0) / total, speed, + job.getEstimatedTimeRemaining() != null ? job.getEstimatedTimeRemaining() : 0); } - /** - * Bulk index documents - */ private int bulkIndexDocuments(List documents) { if (documents == null || documents.isEmpty()) { return 0; @@ -276,59 +347,21 @@ private int bulkIndexDocuments(List documents) { for (BulkResponseItem item : result.items()) { if (item.error() == null) { successCount++; + } else { + logger.error("ES indexing error for doc {}: {}", + item.id(), item.error().reason()); } } } else { successCount = documents.size(); } + logger.debug("Bulk indexed {} documents successfully", successCount); return successCount; } catch (Exception e) { - logger.error("Error in bulk indexing: {}", e.getMessage()); + logger.error("Error in bulk indexing: {}", e.getMessage(), e); return 0; } } - - /** - * Convert DTO to Document - */ - private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { - if (dto == null) { - return null; - } - - try { - BeneficiaryDocument doc = new BeneficiaryDocument(); - - if (dto.getBenRegId() != null) { - BigInteger benRegId = (BigInteger) dto.getBenRegId(); - doc.setBenId(benRegId.toString()); - doc.setBenRegId(benRegId.longValue()); - } else if (dto.getBenId() != null) { - doc.setBenId(dto.getBenId().toString()); - if (dto.getBenId() instanceof BigInteger) { - doc.setBenRegId(((BigInteger) dto.getBenId()).longValue()); - } - } else { - return null; - } - - doc.setPhoneNum(dto.getPreferredPhoneNum()); - - if (dto.getBeneficiaryDetails() != null) { - BenDetailDTO benDetails = dto.getBeneficiaryDetails(); - doc.setFirstName(benDetails.getFirstName()); - doc.setLastName(benDetails.getLastName()); - doc.setAge(benDetails.getBeneficiaryAge()); - doc.setGender(benDetails.getGender()); - } - - return doc; - - } catch (Exception e) { - logger.error("Error converting DTO: {}", e.getMessage()); - return null; - } - } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java new file mode 100644 index 00000000..4e60354f --- /dev/null +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java @@ -0,0 +1,132 @@ +package com.iemr.common.identity.service.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.mapping.*; +import co.elastic.clients.elasticsearch.indices.CreateIndexRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class ElasticsearchIndexingService { + + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchIndexingService.class); + + @Autowired + private ElasticsearchClient esClient; + + @Autowired + private ElasticsearchSyncService syncService; + + @Value("${elasticsearch.index.beneficiary:beneficiary_index_v5}") + private String beneficiaryIndex; + + /** + * Create or recreate the Elasticsearch index with proper mapping + */ + public void createIndexWithMapping() throws Exception { + logger.info("Creating index with mapping: {}", beneficiaryIndex); + + // Delete existing index if it exists + if (esClient.indices().exists(e -> e.index(beneficiaryIndex)).value()) { + logger.warn("Index {} already exists, deleting...", beneficiaryIndex); + esClient.indices().delete(d -> d.index(beneficiaryIndex)); + } + + // Create index with mapping + TypeMapping mapping = TypeMapping.of(tm -> tm + .properties("benId", Property.of(p -> p.keyword(k -> k))) + .properties("benRegId", Property.of(p -> p.long_(l -> l))) + .properties("beneficiaryID", Property.of(p -> p.keyword(k -> k))) + .properties("firstName", Property.of(p -> p.text(t -> t + .fields("keyword", Property.of(fp -> fp.keyword(k -> k)))))) + .properties("lastName", Property.of(p -> p.text(t -> t + .fields("keyword", Property.of(fp -> fp.keyword(k -> k)))))) + .properties("genderID", Property.of(p -> p.integer(i -> i))) + .properties("genderName", Property.of(p -> p.keyword(k -> k))) + .properties("dOB", Property.of(p -> p.date(d -> d))) + .properties("age", Property.of(p -> p.integer(i -> i))) + .properties("phoneNum", Property.of(p -> p.keyword(k -> k))) + .properties("fatherName", Property.of(p -> p.text(t -> t + .fields("keyword", Property.of(fp -> fp.keyword(k -> k)))))) + .properties("spouseName", Property.of(p -> p.text(t -> t + .fields("keyword", Property.of(fp -> fp.keyword(k -> k)))))) + .properties("isHIVPos", Property.of(p -> p.keyword(k -> k))) + .properties("createdBy", Property.of(p -> p.keyword(k -> k))) + .properties("createdDate", Property.of(p -> p.date(d -> d))) + .properties("lastModDate", Property.of(p -> p.long_(l -> l))) + .properties("benAccountID", Property.of(p -> p.long_(l -> l))) + + // Health IDs + .properties("healthID", Property.of(p -> p.keyword(k -> k))) + .properties("abhaID", Property.of(p -> p.keyword(k -> k))) + .properties("familyID", Property.of(p -> p.keyword(k -> k))) + + // Current Address + .properties("stateID", Property.of(p -> p.integer(i -> i))) + .properties("stateName", Property.of(p -> p.keyword(k -> k))) + .properties("districtID", Property.of(p -> p.integer(i -> i))) + .properties("districtName", Property.of(p -> p.keyword(k -> k))) + .properties("blockID", Property.of(p -> p.integer(i -> i))) + .properties("blockName", Property.of(p -> p.keyword(k -> k))) + .properties("villageID", Property.of(p -> p.integer(i -> i))) + .properties("villageName", Property.of(p -> p.keyword(k -> k))) + .properties("pinCode", Property.of(p -> p.keyword(k -> k))) + .properties("servicePointID", Property.of(p -> p.integer(i -> i))) + .properties("servicePointName", Property.of(p -> p.keyword(k -> k))) + .properties("parkingPlaceID", Property.of(p -> p.integer(i -> i))) + + // Permanent Address + .properties("permStateID", Property.of(p -> p.integer(i -> i))) + .properties("permStateName", Property.of(p -> p.keyword(k -> k))) + .properties("permDistrictID", Property.of(p -> p.integer(i -> i))) + .properties("permDistrictName", Property.of(p -> p.keyword(k -> k))) + .properties("permBlockID", Property.of(p -> p.integer(i -> i))) + .properties("permBlockName", Property.of(p -> p.keyword(k -> k))) + .properties("permVillageID", Property.of(p -> p.integer(i -> i))) + .properties("permVillageName", Property.of(p -> p.keyword(k -> k))) + + // Identity + .properties("aadharNo", Property.of(p -> p.keyword(k -> k))) + .properties("govtIdentityNo", Property.of(p -> p.keyword(k -> k))) + ); + + esClient.indices().create(c -> c + .index(beneficiaryIndex) + .mappings(mapping) + ); + + logger.info("Index created successfully: {}", beneficiaryIndex); + } + + /** + * Index all beneficiaries - delegates to existing sync service + * This is much safer than loading all records at once + */ + public Map indexAllBeneficiaries() { + logger.info("Starting full indexing via sync service..."); + + try { + // Use the existing, battle-tested sync service + ElasticsearchSyncService.SyncResult result = syncService.syncAllBeneficiaries(); + + Map response = new HashMap<>(); + response.put("success", result.getSuccessCount()); + response.put("failed", result.getFailureCount()); + + return response; + + } catch (Exception e) { + logger.error("Error during indexing", e); + Map response = new HashMap<>(); + response.put("success", 0); + response.put("failed", 0); + return response; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index 49cf087d..ccd7d1e7 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -3,7 +3,10 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType; import co.elastic.clients.elasticsearch.core.SearchResponse; + import com.iemr.common.identity.dto.BeneficiariesESDTO; +import com.iemr.common.identity.repo.BenDetailRepo; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +18,7 @@ import java.util.*; import java.util.stream.Collectors; -import com.iemr.common.identity.repo.BenDetailRepo; +import com.iemr.common.identity.repo.BenAddressRepo; @Service public class ElasticsearchService { @@ -26,223 +29,372 @@ public class ElasticsearchService { private ElasticsearchClient esClient; @Autowired - private BenDetailRepo benRegRepository; + private BenDetailRepo benDetailRepo; + + @Autowired + private BenAddressRepo benAddressRepo; - @Value("${elasticsearch.index.beneficiary:beneficiary_index_v4}") + @Value("${elasticsearch.index.beneficiary:beneficiary_index_v5}") private String beneficiaryIndex; - public List> universalSearch(String query) { - logger.info("Universal ES search for: {}", query); - - try { - boolean isNumeric = query.matches("\\d+"); + /** + * Universal search with optional user location for ranking + */ + public List> universalSearch(String query, Integer userId) { + logger.info("Universal ES search for: {} with userId: {}", query, userId); - SearchResponse response = esClient.search(s -> s - .index(beneficiaryIndex) - .query(q -> q - .bool(b -> { - // Fuzzy multi-match for text fields - b.should(s1 -> s1.multiMatch(mm -> mm - .query(query) - .fields("firstName^3", "lastName^2", "fatherName", "spouseName") - .type(TextQueryType.BestFields) - .fuzziness("AUTO") - )); - - // Exact match for phone number - b.should(s2 -> s2.term(t -> t - .field("phoneNum") - .value(query) - )); - - // Numeric fields (only if query is numeric) - if (isNumeric) { - try { - Long numericValue = Long.parseLong(query); - b.should(s3 -> s3.term(t -> t.field("benRegId").value(numericValue))); - } catch (NumberFormatException e) { - logger.debug("Could not parse as long: {}", query); + try { + // Get user location if userId provided + Map userLocation = null; + if (userId != null) { + userLocation = getUserLocation(userId); + logger.info("User location: {}", userLocation); + } + + boolean isNumeric = query.matches("\\d+"); + + SearchResponse response = esClient.search(s -> s + .index(beneficiaryIndex) + .query(q -> q + .bool(b -> { + // Fuzzy multi-match for text fields + b.should(s1 -> s1.multiMatch(mm -> mm + .query(query) + .fields("firstName^3", "lastName^2", "fatherName", "spouseName") + .type(TextQueryType.BestFields) + .fuzziness("AUTO") + )); + + // Exact match for phone number + b.should(s2 -> s2.term(t -> t + .field("phoneNum") + .value(query) + )); + + // NEW: Search in healthID, abhaID, familyID + b.should(s3 -> s3.term(t -> t + .field("healthID") + .value(query) + )); + + b.should(s4 -> s4.term(t -> t + .field("abhaID") + .value(query) + )); + + b.should(s5 -> s5.term(t -> t + .field("familyID") + .value(query) + )); + + // Numeric fields (only if query is numeric) + if (isNumeric) { + try { + Long numericValue = Long.parseLong(query); + b.should(s6 -> s6.term(t -> t.field("benRegId").value(numericValue))); + } catch (NumberFormatException e) { + logger.debug("Could not parse as long: {}", query); + } + + b.should(s7 -> s7.term(t -> t.field("benId").value(query))); } - b.should(s4 -> s4.term(t -> t.field("benId").value(query))); - } - - b.minimumShouldMatch("1"); - return b; - }) - ) - .size(20) - , BeneficiariesESDTO.class); - - logger.info("ES returned {} hits", response.hits().hits().size()); - - // Convert ES results directly to response format - List> results = response.hits().hits().stream() - .map(hit -> mapESResultToExpectedFormat(hit.source())) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - if (results.isEmpty()) { - logger.info("No results found in ES, falling back to database"); + b.minimumShouldMatch("1"); + return b; + }) + ) + .size(100) // Increased to allow for location-based filtering + , BeneficiariesESDTO.class); + + logger.info("ES returned {} hits", response.hits().hits().size()); + + // Convert ES results + List> allResults = response.hits().hits().stream() + .map(hit -> mapESResultToExpectedFormat(hit.source())) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (allResults.isEmpty()) { + logger.info("No results found in ES, falling back to database"); + return searchInDatabaseDirectly(query); + } + + // Apply location-based ranking if user location available + if (userLocation != null) { + allResults = rankByLocation(allResults, userLocation); + } + + // Limit to top 20 results + List> results = allResults.stream() + .limit(20) + .collect(Collectors.toList()); + + logger.info("Returning {} results from ES", results.size()); + return results; + + } catch (Exception e) { + logger.error("ES universal search failed: {}", e.getMessage(), e); + logger.info("Fallback: Searching in MySQL database"); return searchInDatabaseDirectly(query); } - - logger.info("Returning {} results from ES", results.size()); - return results; - - } catch (Exception e) { - logger.error("ES universal search failed: {}", e.getMessage(), e); - logger.info("Fallback: Searching in MySQL database"); - return searchInDatabaseDirectly(query); } -} - -/** - * Map ES DTO directly to expected API format (NO DATABASE CALL) - */ -private Map mapESResultToExpectedFormat(BeneficiariesESDTO esData) { - if (esData == null) { - return null; + + /** + * Overloaded method without userId (backward compatibility) + */ + public List> universalSearch(String query) { + return universalSearch(query, null); } - Map result = new HashMap<>(); + /** + * Get user location from database + */ + private Map getUserLocation(Integer userId) { + try { + List results = benAddressRepo.getUserLocation(userId); + if (results != null && !results.isEmpty()) { + Object[] row = results.get(0); + Map location = new HashMap<>(); + location.put("psmId", getInteger(row[0])); + location.put("blockId", getInteger(row[1])); + location.put("villageId", getInteger(row[2])); + location.put("servicePointId", getInteger(row[3])); + return location; + } + } catch (Exception e) { + logger.error("Error fetching user location: {}", e.getMessage(), e); + } + return null; + } - try { - // Basic fields from ES - result.put("beneficiaryRegID", esData.getBenRegId()); - result.put("beneficiaryID", esData.getBeneficiaryID()); - result.put("firstName", esData.getFirstName()); - result.put("lastName", esData.getLastName()); - result.put("genderID", esData.getGenderID()); - result.put("genderName", esData.getGenderName()); - // result.put("dOB", esData.getdOB()); - result.put("dob", esData.getDOB()); - result.put("age", esData.getAge()); - result.put("actualAge", esData.getAge()); - result.put("ageUnits", "Years"); - result.put("fatherName", esData.getFatherName() != null ? esData.getFatherName() : ""); - result.put("spouseName", esData.getSpouseName() != null ? esData.getSpouseName() : ""); - result.put("isHIVPos", ""); - result.put("createdBy", esData.getCreatedBy()); - result.put("createdDate", esData.getCreatedDate()); - result.put("lastModDate", esData.getLastModDate()); - result.put("benAccountID", esData.getBenAccountID()); - - // Gender object - Map mGender = new HashMap<>(); - mGender.put("genderID", esData.getGenderID()); - mGender.put("genderName", esData.getGenderName()); - result.put("m_gender", mGender); - - // Demographics object from ES - Map demographics = new HashMap<>(); - demographics.put("beneficiaryRegID", esData.getBenRegId()); - demographics.put("stateID", esData.getStateID()); - demographics.put("stateName", esData.getStateName()); - demographics.put("districtID", esData.getDistrictID()); - demographics.put("districtName", esData.getDistrictName()); - demographics.put("blockID", esData.getBlockID()); - demographics.put("blockName", esData.getBlockName()); - demographics.put("districtBranchID", null); - demographics.put("districtBranchName", null); - demographics.put("parkingPlaceID", esData.getParkingPlaceID()); - demographics.put("servicePointID", esData.getServicePointID()); - demographics.put("servicePointName", esData.getServicePointName()); - demographics.put("createdBy", esData.getCreatedBy()); - - // Nested m_state - Map mState = new HashMap<>(); - mState.put("stateID", esData.getStateID()); - mState.put("stateName", esData.getStateName()); - mState.put("stateCode", null); - mState.put("countryID", 1); - demographics.put("m_state", mState); - - // Nested m_district - Map mDistrict = new HashMap<>(); - mDistrict.put("districtID", esData.getDistrictID()); - mDistrict.put("districtName", esData.getDistrictName()); - mDistrict.put("stateID", esData.getStateID()); - demographics.put("m_district", mDistrict); - - // Nested m_districtblock - Map mBlock = new HashMap<>(); - mBlock.put("blockID", esData.getBlockID()); - mBlock.put("blockName", esData.getBlockName()); - mBlock.put("districtID", esData.getDistrictID()); - mBlock.put("stateID", esData.getStateID()); - demographics.put("m_districtblock", mBlock); + /** + * Rank results by location match priority + * Priority: Village > Block > District > State > No Match + */ + private List> rankByLocation(List> results, + Map userLocation) { + Integer userBlockId = userLocation.get("blockId"); + Integer userVillageId = userLocation.get("villageId"); - // Nested m_districtbranchmapping - Map mBranch = new HashMap<>(); - mBranch.put("districtBranchID", null); - mBranch.put("blockID", esData.getBlockID()); - mBranch.put("villageName", null); - mBranch.put("pinCode", esData.getPinCode()); - demographics.put("m_districtbranchmapping", mBranch); + logger.info("Ranking by location - User Block: {}, Village: {}", userBlockId, userVillageId); - result.put("i_bendemographics", demographics); + return results.stream() + .sorted((r1, r2) -> { + int score1 = calculateLocationScore(r1, userBlockId, userVillageId); + int score2 = calculateLocationScore(r2, userBlockId, userVillageId); + return Integer.compare(score2, score1); // Higher score first + }) + .collect(Collectors.toList()); + } + + /** + * Calculate location match score + * Higher score = better match + */ + private int calculateLocationScore(Map beneficiary, + Integer userBlockId, + Integer userVillageId) { + int score = 0; - // Phone numbers from ES - List> benPhoneMaps = new ArrayList<>(); - if (esData.getPhoneNum() != null && !esData.getPhoneNum().isEmpty()) { - Map phoneMap = new HashMap<>(); - phoneMap.put("benPhMapID", 1L); - phoneMap.put("benificiaryRegID", esData.getBenRegId()); - phoneMap.put("parentBenRegID", esData.getBenRegId()); - phoneMap.put("benRelationshipID", 1); - phoneMap.put("phoneNo", esData.getPhoneNum()); - - Map relationType = new HashMap<>(); - relationType.put("benRelationshipID", 1); - relationType.put("benRelationshipType", "Self"); - phoneMap.put("benRelationshipType", relationType); - - benPhoneMaps.add(phoneMap); + try { + Map demographics = (Map) beneficiary.get("i_bendemographics"); + if (demographics == null) { + return score; + } + + // Check current address + Integer currBlockId = getIntegerFromMap(demographics, "blockID"); + Integer currVillageId = getIntegerFromMap(demographics, "m_districtblock", "blockID"); + + // Village match (highest priority) - score: 100 + if (userVillageId != null && userVillageId.equals(currVillageId)) { + score += 100; + } + + // Block match - score: 50 + if (userBlockId != null && userBlockId.equals(currBlockId)) { + score += 50; + } + + // Check permanent address as fallback + Integer permBlockId = getIntegerFromMap(beneficiary, "permBlockID"); + Integer permVillageId = getIntegerFromMap(beneficiary, "permVillageID"); + + if (userVillageId != null && userVillageId.equals(permVillageId)) { + score += 75; // Slightly lower than current village + } + + if (userBlockId != null && userBlockId.equals(permBlockId)) { + score += 25; // Lower than current block + } + + } catch (Exception e) { + logger.error("Error calculating location score: {}", e.getMessage()); } - result.put("benPhoneMaps", benPhoneMaps); - - // Default values - result.put("isConsent", false); - result.put("m_title", new HashMap<>()); - result.put("maritalStatus", new HashMap<>()); - result.put("changeInSelfDetails", false); - result.put("changeInAddress", false); - result.put("changeInContacts", false); - result.put("changeInIdentities", false); - result.put("changeInOtherDetails", false); - result.put("changeInFamilyDetails", false); - result.put("changeInAssociations", false); - result.put("changeInBankDetails", false); - result.put("changeInBenImage", false); - result.put("is1097", false); - result.put("emergencyRegistration", false); - result.put("passToNurse", false); - result.put("beneficiaryIdentities", new ArrayList<>()); - } catch (Exception e) { - logger.error("Error mapping ES result: {}", e.getMessage(), e); - return null; + return score; } - return result; -} + /** + * Helper to safely get Integer from nested maps + */ + private Integer getIntegerFromMap(Map map, String... keys) { + Object value = map; + for (String key : keys) { + if (value instanceof Map) { + value = ((Map) value).get(key); + } else { + return null; + } + } + return value instanceof Integer ? (Integer) value : null; + } /** - * Fetch complete beneficiary data from database using IDs from ES + * Map ES DTO directly to expected API format with COMPLETE data */ - private List> fetchCompleteDataFromDatabase(List beneficiaryIds) { + private Map mapESResultToExpectedFormat(BeneficiariesESDTO esData) { + if (esData == null) { + return null; + } + + Map result = new HashMap<>(); + try { - List results = benRegRepository.findCompleteDataByIds(beneficiaryIds); + // Basic fields from ES + result.put("beneficiaryRegID", esData.getBenRegId()); + result.put("beneficiaryID", esData.getBeneficiaryID()); + result.put("firstName", esData.getFirstName()); + result.put("lastName", esData.getLastName()); + result.put("genderID", esData.getGenderID()); + result.put("genderName", esData.getGenderName()); + result.put("dob", esData.getDOB()); + result.put("age", esData.getAge()); + result.put("actualAge", esData.getAge()); + result.put("ageUnits", "Years"); + result.put("fatherName", esData.getFatherName() != null ? esData.getFatherName() : ""); + result.put("spouseName", esData.getSpouseName() != null ? esData.getSpouseName() : ""); + result.put("isHIVPos", esData.getIsHIVPos() != null ? esData.getIsHIVPos() : ""); + result.put("createdBy", esData.getCreatedBy()); + result.put("createdDate", esData.getCreatedDate()); + result.put("lastModDate", esData.getLastModDate()); + result.put("benAccountID", esData.getBenAccountID()); - return results.stream() - .map(this::mapToExpectedFormat) - .collect(Collectors.toList()); + // Health IDs + result.put("healthID", esData.getHealthID()); + result.put("abhaID", esData.getAbhaID()); + result.put("familyID", esData.getFamilyID()); + + // Permanent address fields at root level + result.put("permStateID", esData.getPermStateID()); + result.put("permStateName", esData.getPermStateName()); + result.put("permDistrictID", esData.getPermDistrictID()); + result.put("permDistrictName", esData.getPermDistrictName()); + result.put("permBlockID", esData.getPermBlockID()); + result.put("permBlockName", esData.getPermBlockName()); + result.put("permVillageID", esData.getPermVillageID()); + result.put("permVillageName", esData.getPermVillageName()); + + // Gender object + Map mGender = new HashMap<>(); + mGender.put("genderID", esData.getGenderID()); + mGender.put("genderName", esData.getGenderName()); + result.put("m_gender", mGender); + + // Demographics object from ES with COMPLETE address data + Map demographics = new HashMap<>(); + demographics.put("beneficiaryRegID", esData.getBenRegId()); + demographics.put("stateID", esData.getStateID()); + demographics.put("stateName", esData.getStateName()); + demographics.put("districtID", esData.getDistrictID()); + demographics.put("districtName", esData.getDistrictName()); + demographics.put("blockID", esData.getBlockID()); + demographics.put("blockName", esData.getBlockName()); + demographics.put("villageID", esData.getVillageID()); + demographics.put("villageName", esData.getVillageName()); + demographics.put("districtBranchID", null); + demographics.put("districtBranchName", null); + demographics.put("parkingPlaceID", esData.getParkingPlaceID()); + demographics.put("servicePointID", esData.getServicePointID()); + demographics.put("servicePointName", esData.getServicePointName()); + demographics.put("createdBy", esData.getCreatedBy()); + + // Nested m_state + Map mState = new HashMap<>(); + mState.put("stateID", esData.getStateID()); + mState.put("stateName", esData.getStateName()); + mState.put("stateCode", null); + mState.put("countryID", 1); + demographics.put("m_state", mState); + + // Nested m_district + Map mDistrict = new HashMap<>(); + mDistrict.put("districtID", esData.getDistrictID()); + mDistrict.put("districtName", esData.getDistrictName()); + mDistrict.put("stateID", esData.getStateID()); + demographics.put("m_district", mDistrict); + + // Nested m_districtblock + Map mBlock = new HashMap<>(); + mBlock.put("blockID", esData.getBlockID()); + mBlock.put("blockName", esData.getBlockName()); + mBlock.put("districtID", esData.getDistrictID()); + mBlock.put("stateID", esData.getStateID()); + demographics.put("m_districtblock", mBlock); + + // Nested m_districtbranchmapping + Map mBranch = new HashMap<>(); + mBranch.put("districtBranchID", null); + mBranch.put("blockID", esData.getBlockID()); + mBranch.put("villageName", esData.getVillageName()); + mBranch.put("pinCode", esData.getPinCode()); + demographics.put("m_districtbranchmapping", mBranch); + + result.put("i_bendemographics", demographics); + + // Phone numbers from ES + List> benPhoneMaps = new ArrayList<>(); + if (esData.getPhoneNum() != null && !esData.getPhoneNum().isEmpty()) { + Map phoneMap = new HashMap<>(); + phoneMap.put("benPhMapID", 1L); + phoneMap.put("benificiaryRegID", esData.getBenRegId()); + phoneMap.put("parentBenRegID", esData.getBenRegId()); + phoneMap.put("benRelationshipID", 1); + phoneMap.put("phoneNo", esData.getPhoneNum()); + + Map relationType = new HashMap<>(); + relationType.put("benRelationshipID", 1); + relationType.put("benRelationshipType", "Self"); + phoneMap.put("benRelationshipType", relationType); + benPhoneMaps.add(phoneMap); + } + result.put("benPhoneMaps", benPhoneMaps); + + // Default values + result.put("isConsent", false); + result.put("m_title", new HashMap<>()); + result.put("maritalStatus", new HashMap<>()); + result.put("changeInSelfDetails", false); + result.put("changeInAddress", false); + result.put("changeInContacts", false); + result.put("changeInIdentities", false); + result.put("changeInOtherDetails", false); + result.put("changeInFamilyDetails", false); + result.put("changeInAssociations", false); + result.put("changeInBankDetails", false); + result.put("changeInBenImage", false); + result.put("is1097", false); + result.put("emergencyRegistration", false); + result.put("passToNurse", false); + result.put("beneficiaryIdentities", new ArrayList<>()); + } catch (Exception e) { - logger.error("Failed to fetch complete data from database: {}", e.getMessage(), e); - return Collections.emptyList(); + logger.error("Error mapping ES result: {}", e.getMessage(), e); + return null; } + + return result; } /** @@ -250,7 +402,7 @@ private List> fetchCompleteDataFromDatabase(List benef */ private List> searchInDatabaseDirectly(String query) { try { - List results = benRegRepository.searchBeneficiaries(query); + List results = benDetailRepo.searchBeneficiaries(query); return results.stream() .map(this::mapToExpectedFormat) @@ -334,7 +486,7 @@ private Map mapToExpectedFormat(Object[] row) { demographics.put("districtName", districtName); demographics.put("blockID", blockID); demographics.put("blockName", blockName); - demographics.put("districtBranchID", null); // Not in your tables + demographics.put("districtBranchID", null); demographics.put("districtBranchName", null); demographics.put("parkingPlaceID", parkingPlaceID); demographics.put("servicePointID", servicePointID); @@ -345,7 +497,7 @@ private Map mapToExpectedFormat(Object[] row) { Map mState = new HashMap<>(); mState.put("stateID", stateID); mState.put("stateName", stateName); - mState.put("stateCode", null); // Not available in your tables + mState.put("stateCode", null); mState.put("countryID", 1); demographics.put("m_state", mState); @@ -410,7 +562,7 @@ private List> fetchPhoneNumbers(Long beneficiaryRegID) { List> phoneList = new ArrayList<>(); try { - List phones = benRegRepository.findPhoneNumbersByBeneficiaryId(beneficiaryRegID); + List phones = benDetailRepo.findPhoneNumbersByBeneficiaryId(beneficiaryRegID); int mapId = 1; for (Object[] phone : phones) { diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java index 036214be..3517d4e2 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java @@ -4,116 +4,60 @@ import java.util.ArrayList; import java.util.List; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.iemr.common.identity.dto.BenDetailDTO; -import com.iemr.common.identity.dto.BeneficiariesDTO; +import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; +import com.iemr.common.identity.repo.BenMappingRepo; /** - * Optimized service to fetch beneficiary data in bulk - * Uses native SQL with joins for maximum performance + * Optimized service to fetch complete beneficiary data in bulk + * Uses the new complete data query from BenMappingRepo */ @Service public class OptimizedBeneficiaryDataService { private static final Logger logger = LoggerFactory.getLogger(OptimizedBeneficiaryDataService.class); - @PersistenceContext - private EntityManager entityManager; + @Autowired + private BenMappingRepo mappingRepo; /** - * Fetch multiple beneficiaries in one query using native SQL - * This is MUCH faster than fetching one by one + * Fetch multiple beneficiaries with COMPLETE data in ONE query + * This is the KEY method that replaces multiple individual queries */ @Transactional(readOnly = true, timeout = 30) - public List getBeneficiariesBatch(List benRegIds) { + public List getBeneficiariesBatch(List benRegIds) { if (benRegIds == null || benRegIds.isEmpty()) { return new ArrayList<>(); } try { - // CORRECTED: Use exact column names from your tables - String sql = - "SELECT " + - " m.BenRegId, " + - " m.BenMapId, " + - " d.FirstName, " + - " d.LastName, " + - " d.Gender, " + // Changed from Gender to GenderName - " d.DOB, " + - " c.PreferredPhoneNum " + - "FROM i_beneficiarymapping m " + - "LEFT JOIN i_beneficiarydetails d ON m.BenDetailsId = d.BeneficiaryDetailsId " + - "LEFT JOIN i_beneficiarycontacts c ON m.BenContactsId = c.BenContactsID " + - "WHERE m.BenRegId IN :benRegIds " + - " AND m.deleted = false"; - - Query query = entityManager.createNativeQuery(sql); - query.setParameter("benRegIds", benRegIds); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - - logger.info("Fetched {} beneficiary records from database", results.size()); + logger.debug("Fetching {} beneficiaries with complete data", benRegIds.size()); + + // Use the new complete query from repo + List results = mappingRepo.findCompleteDataByBenRegIds(benRegIds); + + logger.info("Fetched {} complete beneficiary records", results.size()); - List beneficiaries = new ArrayList<>(); + List documents = new ArrayList<>(); for (Object[] row : results) { try { - BeneficiariesDTO dto = new BeneficiariesDTO(); - - // BenRegId (column 0) - Handle both Long and BigInteger - if (row[0] != null) { - BigInteger benRegId = convertToBigInteger(row[0]); - dto.setBenRegId(benRegId); - dto.setBenId(benRegId); // Use benRegId as benId - } - - // BenMapId (column 1) - Handle both Long and BigInteger - if (row[1] != null) { - dto.setBenMapId(convertToBigInteger(row[1])); - } - - // Create BenDetailDTO - BenDetailDTO detailDTO = new BenDetailDTO(); - detailDTO.setFirstName(row[2] != null ? row[2].toString() : null); - detailDTO.setLastName(row[3] != null ? row[3].toString() : null); - detailDTO.setGender(row[4] != null ? row[4].toString() : null); - - // Calculate age from DOB (column 5) - if (row[5] != null) { - try { - java.sql.Timestamp dob = (java.sql.Timestamp) row[5]; - java.time.LocalDate birthDate = dob.toLocalDateTime().toLocalDate(); - java.time.LocalDate now = java.time.LocalDate.now(); - int age = java.time.Period.between(birthDate, now).getYears(); - detailDTO.setBeneficiaryAge(age); - } catch (Exception e) { - logger.debug("Error calculating age: {}", e.getMessage()); - } + BeneficiaryDocument doc = mapRowToDocument(row); + if (doc != null && doc.getBenId() != null) { + documents.add(doc); } - - dto.setBeneficiaryDetails(detailDTO); - - // Phone number (column 6) - dto.setPreferredPhoneNum(row[6] != null ? row[6].toString() : null); - - beneficiaries.add(dto); - } catch (Exception e) { - logger.error("Error parsing row: {}", e.getMessage(), e); + logger.error("Error mapping row to document: {}", e.getMessage(), e); } } - logger.debug("Converted {} beneficiaries to DTOs", beneficiaries.size()); - return beneficiaries; + logger.debug("Successfully converted {} beneficiaries to documents", documents.size()); + return documents; } catch (Exception e) { logger.error("Error fetching beneficiaries batch: {}", e.getMessage(), e); @@ -125,34 +69,135 @@ public List getBeneficiariesBatch(List benRegIds) * Single beneficiary fetch (for backward compatibility) */ @Transactional(readOnly = true, timeout = 10) - public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { + public BeneficiaryDocument getBeneficiaryFromDatabase(BigInteger benRegId) { List ids = new ArrayList<>(); ids.add(benRegId); - List results = getBeneficiariesBatch(ids); + List results = getBeneficiariesBatch(ids); return results.isEmpty() ? null : results.get(0); } /** - * Helper method to convert Object to BigInteger - * Handles both Long and BigInteger types from database + * Map database row to BeneficiaryDocument for ES + * Matches the query column order from BenMappingRepo */ - private BigInteger convertToBigInteger(Object value) { - if (value == null) { - return null; + private BeneficiaryDocument mapRowToDocument(Object[] row) { + BeneficiaryDocument doc = new BeneficiaryDocument(); + + try { + int idx = 0; + + // Basic IDs (0-1) + Long benRegId = getLong(row[idx++]); + doc.setBenRegId(benRegId); + String beneficiaryRegID = getString(row[idx++]); // Column 1: d.BeneficiaryRegID + + // doc.setBenId(benRegId != null ? benRegId.toString() : null); + if (beneficiaryRegID != null && !beneficiaryRegID.isEmpty()) { + doc.setBenId(beneficiaryRegID); + } else { + doc.setBenId(benRegId != null ? benRegId.toString() : null); + } + doc.setBeneficiaryID(beneficiaryRegID); + + // Personal Info (2-10) + doc.setFirstName(getString(row[idx++])); + doc.setLastName(getString(row[idx++])); + doc.setGenderID(getInteger(row[idx++])); + doc.setGenderName(getString(row[idx++])); + doc.setGender(doc.getGenderName()); // Use genderName for gender + doc.setDOB(getDate(row[idx++])); + doc.setAge(getInteger(row[idx++])); + doc.setFatherName(getString(row[idx++])); + doc.setSpouseName(getString(row[idx++])); + doc.setIsHIVPos(getString(row[idx++])); + + // Metadata (11-14) + doc.setCreatedBy(getString(row[idx++])); + doc.setCreatedDate(getDate(row[idx++])); + doc.setLastModDate(getLong(row[idx++])); + doc.setBenAccountID(getLong(row[idx++])); + + // Contact (15) + doc.setPhoneNum(getString(row[idx++])); + + // Health IDs (16-18) + doc.setHealthID(getString(row[idx++])); + doc.setAbhaID(getString(row[idx++])); + doc.setFamilyID(getString(row[idx++])); + + // Current Address (19-30) + doc.setStateID(getInteger(row[idx++])); + doc.setStateName(getString(row[idx++])); + doc.setDistrictID(getInteger(row[idx++])); + doc.setDistrictName(getString(row[idx++])); + doc.setBlockID(getInteger(row[idx++])); + doc.setBlockName(getString(row[idx++])); + doc.setVillageID(getInteger(row[idx++])); + doc.setVillageName(getString(row[idx++])); + doc.setPinCode(getString(row[idx++])); + doc.setServicePointID(getInteger(row[idx++])); + doc.setServicePointName(getString(row[idx++])); + doc.setParkingPlaceID(getInteger(row[idx++])); + + // Permanent Address (31-38) + doc.setPermStateID(getInteger(row[idx++])); + doc.setPermStateName(getString(row[idx++])); + doc.setPermDistrictID(getInteger(row[idx++])); + doc.setPermDistrictName(getString(row[idx++])); + doc.setPermBlockID(getInteger(row[idx++])); + doc.setPermBlockName(getString(row[idx++])); + doc.setPermVillageID(getInteger(row[idx++])); + doc.setPermVillageName(getString(row[idx++])); + + // Identity (39-40) + // doc.setGovtIdentityNo(getString(row[idx++])); + // String aadhar = getString(row[idx]); + // doc.setAadharNo(aadhar != null ? aadhar : doc.getGovtIdentityNo()); + + } catch (Exception e) { + logger.error("Error mapping row to document: {}", e.getMessage(), e); } - if (value instanceof BigInteger) { - return (BigInteger) value; - } else if (value instanceof Long) { - return BigInteger.valueOf((Long) value); - } else if (value instanceof Integer) { - return BigInteger.valueOf((Integer) value); - } else if (value instanceof String) { - return new BigInteger((String) value); - } else { - logger.warn("Unexpected type for BigInteger conversion: {}", value.getClass().getName()); - return BigInteger.valueOf(((Number) value).longValue()); + return doc; + } + + // Helper methods + private String getString(Object value) { + return value != null ? value.toString() : null; + } + + private Long getLong(Object value) { + if (value == null) return null; + if (value instanceof Long) return (Long) value; + if (value instanceof Integer) return ((Integer) value).longValue(); + if (value instanceof BigInteger) return ((BigInteger) value).longValue(); + try { + return Long.parseLong(value.toString()); + } catch (NumberFormatException e) { + return null; + } + } + + private Integer getInteger(Object value) { + if (value == null) return null; + if (value instanceof Integer) return (Integer) value; + if (value instanceof Long) return ((Long) value).intValue(); + if (value instanceof BigInteger) return ((BigInteger) value).intValue(); + try { + return Integer.parseInt(value.toString()); + } catch (NumberFormatException e) { + return null; } } + + private java.util.Date getDate(Object value) { + if (value == null) return null; + if (value instanceof java.util.Date) return (java.util.Date) value; + if (value instanceof java.sql.Timestamp) + return new java.util.Date(((java.sql.Timestamp) value).getTime()); + if (value instanceof java.sql.Date) + return new java.util.Date(((java.sql.Date) value).getTime()); + return null; + } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java index 07a5609d..a6d82599 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java @@ -14,8 +14,6 @@ import co.elastic.clients.elasticsearch.core.IndexRequest; import com.iemr.common.identity.data.elasticsearch.BeneficiaryDocument; -import com.iemr.common.identity.dto.BenDetailDTO; -import com.iemr.common.identity.dto.BeneficiariesDTO; /** * Service for real-time Elasticsearch synchronization @@ -52,19 +50,16 @@ public void syncBeneficiaryAsync(BigInteger benRegId) { try { logger.info("Starting async sync for benRegId: {}", benRegId); - // Fetch beneficiary from database - BeneficiariesDTO benDTO = dataService.getBeneficiaryFromDatabase(benRegId); + // Fetch beneficiary from database - returns BeneficiaryDocument directly + BeneficiaryDocument doc = dataService.getBeneficiaryFromDatabase(benRegId); - if (benDTO == null) { + if (doc == null) { logger.warn("Beneficiary not found in database: {}", benRegId); return; } - // Convert to Elasticsearch document - BeneficiaryDocument doc = convertToDocument(benDTO); - - if (doc == null || doc.getBenId() == null) { - logger.error("Failed to convert beneficiary to document: {}", benRegId); + if (doc.getBenId() == null) { + logger.error("BeneficiaryDocument has null benId: {}", benRegId); return; } @@ -86,46 +81,29 @@ public void syncBeneficiaryAsync(BigInteger benRegId) { } /** - * Convert DTO to Document + * Delete beneficiary from Elasticsearch */ - private BeneficiaryDocument convertToDocument(BeneficiariesDTO dto) { - if (dto == null) return null; - - try { - BeneficiaryDocument doc = new BeneficiaryDocument(); - - // IDs - doc.setBenId(dto.getBenId() != null ? dto.getBenId().toString() : null); - doc.setBenRegId(dto.getBenRegId() != null ? dto.getBenRegId().longValue() : null); - - // Phone - if (dto.getContacts() != null && !dto.getContacts().isEmpty()) { - doc.setPhoneNum(dto.getContacts().get(0).getPhoneNum()); - } else if (dto.getPreferredPhoneNum() != null) { - doc.setPhoneNum(dto.getPreferredPhoneNum()); + @Async("elasticsearchSyncExecutor") + public void deleteBeneficiaryAsync(String benId) { + if (!esEnabled) { + logger.debug("Elasticsearch is disabled, skipping delete"); + return; } - // Names - if (dto.getBeneficiaryDetails() != null) { - BenDetailDTO benDetails = dto.getBeneficiaryDetails(); - doc.setFirstName(benDetails.getFirstName()); - doc.setLastName(benDetails.getLastName()); - doc.setGender(benDetails.getGender()); - } + try { + logger.info("Starting async delete for benId: {}", benId); - // Age from DTO - doc.setAge(dto.getBeneficiaryAge()); + DeleteRequest request = DeleteRequest.of(d -> d + .index(beneficiaryIndex) + .id(benId) + ); - // You can add district, village if available - // doc.setDistrictName(...); - // doc.setVillageName(...); + esClient.delete(request); - return doc; + logger.info("Successfully deleted beneficiary from Elasticsearch: benId={}", benId); - } catch (Exception e) { - logger.error("Error converting DTO to document: {}", e.getMessage()); - return null; + } catch (Exception e) { + logger.error("Error deleting beneficiary {} from Elasticsearch: {}", benId, e.getMessage(), e); + } } -} - } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/utils/CookieUtil.java b/src/main/java/com/iemr/common/identity/utils/CookieUtil.java index 84f54526..dd4a02db 100644 --- a/src/main/java/com/iemr/common/identity/utils/CookieUtil.java +++ b/src/main/java/com/iemr/common/identity/utils/CookieUtil.java @@ -1,5 +1,6 @@ package com.iemr.common.identity.utils; +import java.util.Arrays; import java.util.Optional; import org.springframework.stereotype.Service; @@ -22,7 +23,15 @@ public Optional getCookieValue(HttpServletRequest request, String cookie return Optional.empty(); } - public String getJwtTokenFromCookie(HttpServletRequest request) { - return getCookieValue(request, "Jwttoken").orElse(null); + public static String getJwtTokenFromCookie(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; // No cookies present, return null safely + } + return Arrays.stream(cookies).filter(cookie -> "Jwttoken".equals(cookie.getName())).map(Cookie::getValue) + .findFirst().orElse(null); } + } + + diff --git a/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java index 6d5c55f9..1156daa1 100644 --- a/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java @@ -162,6 +162,7 @@ private boolean isMobileClient(String userAgent) { return userAgent.contains("okhttp") || userAgent.contains("java/"); // iOS (custom clients) } + @SuppressWarnings("static-access") private String getJwtTokenFromCookies(HttpServletRequest request) { return cookieUtil.getJwtTokenFromCookie(request); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3e509531..7813571c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -99,7 +99,7 @@ elasticsearch.host=localhost elasticsearch.port=9200 elasticsearch.username=elastic elasticsearch.password=piramalES -elasticsearch.index.beneficiary=beneficiary_index_v4 +elasticsearch.index.beneficiary=beneficiary_index_v5 # Enable/Disable ES (for gradual rollout) elasticsearch.enabled=true From 258a5ee709b05a8a90235a6a9b130bd4a0901e64 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Mon, 15 Dec 2025 14:39:30 +0530 Subject: [PATCH 05/13] fix: comment extra fields --- .../elasticsearch/ElasticsearchService.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index ccd7d1e7..9e5ac270 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -270,11 +270,11 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat result.put("genderName", esData.getGenderName()); result.put("dob", esData.getDOB()); result.put("age", esData.getAge()); - result.put("actualAge", esData.getAge()); - result.put("ageUnits", "Years"); + // result.put("actualAge", esData.getAge()); + // result.put("ageUnits", "Years"); result.put("fatherName", esData.getFatherName() != null ? esData.getFatherName() : ""); result.put("spouseName", esData.getSpouseName() != null ? esData.getSpouseName() : ""); - result.put("isHIVPos", esData.getIsHIVPos() != null ? esData.getIsHIVPos() : ""); + // result.put("isHIVPos", esData.getIsHIVPos() != null ? esData.getIsHIVPos() : ""); result.put("createdBy", esData.getCreatedBy()); result.put("createdDate", esData.getCreatedDate()); result.put("lastModDate", esData.getLastModDate()); @@ -286,14 +286,14 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat result.put("familyID", esData.getFamilyID()); // Permanent address fields at root level - result.put("permStateID", esData.getPermStateID()); - result.put("permStateName", esData.getPermStateName()); - result.put("permDistrictID", esData.getPermDistrictID()); - result.put("permDistrictName", esData.getPermDistrictName()); - result.put("permBlockID", esData.getPermBlockID()); - result.put("permBlockName", esData.getPermBlockName()); - result.put("permVillageID", esData.getPermVillageID()); - result.put("permVillageName", esData.getPermVillageName()); + // result.put("permStateID", esData.getPermStateID()); + // result.put("permStateName", esData.getPermStateName()); + // result.put("permDistrictID", esData.getPermDistrictID()); + // result.put("permDistrictName", esData.getPermDistrictName()); + // result.put("permBlockID", esData.getPermBlockID()); + // result.put("permBlockName", esData.getPermBlockName()); + // result.put("permVillageID", esData.getPermVillageID()); + // result.put("permVillageName", esData.getPermVillageName()); // Gender object Map mGender = new HashMap<>(); @@ -461,11 +461,11 @@ private Map mapToExpectedFormat(Object[] row) { result.put("dOB", dob); result.put("dob", dob); result.put("age", age); - result.put("actualAge", age); - result.put("ageUnits", "Years"); + // result.put("actualAge", age); + // result.put("ageUnits", "Years"); result.put("fatherName", fatherName != null ? fatherName : ""); result.put("spouseName", spouseName != null ? spouseName : ""); - result.put("isHIVPos", isHIVPos != null ? isHIVPos : ""); + // result.put("isHIVPos", isHIVPos != null ? isHIVPos : ""); result.put("createdBy", createdBy); result.put("createdDate", createdDate); result.put("lastModDate", lastModDate); From 2944857c431c0ca70795225031f7f3dfb7468bb4 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Tue, 16 Dec 2025 16:25:01 +0530 Subject: [PATCH 06/13] fix: rename the files, remove commented code --- ...nfig.java => ElasticsearchSyncConfig.java} | 2 +- .../controller/IdentityESController.java | 5 +- .../ElasticsearchSyncController.java | 32 +- .../identity/mapper/BeneficiaryESMapper.java | 7 - .../identity/service/IdentityService.java | 3759 ++++++++--------- .../elasticsearch/BeneficiaryDataService.java | 15 +- ...va => BeneficiaryDocumentDataService.java} | 14 +- ...BeneficiaryElasticsearchIndexService.java} | 31 +- ...BeneficiaryElasticsearchIndexUpdater.java} | 8 +- ...java => BeneficiaryTransactionHelper.java} | 4 +- .../ElasticsearchIndexingService.java | 7 +- .../elasticsearch/ElasticsearchService.java | 48 +- .../ElasticsearchSyncService.java | 15 +- .../service/elasticsearch/SyncJobService.java | 6 +- 14 files changed, 1915 insertions(+), 2038 deletions(-) rename src/main/java/com/iemr/common/identity/config/{AsyncConfig.java => ElasticsearchSyncConfig.java} (97%) rename src/main/java/com/iemr/common/identity/service/elasticsearch/{OptimizedBeneficiaryDataService.java => BeneficiaryDocumentDataService.java} (94%) rename src/main/java/com/iemr/common/identity/service/elasticsearch/{AsyncElasticsearchSyncService.java => BeneficiaryElasticsearchIndexService.java} (89%) rename src/main/java/com/iemr/common/identity/service/elasticsearch/{RealtimeElasticsearchSyncService.java => BeneficiaryElasticsearchIndexUpdater.java} (92%) rename src/main/java/com/iemr/common/identity/service/elasticsearch/{TransactionalSyncWrapper.java => BeneficiaryTransactionHelper.java} (96%) diff --git a/src/main/java/com/iemr/common/identity/config/AsyncConfig.java b/src/main/java/com/iemr/common/identity/config/ElasticsearchSyncConfig.java similarity index 97% rename from src/main/java/com/iemr/common/identity/config/AsyncConfig.java rename to src/main/java/com/iemr/common/identity/config/ElasticsearchSyncConfig.java index e84cd1c4..bb72f91a 100644 --- a/src/main/java/com/iemr/common/identity/config/AsyncConfig.java +++ b/src/main/java/com/iemr/common/identity/config/ElasticsearchSyncConfig.java @@ -14,7 +14,7 @@ @Configuration @EnableAsync @EnableScheduling -public class AsyncConfig { +public class ElasticsearchSyncConfig { /** * Thread pool for Elasticsearch sync operations diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java index f445aaa5..4e4fbedf 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityESController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityESController.java @@ -35,9 +35,8 @@ public class IdentityESController { * MAIN UNIVERSAL SEARCH ENDPOINT * Searches across all fields - name, phone, ID, etc. * - * Usage: GET /beneficiary/search?q=vani - * Usage: GET /beneficiary/search?q=9876543210 - * Usage: GET /beneficiary/search?q=rajesh kumar + * Usage: GET /beneficiary/search?query=vani + * Usage: GET /beneficiary/search?query=9876543210 */ @GetMapping("/search") public ResponseEntity> search(@RequestParam String query, HttpServletRequest request) { diff --git a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java index 127a8fa5..075e7239 100644 --- a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java +++ b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java @@ -45,9 +45,9 @@ public class ElasticsearchSyncController { * Start async full sync (RECOMMENDED for millions of records) * Returns immediately with job ID for tracking * - * Usage: POST http://localhost:8080/elasticsearch/sync/async/start + * Usage: POST http://localhost:8080/elasticsearch/sync/start */ - @PostMapping("/async/start") + @PostMapping("/start") public ResponseEntity> startAsyncFullSync( @RequestParam(required = false, defaultValue = "API") String triggeredBy) { @@ -62,7 +62,7 @@ public ResponseEntity> startAsyncFullSync( response.put("message", "Sync job started in background"); response.put("jobId", job.getJobId()); response.put("jobStatus", job.getStatus()); - response.put("checkStatusUrl", "/elasticsearch/async/status/" + job.getJobId()); + response.put("checkStatusUrl", "/elasticsearch/status/" + job.getJobId()); return ResponseEntity.ok(response); @@ -83,9 +83,9 @@ public ResponseEntity> startAsyncFullSync( /** * Get job status by ID * - * Usage: GET http://localhost:8080/elasticsearch/sync/async/status/1 + * Usage: GET http://localhost:8080/elasticsearch/sync/status/1 */ - @GetMapping("/async/status/{jobId}") + @GetMapping("/status/{jobId}") public ResponseEntity> getAsyncJobStatus(@PathVariable Long jobId) { logger.info("Checking status for job: {}", jobId); @@ -120,9 +120,9 @@ public ResponseEntity> getAsyncJobStatus(@PathVariable Long /** * Get all active jobs * - * Usage: GET http://localhost:8080/elasticsearch/sync/async/active + * Usage: GET http://localhost:8080/elasticsearch/sync/active */ - @GetMapping("/async/active") + @GetMapping("/active") public ResponseEntity> getActiveJobs() { logger.info("Fetching active jobs"); return ResponseEntity.ok(syncJobService.getActiveJobs()); @@ -131,9 +131,9 @@ public ResponseEntity> getActiveJobs() { /** * Get recent jobs * - * Usage: GET http://localhost:8080/elasticsearch/sync/async/recent + * Usage: GET http://localhost:8080/elasticsearch/sync/recent */ - @GetMapping("/async/recent") + @GetMapping("/recent") public ResponseEntity> getRecentJobs() { logger.info("Fetching recent jobs"); return ResponseEntity.ok(syncJobService.getRecentJobs()); @@ -142,9 +142,9 @@ public ResponseEntity> getRecentJobs() { /** * Resume a failed job * - * Usage: POST http://localhost:8080/elasticsearch/sync/async/resume/1 + * Usage: POST http://localhost:8080/elasticsearch/sync/resume/1 */ - @PostMapping("/async/resume/{jobId}") + @PostMapping("/resume/{jobId}") public ResponseEntity> resumeJob( @PathVariable Long jobId, @RequestParam(required = false, defaultValue = "API") String triggeredBy) { @@ -173,9 +173,9 @@ public ResponseEntity> resumeJob( /** * Cancel a running job * - * Usage: POST http://localhost:8080/elasticsearch/sync/async/cancel/1 + * Usage: POST http://localhost:8080/elasticsearch/sync/cancel/1 */ - @PostMapping("/async/cancel/{jobId}") + @PostMapping("/cancel/{jobId}") public ResponseEntity> cancelJob(@PathVariable Long jobId) { logger.info("Cancelling job: {}", jobId); @@ -195,13 +195,13 @@ public ResponseEntity> cancelJob(@PathVariable Long jobId) { /** * LEGACY: Synchronous full sync (NOT recommended for large datasets) - * Use /async/start instead + * Use /start instead * * Usage: POST http://localhost:8080/elasticsearch/sync/all */ @PostMapping("/all") public ResponseEntity> syncAllBeneficiaries() { - logger.warn("LEGACY sync endpoint called. Consider using /async/start instead."); + logger.warn("LEGACY sync endpoint called. Consider using /start instead."); logger.info("Received request to sync all beneficiaries (BLOCKING)"); Map response = new HashMap<>(); @@ -213,7 +213,7 @@ public ResponseEntity> syncAllBeneficiaries() { response.put("successCount", result.getSuccessCount()); response.put("failureCount", result.getFailureCount()); response.put("error", result.getError()); - response.put("warning", "This is a blocking operation. For large datasets, use /async/start"); + response.put("warning", "This is a blocking operation. For large datasets, use /start"); if (result.getError() != null) { return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(response); diff --git a/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java b/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java index 3233aa74..4cec8d2e 100644 --- a/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java +++ b/src/main/java/com/iemr/common/identity/mapper/BeneficiaryESMapper.java @@ -16,7 +16,6 @@ public Map transformESResponse(List> esResul for (Map esDoc : esResults) { Map beneficiary = new HashMap<>(); - // Basic beneficiary fields beneficiary.put("beneficiaryRegID", esDoc.get("beneficiaryRegID")); beneficiary.put("beneficiaryID", esDoc.get("beneficiaryID")); beneficiary.put("firstName", esDoc.get("firstName")); @@ -36,19 +35,16 @@ public Map transformESResponse(List> esResul beneficiary.put("lastModDate", esDoc.get("lastModDate")); beneficiary.put("benAccountID", esDoc.get("benAccountID")); - // Gender object Map mGender = new HashMap<>(); mGender.put("genderID", esDoc.get("genderID")); mGender.put("genderName", esDoc.get("genderName")); beneficiary.put("m_gender", mGender); - // Demographics Map demographics = (Map) esDoc.get("demographics"); if (demographics != null) { Map benDemographics = new HashMap<>(demographics); benDemographics.put("beneficiaryRegID", esDoc.get("beneficiaryRegID")); - // Add nested objects for demographics benDemographics.put("m_state", createStateObject(demographics)); benDemographics.put("m_district", createDistrictObject(demographics)); benDemographics.put("m_districtblock", createBlockObject(demographics)); @@ -57,7 +53,6 @@ public Map transformESResponse(List> esResul beneficiary.put("i_bendemographics", benDemographics); } - // Phone mappings List> phoneMaps = new ArrayList<>(); List> phoneNumbers = (List>) esDoc.get("phoneNumbers"); if (phoneNumbers != null && !phoneNumbers.isEmpty()) { @@ -65,7 +60,6 @@ public Map transformESResponse(List> esResul Map phoneMap = new HashMap<>(phone); phoneMap.put("benificiaryRegID", esDoc.get("beneficiaryRegID")); - // Add relationship type object Map relationType = new HashMap<>(); relationType.put("benRelationshipID", phone.get("benRelationshipID")); relationType.put("benRelationshipType", phone.get("benRelationshipType")); @@ -76,7 +70,6 @@ public Map transformESResponse(List> esResul } beneficiary.put("benPhoneMaps", phoneMaps); - // Default values beneficiary.put("isConsent", false); beneficiary.put("m_title", new HashMap<>()); beneficiary.put("maritalStatus", new HashMap<>()); diff --git a/src/main/java/com/iemr/common/identity/service/IdentityService.java b/src/main/java/com/iemr/common/identity/service/IdentityService.java index e0dc9c61..a9051624 100644 --- a/src/main/java/com/iemr/common/identity/service/IdentityService.java +++ b/src/main/java/com/iemr/common/identity/service/IdentityService.java @@ -18,7 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/. -*/ + */ package com.iemr.common.identity.service; import java.math.BigDecimal; @@ -96,934 +96,847 @@ import org.springframework.beans.factory.annotation.Value; -import com.iemr.common.identity.service.elasticsearch.RealtimeElasticsearchSyncService; +import com.iemr.common.identity.service.elasticsearch.BeneficiaryElasticsearchIndexUpdater; import jakarta.persistence.NoResultException; import jakarta.persistence.QueryTimeoutException; @Service public class IdentityService { - private static final Logger logger = LoggerFactory.getLogger(IdentityService.class); - public static final String CREATED_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; - @Autowired - private DataSource dataSource; - private JdbcTemplate jdbcTemplate; + private static final Logger logger = LoggerFactory.getLogger(IdentityService.class); + public static final String CREATED_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; + @Autowired + private DataSource dataSource; + + private JdbcTemplate jdbcTemplate; - private JdbcTemplate getJdbcTemplate() { - return new JdbcTemplate(dataSource); + private JdbcTemplate getJdbcTemplate() { + return new JdbcTemplate(dataSource); - } + } - @Autowired - private RMNCHBeneficiaryDetailsRmnchRepo rMNCHBeneficiaryDetailsRmnchRepo; + @Autowired + private RMNCHBeneficiaryDetailsRmnchRepo rMNCHBeneficiaryDetailsRmnchRepo; - @Autowired + @Autowired private ElasticsearchService elasticsearchService; - @Autowired - private RealtimeElasticsearchSyncService realtimeSyncService; - - @Autowired - IdentityMapper mapper; - @Autowired - IdentityEditMapper editMapper; - @Autowired - IdentitySearchMapper searchMapper; - @Autowired - private IdentityPartialMapper partialMapper; - @Autowired - BenAddressRepo addressRepo; - @Autowired - BenConsentRepo consentRepo; - @Autowired - BenContactRepo contactRepo; - @Autowired - BenDataAccessRepo accessRepo; - @Autowired - BenDetailRepo detailRepo; - @Autowired - BenFamilyMappingRepo familyMapRepo; - @Autowired - BenIdentityRepo identityRepo; - @Autowired - BenMappingRepo mappingRepo; - @Autowired - BenRegIdMappingRepo regIdRepo; - @Autowired - BenServiceMappingRepo serviceMapRepo; - @Autowired - MBeneficiaryAccountRepo accountRepo; - @Autowired - MBeneficiaryImageRepo imageRepo; - @Autowired - private BenIdImportMapper benIdImportMapper; - @Autowired - private V_BenAdvanceSearchRepo v_BenAdvanceSearchRepo; - - @Value("${elasticsearch.enabled}") + @Autowired + private BeneficiaryElasticsearchIndexUpdater syncService; + + @Autowired + IdentityMapper mapper; + @Autowired + IdentityEditMapper editMapper; + @Autowired + IdentitySearchMapper searchMapper; + @Autowired + private IdentityPartialMapper partialMapper; + @Autowired + BenAddressRepo addressRepo; + @Autowired + BenConsentRepo consentRepo; + @Autowired + BenContactRepo contactRepo; + @Autowired + BenDataAccessRepo accessRepo; + @Autowired + BenDetailRepo detailRepo; + @Autowired + BenFamilyMappingRepo familyMapRepo; + @Autowired + BenIdentityRepo identityRepo; + @Autowired + BenMappingRepo mappingRepo; + @Autowired + BenRegIdMappingRepo regIdRepo; + @Autowired + BenServiceMappingRepo serviceMapRepo; + @Autowired + MBeneficiaryAccountRepo accountRepo; + @Autowired + MBeneficiaryImageRepo imageRepo; + @Autowired + private BenIdImportMapper benIdImportMapper; + @Autowired + private V_BenAdvanceSearchRepo v_BenAdvanceSearchRepo; + + @Value("${elasticsearch.enabled}") private boolean esEnabled; - public void getBenAdress() { - logger.debug("Address count: " + addressRepo.count()); - logger.debug( - "Address By Address: " + addressRepo.findByBenAddressIDOrderByBenAddressIDAsc(new BigInteger("12"))); - logger.debug("Consent count: " + consentRepo.count()); - logger.debug("Consent By Id: " + consentRepo.findByBenConsentIDOrderByBenConsentIDAsc(new BigInteger("12"))); - logger.debug("Contact count: " + contactRepo.count()); - logger.debug("Contact By Id: " + contactRepo.findByBenContactsIDOrderByBenContactsIDAsc(new BigInteger("12"))); - logger.debug("Data Access count: " + accessRepo.count()); - logger.debug("Data Access By Id: " + accessRepo.findByAccessIdOrderByAccessIdAsc(new BigInteger("12"))); - logger.debug("Detail count: " + detailRepo.count()); - logger.debug("Detail By Address: " - + detailRepo.findByBeneficiaryDetailsIdOrderByBeneficiaryDetailsIdAsc(new BigInteger("12"))); - logger.debug("FamilyMap count: " + familyMapRepo.count()); - logger.debug( - "FamilyMap By Id: " + familyMapRepo.findByBenFamilyMapIdOrderByBenFamilyMapIdAsc(new BigInteger("12"))); - logger.debug("Identity count: " + identityRepo.count()); - logger.debug("Identity By Id: " + identityRepo.findByBenIdentityId(new BigInteger("12"))); - logger.debug("Mapping count: " + mappingRepo.count()); - logger.debug("Mapping By Id: " + mappingRepo.findByBenMapIdOrderByBenMapIdAsc(new BigInteger("12"))); - } - - /** - * - * @param searchDTO - * @return - */ - public List getBeneficiaries(IdentitySearchDTO searchDTO) - throws NoResultException, QueryTimeoutException, Exception { - List list = new ArrayList(); - - /** - * if beneficiary Id present - */ - if (searchDTO.getBeneficiaryId() != null) { - logger.info("getting beneficiaries by ID for " + searchDTO.getBeneficiaryId()); - return this.getBeneficiariesByBenId(searchDTO.getBeneficiaryId()); - } - - /** - * if beneficiary Reg Id present - */ - if (searchDTO.getBeneficiaryRegId() != null) { - logger.info("getting beneficiaries by reg ID for " + searchDTO.getBeneficiaryRegId()); - return this.getBeneficiariesByBenRegId(searchDTO.getBeneficiaryRegId()); - } - - /** - * if beneficiary Reg Id present - */ - if (searchDTO.getContactNumber() != null) { - logger.info("getting beneficiaries by contact no for " + searchDTO.getContactNumber()); - List list3 = this.getBeneficiariesByPhoneNum(searchDTO.getContactNumber()); - if (!list3.isEmpty() && searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { - - for (int i = 0; i < list3.size(); i++) { - if (searchDTO.getFirstName() != null) { - if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null - || list3.get(i).getBeneficiaryDetails().getFirstName() == null - || !list3.get(i).getBeneficiaryDetails().getFirstName() - .equalsIgnoreCase(searchDTO.getFirstName())) { - list3.remove(i); - i--; - - continue; - } - - } - if (searchDTO.getLastName() != null) { - if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null - || list3.get(i).getBeneficiaryDetails().getLastName() == null - || !list3.get(i).getBeneficiaryDetails().getLastName() - .equalsIgnoreCase(searchDTO.getLastName())) { - list3.remove(i); - i--; - - continue; - } - } - - if (searchDTO.getGenderId() != null) { - if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null - || list3.get(i).getBeneficiaryDetails().getGenderId() == null || !list3.get(i) - .getBeneficiaryDetails().getGenderId().equals(searchDTO.getGenderId())) { - - list3.remove(i); - i--; - continue; - } - } - if (searchDTO.getHouseHoldID() != null) { - if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null - || list3.get(i).getBeneficiaryDetails().getHouseHoldID() == null || !list3.get(i) - .getBeneficiaryDetails().getHouseHoldID().equals(searchDTO.getHouseHoldID())) { - - list3.remove(i); - i--; - continue; - } - } - - if (searchDTO.getCurrentAddress().getStateId() != null) { - if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null - || list3.get(i).getCurrentAddress().getStateId() == null - || !list3.get(i).getCurrentAddress().getStateId() - .equals(searchDTO.getCurrentAddress().getStateId())) { - - list3.remove(i); - i--; - continue; - } - } - - if (searchDTO.getCurrentAddress().getDistrictId() != null) { - if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null - || list3.get(i).getCurrentAddress().getDistrictId() == null - || !list3.get(i).getCurrentAddress().getDistrictId() - .equals(searchDTO.getCurrentAddress().getDistrictId())) { - - list3.remove(i); - i--; - continue; - } - } - - if (searchDTO.getCurrentAddress().getVillageId() != null) { - if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null - || list3.get(i).getCurrentAddress().getVillageId() == null - || !list3.get(i).getCurrentAddress().getVillageId() - .equals(searchDTO.getCurrentAddress().getVillageId())) { - - list3.remove(i); - i--; - continue; - } - } - - } - } - return list3; - } - - /** - * New logic for advance search, 03-10-2018 - */ - List tmpList = mappingRepo.dynamicFilterSearchNew(searchDTO); - for (VBenAdvanceSearch obj : tmpList) { - list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew1(obj))); - logger.debug("benMapId: " + obj.getBenMapID()); - } - /** - * End - */ - - return list; - } - - - - public List getBeneficiarieswithES(IdentitySearchDTO searchDTO) - throws NoResultException, QueryTimeoutException, Exception { - List list = new ArrayList(); + public void getBenAdress() { + logger.debug("Address count: " + addressRepo.count()); + logger.debug( + "Address By Address: " + addressRepo.findByBenAddressIDOrderByBenAddressIDAsc(new BigInteger("12"))); + logger.debug("Consent count: " + consentRepo.count()); + logger.debug("Consent By Id: " + consentRepo.findByBenConsentIDOrderByBenConsentIDAsc(new BigInteger("12"))); + logger.debug("Contact count: " + contactRepo.count()); + logger.debug("Contact By Id: " + contactRepo.findByBenContactsIDOrderByBenContactsIDAsc(new BigInteger("12"))); + logger.debug("Data Access count: " + accessRepo.count()); + logger.debug("Data Access By Id: " + accessRepo.findByAccessIdOrderByAccessIdAsc(new BigInteger("12"))); + logger.debug("Detail count: " + detailRepo.count()); + logger.debug("Detail By Address: " + + detailRepo.findByBeneficiaryDetailsIdOrderByBeneficiaryDetailsIdAsc(new BigInteger("12"))); + logger.debug("FamilyMap count: " + familyMapRepo.count()); + logger.debug( + "FamilyMap By Id: " + familyMapRepo.findByBenFamilyMapIdOrderByBenFamilyMapIdAsc(new BigInteger("12"))); + logger.debug("Identity count: " + identityRepo.count()); + logger.debug("Identity By Id: " + identityRepo.findByBenIdentityId(new BigInteger("12"))); + logger.debug("Mapping count: " + mappingRepo.count()); + logger.debug("Mapping By Id: " + mappingRepo.findByBenMapIdOrderByBenMapIdAsc(new BigInteger("12"))); + } /** - * Try Elasticsearch first if enabled + * + * @param searchDTO + * @return */ - // if (esEnabled) { - // try { - // logger.info("Attempting Elasticsearch search"); - - // // Search by beneficiary Id - // if (searchDTO.getBeneficiaryId() != null) { - // logger.info("Elasticsearch: searching by beneficiaryId: {}", searchDTO.getBeneficiaryId()); - // list = elasticsearchService.searchByBenId(searchDTO.getBeneficiaryId()); - // if (!list.isEmpty()) { - // logger.info("Found {} results from Elasticsearch", list.size()); - // return enrichBeneficiariesFromDatabase(list); - // } - // } - - // // Search by beneficiary Reg Id - // if (searchDTO.getBeneficiaryRegId() != null) { - // logger.info("Elasticsearch: searching by beneficiaryRegId: {}", searchDTO.getBeneficiaryRegId()); - // list = elasticsearchService.searchByBenRegId(searchDTO.getBeneficiaryRegId()); - // if (!list.isEmpty()) { - // logger.info("Found {} results from Elasticsearch", list.size()); - // return enrichBeneficiariesFromDatabase(list); - // } - // } - - // // Search by contact number - // if (searchDTO.getContactNumber() != null) { - // logger.info("Elasticsearch: searching by phoneNum: {}", searchDTO.getContactNumber()); - // list = elasticsearchService.searchByPhoneNum(searchDTO.getContactNumber()); - // if (!list.isEmpty()) { - // logger.info("Found {} results from Elasticsearch", list.size()); - - // // Apply D2D filters if needed - // if (searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { - // list = applyD2DFilters(list, searchDTO); - // } - - // return enrichBeneficiariesFromDatabase(list); - // } - // } - - // // Advanced search (multiple criteria) - // if (hasMultipleCriteria(searchDTO)) { - // logger.info("Elasticsearch: advanced search"); - // list = elasticsearchService.advancedSearch( - // searchDTO.getFirstName(), - // searchDTO.getLastName(), - // searchDTO.getContactNumber(), - // null, // gender from searchDTO if available - // null // age from searchDTO if available - // ); - // if (!list.isEmpty()) { - // logger.info("Found {} results from Elasticsearch", list.size()); - // return enrichBeneficiariesFromDatabase(list); - // } - // } - - // logger.info("No results from Elasticsearch, falling back to database"); - - // } catch (Exception e) { - // logger.error("Elasticsearch search failed, falling back to database: {}", e.getMessage()); - // } - // } + public List getBeneficiaries(IdentitySearchDTO searchDTO) + throws NoResultException, QueryTimeoutException, Exception { + List list = new ArrayList(); + + /** + * if beneficiary Id present + */ + if (searchDTO.getBeneficiaryId() != null) { + logger.info("getting beneficiaries by ID for " + searchDTO.getBeneficiaryId()); + return this.getBeneficiariesByBenId(searchDTO.getBeneficiaryId()); + } + + /** + * if beneficiary Reg Id present + */ + if (searchDTO.getBeneficiaryRegId() != null) { + logger.info("getting beneficiaries by reg ID for " + searchDTO.getBeneficiaryRegId()); + return this.getBeneficiariesByBenRegId(searchDTO.getBeneficiaryRegId()); + } + + /** + * if beneficiary Reg Id present + */ + if (searchDTO.getContactNumber() != null) { + logger.info("getting beneficiaries by contact no for " + searchDTO.getContactNumber()); + List list3 = this.getBeneficiariesByPhoneNum(searchDTO.getContactNumber()); + if (!list3.isEmpty() && searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { + + for (int i = 0; i < list3.size(); i++) { + if (searchDTO.getFirstName() != null) { + if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null + || list3.get(i).getBeneficiaryDetails().getFirstName() == null + || !list3.get(i).getBeneficiaryDetails().getFirstName() + .equalsIgnoreCase(searchDTO.getFirstName())) { + list3.remove(i); + i--; + + continue; + } + + } + if (searchDTO.getLastName() != null) { + if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null + || list3.get(i).getBeneficiaryDetails().getLastName() == null + || !list3.get(i).getBeneficiaryDetails().getLastName() + .equalsIgnoreCase(searchDTO.getLastName())) { + list3.remove(i); + i--; + + continue; + } + } + + if (searchDTO.getGenderId() != null) { + if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null + || list3.get(i).getBeneficiaryDetails().getGenderId() == null || !list3.get(i) + .getBeneficiaryDetails().getGenderId().equals(searchDTO.getGenderId())) { + + list3.remove(i); + i--; + continue; + } + } + if (searchDTO.getHouseHoldID() != null) { + if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null + || list3.get(i).getBeneficiaryDetails().getHouseHoldID() == null || !list3.get(i) + .getBeneficiaryDetails().getHouseHoldID().equals(searchDTO.getHouseHoldID())) { + + list3.remove(i); + i--; + continue; + } + } + + if (searchDTO.getCurrentAddress().getStateId() != null) { + if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null + || list3.get(i).getCurrentAddress().getStateId() == null + || !list3.get(i).getCurrentAddress().getStateId() + .equals(searchDTO.getCurrentAddress().getStateId())) { + + list3.remove(i); + i--; + continue; + } + } + + if (searchDTO.getCurrentAddress().getDistrictId() != null) { + if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null + || list3.get(i).getCurrentAddress().getDistrictId() == null + || !list3.get(i).getCurrentAddress().getDistrictId() + .equals(searchDTO.getCurrentAddress().getDistrictId())) { + + list3.remove(i); + i--; + continue; + } + } + + if (searchDTO.getCurrentAddress().getVillageId() != null) { + if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null + || list3.get(i).getCurrentAddress().getVillageId() == null + || !list3.get(i).getCurrentAddress().getVillageId() + .equals(searchDTO.getCurrentAddress().getVillageId())) { + + list3.remove(i); + i--; + continue; + } + } + + } + } + return list3; + } + + /** + * New logic for advance search, 03-10-2018 + */ + List tmpList = mappingRepo.dynamicFilterSearchNew(searchDTO); + for (VBenAdvanceSearch obj : tmpList) { + list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew1(obj))); + logger.debug("benMapId: " + obj.getBenMapID()); + } + /** + * End + */ + + return list; + } + + public List getBeneficiarieswithES(IdentitySearchDTO searchDTO) + throws NoResultException, QueryTimeoutException, Exception { + List list = new ArrayList(); + // if beneficiary Id present + if (searchDTO.getBeneficiaryId() != null) { + logger.info("getting beneficiaries by ID for " + searchDTO.getBeneficiaryId()); + return this.getBeneficiariesByBenId(searchDTO.getBeneficiaryId()); + } + + // if beneficiary Reg Id present + if (searchDTO.getBeneficiaryRegId() != null) { + logger.info("getting beneficiaries by reg ID for " + searchDTO.getBeneficiaryRegId()); + return this.getBeneficiariesByBenRegId(searchDTO.getBeneficiaryRegId()); + } + + // if beneficiary Reg Id present + if (searchDTO.getContactNumber() != null) { + logger.info("getting beneficiaries by contact no for " + searchDTO.getContactNumber()); + List list3 = this.getBeneficiariesByPhoneNum(searchDTO.getContactNumber()); + if (!list3.isEmpty() && searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { + list3 = applyD2DFilters(list3, searchDTO); + } + return list3; + } + + // Advanced search from database + List tmpList = mappingRepo.dynamicFilterSearchNew(searchDTO); + for (VBenAdvanceSearch obj : tmpList) { + list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew1(obj))); + logger.debug("benMapId: " + obj.getBenMapID()); + } + + return list; + } /** - * Fallback to MySQL database search + * Check if search has multiple criteria (for advanced search) */ - logger.info("Searching in MySQL database"); - - // if beneficiary Id present - if (searchDTO.getBeneficiaryId() != null) { - logger.info("getting beneficiaries by ID for " + searchDTO.getBeneficiaryId()); - return this.getBeneficiariesByBenId(searchDTO.getBeneficiaryId()); + private boolean hasMultipleCriteria(IdentitySearchDTO searchDTO) { + int criteriaCount = 0; + + if (searchDTO.getFirstName() != null && !searchDTO.getFirstName().trim().isEmpty()) { + criteriaCount++; + } + if (searchDTO.getLastName() != null && !searchDTO.getLastName().trim().isEmpty()) { + criteriaCount++; + } + if (searchDTO.getContactNumber() != null && !searchDTO.getContactNumber().trim().isEmpty()) { + criteriaCount++; + } + + return criteriaCount >= 2; + } + + /** + * Enrich Elasticsearch results with complete data from database ES returns + * only basic fields, fetch complete data from DB + */ + private List enrichBeneficiariesFromDatabase(List esResults) { + List enrichedList = new ArrayList<>(); + + for (BeneficiariesDTO esResult : esResults) { + try { + if (esResult.getBenRegId() != null) { + // Fetch complete data from database + List fullData = this.getBeneficiariesByBenRegId(esResult.getBenRegId()); + if (!fullData.isEmpty()) { + enrichedList.add(fullData.get(0)); + } else { + // If not in DB anymore, return ES data + enrichedList.add(esResult); + } + } + } catch (Exception e) { + logger.error("Error enriching beneficiary data: {}", e.getMessage()); + // Return ES data if enrichment fails + enrichedList.add(esResult); + } + } + + return enrichedList; } - // if beneficiary Reg Id present - if (searchDTO.getBeneficiaryRegId() != null) { - logger.info("getting beneficiaries by reg ID for " + searchDTO.getBeneficiaryRegId()); - return this.getBeneficiariesByBenRegId(searchDTO.getBeneficiaryRegId()); + /** + * Apply D2D filters to search results + */ + private List applyD2DFilters(List list, IdentitySearchDTO searchDTO) { + List filtered = new ArrayList<>(); + + for (BeneficiariesDTO dto : list) { + boolean matches = true; + + if (searchDTO.getFirstName() != null) { + if (dto.getBeneficiaryDetails() == null + || dto.getBeneficiaryDetails().getFirstName() == null + || !dto.getBeneficiaryDetails().getFirstName().equalsIgnoreCase(searchDTO.getFirstName())) { + matches = false; + } + } + + if (matches && searchDTO.getLastName() != null) { + if (dto.getBeneficiaryDetails() == null + || dto.getBeneficiaryDetails().getLastName() == null + || !dto.getBeneficiaryDetails().getLastName().equalsIgnoreCase(searchDTO.getLastName())) { + matches = false; + } + } + + if (matches && searchDTO.getGenderId() != null) { + if (dto.getBeneficiaryDetails() == null + || dto.getBeneficiaryDetails().getGenderId() == null + || !dto.getBeneficiaryDetails().getGenderId().equals(searchDTO.getGenderId())) { + matches = false; + } + } + + if (matches && searchDTO.getHouseHoldID() != null) { + if (dto.getBeneficiaryDetails() == null + || dto.getBeneficiaryDetails().getHouseHoldID() == null + || !dto.getBeneficiaryDetails().getHouseHoldID().equals(searchDTO.getHouseHoldID())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getStateId() != null) { + if (dto.getCurrentAddress() == null + || dto.getCurrentAddress().getStateId() == null + || !dto.getCurrentAddress().getStateId().equals(searchDTO.getCurrentAddress().getStateId())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getDistrictId() != null) { + if (dto.getCurrentAddress() == null + || dto.getCurrentAddress().getDistrictId() == null + || !dto.getCurrentAddress().getDistrictId().equals(searchDTO.getCurrentAddress().getDistrictId())) { + matches = false; + } + } + + if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getVillageId() != null) { + if (dto.getCurrentAddress() == null + || dto.getCurrentAddress().getVillageId() == null + || !dto.getCurrentAddress().getVillageId().equals(searchDTO.getCurrentAddress().getVillageId())) { + matches = false; + } + } + + if (matches) { + filtered.add(dto); + } + } + + return filtered; } - // if beneficiary Reg Id present - if (searchDTO.getContactNumber() != null) { - logger.info("getting beneficiaries by contact no for " + searchDTO.getContactNumber()); - List list3 = this.getBeneficiariesByPhoneNum(searchDTO.getContactNumber()); - if (!list3.isEmpty() && searchDTO.getIsD2D() != null && Boolean.TRUE.equals(searchDTO.getIsD2D())) { - list3 = applyD2DFilters(list3, searchDTO); + /** + * + * Check which parameters available Get BenMapID based on the parameter/set + * of parameters Use BenMapID to retrieve all data for a Beneficiary + * + * @param benId + * @return + */ + public List getBeneficiariesByBenId(BigInteger benId) + throws NoResultException, QueryTimeoutException, Exception { + logger.info("IdentityService.getBeneficiariesByBenId - start, beneficiaryID : " + benId); + List list = new ArrayList(); + + MBeneficiaryregidmapping regId = regIdRepo.findByBeneficiaryID(benId); + if (regId != null && regId.getBenRegId() != null) { + List benMapObjArr = mappingRepo.getBenMappingByRegID(regId.getBenRegId()); + + // new logic, 27-08-2018 + if (benMapObjArr != null && benMapObjArr.size() > 0) { + MBeneficiarymapping benMap = this.getBeneficiariesDTONew(benMapObjArr.get(0)); + list.add(this.getBeneficiariesDTO(benMap)); + } } - return list3; + logger.info("benMap size " + (list.size() == 0 ? "No Beneficiary Found" : list.size())); + // end new logic + + logger.info("IdentityService.getBeneficiariesByBenId - end - beneficiaryID : " + benId); + + return list; } - // Advanced search from database - List tmpList = mappingRepo.dynamicFilterSearchNew(searchDTO); - for (VBenAdvanceSearch obj : tmpList) { - list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew1(obj))); - logger.debug("benMapId: " + obj.getBenMapID()); + /** + * + * @param BenRegId + * @return + */ + public List getBeneficiariesByBenRegId(BigInteger benRegId) + throws NoResultException, QueryTimeoutException, Exception { + List list = new ArrayList(); + logger.info("IdentityService.getBeneficiariesByBenRegId - start for benRegId " + benRegId); + try { + // new logic, 27-09-2018 + List benMapObjArr = mappingRepo.getBenMappingByRegID(benRegId); + + // new logic, 27-08-2018 + if (benMapObjArr != null && !benMapObjArr.isEmpty()) { + MBeneficiarymapping benMap = this.getBeneficiariesDTONew(benMapObjArr.get(0)); + list.add(this.getBeneficiariesDTO(benMap)); + } + logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); + // end new logic + + logger.info("IdentityService.getBeneficiariesByBenRegId - end for benRegId " + benRegId); + } catch (Exception e) { + logger.error("error in beneficiary search for beneficiary reg id : " + benRegId + " error : " + + e.getLocalizedMessage()); + } + + return list; } - return list; -} + /** + * + * Check which parameters available Get BenMapID based on the parameter/set + * of parameters Use BenMapID to retrieve all data for a Beneficiary + * + * @param phoneNum + * @return + */ + public List getBeneficiariesByPhoneNum(String phoneNum) + throws NoResultException, QueryTimeoutException { + // new logic, 27-09-2018 + List list = new ArrayList<>(); -/** - * Check if search has multiple criteria (for advanced search) - */ -private boolean hasMultipleCriteria(IdentitySearchDTO searchDTO) { - int criteriaCount = 0; - - if (searchDTO.getFirstName() != null && !searchDTO.getFirstName().trim().isEmpty()) criteriaCount++; - if (searchDTO.getLastName() != null && !searchDTO.getLastName().trim().isEmpty()) criteriaCount++; - if (searchDTO.getContactNumber() != null && !searchDTO.getContactNumber().trim().isEmpty()) criteriaCount++; - - return criteriaCount >= 2; -} - -/** - * Enrich Elasticsearch results with complete data from database - * ES returns only basic fields, fetch complete data from DB - */ -private List enrichBeneficiariesFromDatabase(List esResults) { - List enrichedList = new ArrayList<>(); - - for (BeneficiariesDTO esResult : esResults) { try { - if (esResult.getBenRegId() != null) { - // Fetch complete data from database - List fullData = this.getBeneficiariesByBenRegId(esResult.getBenRegId()); - if (!fullData.isEmpty()) { - enrichedList.add(fullData.get(0)); - } else { - // If not in DB anymore, return ES data - enrichedList.add(esResult); - } + List benContact = contactRepo.findByAnyPhoneNum(phoneNum); + + logger.info(benContact.size() + " contacts found for phone number " + phoneNum); + + List benMapObjArr = new ArrayList<>(); + + for (MBeneficiarycontact benContactOBJ : benContact) { + benMapObjArr.addAll(mappingRepo.getBenMappingByBenContactIdListNew(benContactOBJ.getVanSerialNo(), + benContactOBJ.getVanID())); + } + + for (Object[] benMapOBJ : benMapObjArr) { + list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); } + } catch (Exception e) { - logger.error("Error enriching beneficiary data: {}", e.getMessage()); - // Return ES data if enrichment fails - enrichedList.add(esResult); + logger.error( + "error in beneficiary search for phone no : " + phoneNum + " error : " + e.getLocalizedMessage()); } + + logger.info("IdentityService.getBeneficiariesByPhoneNum - end"); + // end + return list; } - - return enrichedList; -} -/** - * Apply D2D filters to search results - */ -private List applyD2DFilters(List list, IdentitySearchDTO searchDTO) { - List filtered = new ArrayList<>(); - - for (BeneficiariesDTO dto : list) { - boolean matches = true; - - if (searchDTO.getFirstName() != null) { - if (dto.getBeneficiaryDetails() == null || - dto.getBeneficiaryDetails().getFirstName() == null || - !dto.getBeneficiaryDetails().getFirstName().equalsIgnoreCase(searchDTO.getFirstName())) { - matches = false; + /** + * * + * + * Search beneficiary by healthID / ABHA address + * + * @param healthID + * @return + * @throws NoResultException + * @throws QueryTimeoutException + * @throws Exception + */ + public List getBeneficiaryByHealthIDAbhaAddress(String healthID) + throws NoResultException, QueryTimeoutException, Exception { + List beneficiaryList = new ArrayList<>(); + try { + List regIDList = v_BenAdvanceSearchRepo.getBenRegIDByHealthIDAbhaAddress(healthID); + if (regIDList != null && !regIDList.isEmpty()) { + for (BigInteger benRegID : regIDList) { + if (benRegID != null) { + List searchList = this.getBeneficiariesByBenRegId(benRegID); + beneficiaryList.addAll(searchList); + } + } } + } catch (Exception e) { + logger.error("error in beneficiary search for health ID / ABHA Address : " + healthID + " error : " + + e.getLocalizedMessage()); } - - if (matches && searchDTO.getLastName() != null) { - if (dto.getBeneficiaryDetails() == null || - dto.getBeneficiaryDetails().getLastName() == null || - !dto.getBeneficiaryDetails().getLastName().equalsIgnoreCase(searchDTO.getLastName())) { - matches = false; + return beneficiaryList; + } + + /** + * * + * + * Search beneficiary by healthIDNo / ABHA ID No + * + * @param healthIDNo + * @return + * @throws NoResultException + * @throws QueryTimeoutException + * @throws Exception + */ + public List getBeneficiaryByHealthIDNoAbhaIdNo(String healthIDNo) + throws NoResultException, QueryTimeoutException, Exception { + List beneficiaryList = new ArrayList<>(); + try { + List regIDList = v_BenAdvanceSearchRepo.getBenRegIDByHealthIDNoAbhaIdNo(healthIDNo); + if (regIDList != null && !regIDList.isEmpty()) { + for (BigInteger benRegID : regIDList) { + if (benRegID != null) { + List searchList = this.getBeneficiariesByBenRegId(benRegID); + beneficiaryList.addAll(searchList); + } + } } + } catch (Exception e) { + logger.error("error in beneficiary search for health ID No / ABHA ID No : " + healthIDNo + " error : " + + e.getLocalizedMessage()); } - - if (matches && searchDTO.getGenderId() != null) { - if (dto.getBeneficiaryDetails() == null || - dto.getBeneficiaryDetails().getGenderId() == null || - !dto.getBeneficiaryDetails().getGenderId().equals(searchDTO.getGenderId())) { - matches = false; + return beneficiaryList; + } + + public List searhBeneficiaryByFamilyId(String familyId) + throws NoResultException, QueryTimeoutException { + List beneficiaryList = new ArrayList<>(); + try { + + // find benmap ids + List benMapObjArr = null; + List benDetailsVanSerialNoList = new ArrayList<>(); + int vanID = 0; + + List benDetailsList = detailRepo.searchByFamilyId(familyId); + + if (benDetailsList == null || benDetailsList.isEmpty()) { + return beneficiaryList; + }else { + // considering as of now family creation is possible through facility modules + // only + vanID = benDetailsList.get(0).getVanID(); + + for (MBeneficiarydetail benDetails : benDetailsList) { + benDetailsVanSerialNoList.add(benDetails.getVanSerialNo()); + } + + benMapObjArr = mappingRepo.getBenMappingByBenDetailsIds(benDetailsVanSerialNoList, vanID); + + for (Object[] benMapOBJ : benMapObjArr) { + beneficiaryList.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); + } + } + + } catch (Exception e) { + logger.error( + "error in beneficiary search for familyId : " + familyId + " error : " + e.getLocalizedMessage()); } - - if (matches && searchDTO.getHouseHoldID() != null) { - if (dto.getBeneficiaryDetails() == null || - dto.getBeneficiaryDetails().getHouseHoldID() == null || - !dto.getBeneficiaryDetails().getHouseHoldID().equals(searchDTO.getHouseHoldID())) { - matches = false; + return beneficiaryList; + } + + public List searchBeneficiaryByVillageIdAndLastModifyDate(List villageIDs, + Timestamp lastModifiedDate) { + + List beneficiaryList = new ArrayList<>(); + try { + // find benmap ids + List benMappingsList = mappingRepo + .findByBeneficiaryDetailsByVillageIDAndLastModifyDate(villageIDs, lastModifiedDate); + if (benMappingsList != null && !benMappingsList.isEmpty()) { + for (MBeneficiarymapping benMapOBJ : benMappingsList) { + beneficiaryList.add(this.getBeneficiariesDTO(benMapOBJ)); + } } + + } catch (Exception e) { + logger.error("error in beneficiary search to sync to CHO App with villageIDs: {} ", + villageIDs + " error : " + e.getLocalizedMessage()); } - - if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getStateId() != null) { - if (dto.getCurrentAddress() == null || - dto.getCurrentAddress().getStateId() == null || - !dto.getCurrentAddress().getStateId().equals(searchDTO.getCurrentAddress().getStateId())) { - matches = false; + return beneficiaryList; + } + + public Long countBeneficiaryByVillageIdAndLastModifyDate(List villageIDs, Timestamp lastModifiedDate) { + Long beneficiaryCount = 0L; + try { + beneficiaryCount = mappingRepo.getBeneficiaryCountsByVillageIDAndLastModifyDate(villageIDs, + lastModifiedDate); + } catch (Exception e) { + logger.error("error in getting beneficiary count to sync to CHO App with villageIDs: {},error :{} ", + villageIDs, e.getLocalizedMessage()); + } + return beneficiaryCount; + } + + public List searhBeneficiaryByGovIdentity(String identity) + throws NoResultException, QueryTimeoutException { + List beneficiaryList = new ArrayList<>(); + try { + + List benMapObjArr = new ArrayList<>(); + + // find identity no + List benIdentityList = identityRepo.searchByIdentityNo(identity); + + // find benmap ids + if (benIdentityList == null || benIdentityList.isEmpty()) { + return beneficiaryList; + }else { + for (MBeneficiaryidentity identityObj : benIdentityList) { + benMapObjArr.addAll( + mappingRepo.getBenMappingByVanSerialNo(identityObj.getBenMapId(), identityObj.getVanID())); + } + + for (Object[] benMapOBJ : benMapObjArr) { + beneficiaryList.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); + } + } + + } catch (Exception e) { + logger.error("error in beneficiary search for gov identity : " + identity + " error : " + + e.getLocalizedMessage()); } - - if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getDistrictId() != null) { - if (dto.getCurrentAddress() == null || - dto.getCurrentAddress().getDistrictId() == null || - !dto.getCurrentAddress().getDistrictId().equals(searchDTO.getCurrentAddress().getDistrictId())) { - matches = false; + return beneficiaryList; + } + + private MBeneficiarymapping getBeneficiariesDTONew(Object[] benMapArr) { + MBeneficiarymapping mapping = new MBeneficiarymapping(); + if (benMapArr != null && benMapArr.length == 12 && benMapArr[8] != null && benMapArr[9] != null) { + mapping.setBenMapId(getBigIntegerValueFromObject(benMapArr[0])); + mapping.setCreatedBy(String.valueOf(benMapArr[10])); + mapping.setCreatedDate((Timestamp) benMapArr[11]); + mapping = mappingRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[9]), (Integer) benMapArr[8]); + MBeneficiaryaddress address = addressRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[1]), (Integer) benMapArr[8]); + MBeneficiaryconsent consent = consentRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[2]), (Integer) benMapArr[8]); + MBeneficiarycontact contact = contactRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[3]), (Integer) benMapArr[8]); + MBeneficiarydetail details = detailRepo.getWith_vanSerialNo_vanID(getBigIntegerValueFromObject(benMapArr[4]), (Integer) benMapArr[8]); + MBeneficiaryregidmapping regidmapping = regIdRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[5]), (Integer) benMapArr[8]); + MBeneficiaryAccount account = accountRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[7]), (Integer) benMapArr[8]); + MBeneficiaryImage image = imageRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[6]), (Integer) benMapArr[8]); + List servicemap = serviceMapRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); + List identity = identityRepo.findByBenMapIdAndVanID(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); + List familymapping = familyMapRepo.findByBenMapIdAndVanIDOrderByBenFamilyMapIdAsc(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); + + mapping.setMBeneficiaryaddress(address); + mapping.setMBeneficiaryconsent(consent); + mapping.setMBeneficiarycontact(contact); + mapping.setMBeneficiarydetail(details); + mapping.setMBeneficiaryfamilymappings(familymapping); + mapping.setMBeneficiaryidentities(identity); + mapping.setMBeneficiaryImage(image); + mapping.setMBeneficiaryregidmapping(regidmapping); + mapping.setMBeneficiaryservicemappings(servicemap); + mapping.setMBeneficiaryAccount(account); + //benMapOBJ = mappingRepo.getMapping(getBigIntegerValueFromObject(benMapArr[9]), (Integer) benMapArr[8]); + + BigInteger benRegId = new BigInteger(benMapArr[5].toString()); + RMNCHBeneficiaryDetailsRmnch obj = rMNCHBeneficiaryDetailsRmnchRepo + .getByRegID(benRegId); + + if (obj != null) { + if (obj.getHouseoldId() != null) { + mapping.setHouseHoldID(obj.getHouseoldId()); + } + if (obj.getGuidelineId() != null) { + mapping.setGuideLineID(obj.getGuidelineId()); + } + if (obj.getRchid() != null) { + mapping.setRchID(obj.getRchid()); + } } + } - - if (matches && searchDTO.getCurrentAddress() != null && searchDTO.getCurrentAddress().getVillageId() != null) { - if (dto.getCurrentAddress() == null || - dto.getCurrentAddress().getVillageId() == null || - !dto.getCurrentAddress().getVillageId().equals(searchDTO.getCurrentAddress().getVillageId())) { - matches = false; + return mapping; + } + + private MBeneficiarymapping getBeneficiariesDTONewPartial(Object[] benMapArr) { + MBeneficiarymapping benMapOBJ = new MBeneficiarymapping(); + + benMapOBJ.setBenMapId(getBigIntegerValueFromObject(benMapArr[0])); + benMapOBJ.setCreatedBy(String.valueOf(benMapArr[10])); + benMapOBJ.setCreatedDate((Timestamp) benMapArr[11]); + + if (benMapArr.length == 12 && benMapArr[8] != null && benMapArr[9] != null) { + + benMapOBJ.setMBeneficiarydetail(detailRepo + .getWith_vanSerialNo_vanID(getBigIntegerValueFromObject(benMapArr[4]), (Integer) benMapArr[8])); + benMapOBJ.setMBeneficiaryregidmapping(regIdRepo + .getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[5]), (Integer) benMapArr[8])); + } + return benMapOBJ; + } + + // 03-10-2018 + // get ben mapping object from v_benadvancesearch + private MBeneficiarymapping getBeneficiariesDTONew1(VBenAdvanceSearch benAdvanceSearchOBJ) { + MBeneficiarymapping benMapOBJ = new MBeneficiarymapping(); + + // for createdBy & createdDate + if (benAdvanceSearchOBJ != null) { + MBeneficiarydetail benDetailsOBJ = detailRepo + .getWith_vanSerialNo_vanID(benAdvanceSearchOBJ.getBenDetailsID(), benAdvanceSearchOBJ.getVanID()); + + if (benAdvanceSearchOBJ.getHouseHoldID() != null) { + benMapOBJ.setHouseHoldID(benAdvanceSearchOBJ.getHouseHoldID()); + } + if (benAdvanceSearchOBJ.getGuideLineID() != null) { + benMapOBJ.setGuideLineID(benAdvanceSearchOBJ.getGuideLineID()); } + if (benAdvanceSearchOBJ.getRchID() != null) { + benMapOBJ.setRchID(benAdvanceSearchOBJ.getRchID()); + } + + benMapOBJ.setBenMapId(benAdvanceSearchOBJ.getBenMapID()); + benMapOBJ.setCreatedBy(benDetailsOBJ.getCreatedBy()); + benMapOBJ.setCreatedDate(benDetailsOBJ.getCreatedDate()); + + benMapOBJ.setMBeneficiaryaddress(addressRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenAddressID(), + benAdvanceSearchOBJ.getVanID())); + benMapOBJ.setMBeneficiaryconsent(consentRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenConsentID(), + benAdvanceSearchOBJ.getVanID())); + benMapOBJ.setMBeneficiarycontact(contactRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenContactID(), + benAdvanceSearchOBJ.getVanID())); + benMapOBJ.setMBeneficiarydetail(benDetailsOBJ); + benMapOBJ.setMBeneficiaryregidmapping(regIdRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenRegID(), + benAdvanceSearchOBJ.getVanID())); + benMapOBJ.setMBeneficiaryImage(imageRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenImageID(), + benAdvanceSearchOBJ.getVanID())); + benMapOBJ.setMBeneficiaryAccount(accountRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenAccountID(), + benAdvanceSearchOBJ.getVanID())); + + benMapOBJ.setMBeneficiaryfamilymappings( + familyMapRepo.findByBenMapIdOrderByBenFamilyMapIdAsc(benAdvanceSearchOBJ.getVanSerialNo())); + benMapOBJ.setMBeneficiaryidentities(identityRepo.findByBenMapId(benAdvanceSearchOBJ.getVanSerialNo())); } - - if (matches) { - filtered.add(dto); + + return benMapOBJ; + } + + private BigInteger getBigIntegerValueFromObject(Object value) { + BigInteger ret = null; + if (value != null) { + if (value instanceof BigInteger) { + ret = (BigInteger) value; + } else if (value instanceof String) { + ret = new BigInteger((String) value); + } else if (value instanceof BigDecimal) { + ret = ((BigDecimal) value).toBigInteger(); + } else if (value instanceof Number) { + ret = BigInteger.valueOf(((Number) value).longValue()); + } else { + throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() + + " into a BigInteger."); + } + } else { + throw new ClassCastException("given value is null"); } + return ret; } - - return filtered; -} - - /** - * - * Check which parameters available Get BenMapID based on the parameter/set of - * parameters Use BenMapID to retrieve all data for a Beneficiary - * - * @param benId - * @return - */ - public List getBeneficiariesByBenId(BigInteger benId) - throws NoResultException, QueryTimeoutException, Exception { - logger.info("IdentityService.getBeneficiariesByBenId - start, beneficiaryID : " + benId); - List list = new ArrayList(); - - // // Try Elasticsearch first if enabled - // if (esEnabled) { - // try { - // list = elasticsearchService.searchByBenId(benId); - // if (!list.isEmpty()) { - // logger.info("Found " + list.size() + " results from Elasticsearch for BenId: " + benId); - // return list; - // } - // logger.info("No results from Elasticsearch, falling back to database"); - // } catch (Exception e) { - // logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); - // } - // } - - - MBeneficiaryregidmapping regId = regIdRepo.findByBeneficiaryID(benId); - if (regId != null && regId.getBenRegId() != null) { - List benMapObjArr = mappingRepo.getBenMappingByRegID(regId.getBenRegId()); - - // new logic, 27-08-2018 - if (benMapObjArr != null && benMapObjArr.size() > 0) { - MBeneficiarymapping benMap = this.getBeneficiariesDTONew(benMapObjArr.get(0)); - list.add(this.getBeneficiariesDTO(benMap)); - } - } - logger.info("benMap size " + (list.size() == 0 ? "No Beneficiary Found" : list.size())); - // end new logic - - logger.info("IdentityService.getBeneficiariesByBenId - end - beneficiaryID : " + benId); - - return list; - } - - /** - * - * @param BenRegId - * @return - */ - public List getBeneficiariesByBenRegId(BigInteger benRegId) - throws NoResultException, QueryTimeoutException, Exception { - List list = new ArrayList(); - logger.info("IdentityService.getBeneficiariesByBenRegId - start for benRegId " + benRegId); - try { - // new logic, 27-09-2018 - List benMapObjArr = mappingRepo.getBenMappingByRegID(benRegId); - - // new logic, 27-08-2018 - if (benMapObjArr != null && !benMapObjArr.isEmpty()) { - MBeneficiarymapping benMap = this.getBeneficiariesDTONew(benMapObjArr.get(0)); - list.add(this.getBeneficiariesDTO(benMap)); - } - logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); - // end new logic - - logger.info("IdentityService.getBeneficiariesByBenRegId - end for benRegId " + benRegId); - } catch (Exception e) { - logger.error("error in beneficiary search for beneficiary reg id : " + benRegId + " error : " - + e.getLocalizedMessage()); - } - - return list; - } - - /** - * - * Check which parameters available Get BenMapID based on the parameter/set of - * parameters Use BenMapID to retrieve all data for a Beneficiary - * - * @param phoneNum - * @return - */ - - public List getBeneficiariesByPhoneNum(String phoneNum) - throws NoResultException, QueryTimeoutException { - // new logic, 27-09-2018 - List list = new ArrayList<>(); - - // Try Elasticsearch first if enabled - // if (esEnabled) { - // try { - // list = elasticsearchService.searchByPhoneNum(phoneNum); - // if (!list.isEmpty()) { - // logger.info("Found " + list.size() + " results from Elasticsearch for PhoneNum: " + phoneNum); - // return list; - // } - // logger.info("No results from Elasticsearch, falling back to database"); - // } catch (Exception e) { - // logger.error("Elasticsearch search failed, falling back to database: " + e.getMessage()); - // } - // } - - try { - List benContact = contactRepo.findByAnyPhoneNum(phoneNum); - - logger.info(benContact.size() + " contacts found for phone number " + phoneNum); - - List benMapObjArr = new ArrayList<>(); - - for (MBeneficiarycontact benContactOBJ : benContact) { - benMapObjArr.addAll(mappingRepo.getBenMappingByBenContactIdListNew(benContactOBJ.getVanSerialNo(), - benContactOBJ.getVanID())); - } - - for (Object[] benMapOBJ : benMapObjArr) { - list.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); - } - - } catch (Exception e) { - logger.error( - "error in beneficiary search for phone no : " + phoneNum + " error : " + e.getLocalizedMessage()); - } - - logger.info("IdentityService.getBeneficiariesByPhoneNum - end"); - // end - return list; - } - - /*** - * - * Search beneficiary by healthID / ABHA address - * - * @param healthID - * @return - * @throws NoResultException - * @throws QueryTimeoutException - * @throws Exception - */ - - public List getBeneficiaryByHealthIDAbhaAddress(String healthID) - throws NoResultException, QueryTimeoutException, Exception { - List beneficiaryList = new ArrayList<>(); - try { - List regIDList = v_BenAdvanceSearchRepo.getBenRegIDByHealthIDAbhaAddress(healthID); - if (regIDList != null && !regIDList.isEmpty()) { - for (BigInteger benRegID : regIDList) { - if (benRegID != null) { - List searchList = this.getBeneficiariesByBenRegId(benRegID); - beneficiaryList.addAll(searchList); - } - } - } - } catch (Exception e) { - logger.error("error in beneficiary search for health ID / ABHA Address : " + healthID + " error : " - + e.getLocalizedMessage()); - } - return beneficiaryList; - } - - /*** - * - * Search beneficiary by healthIDNo / ABHA ID No - * - * @param healthIDNo - * @return - * @throws NoResultException - * @throws QueryTimeoutException - * @throws Exception - */ - - public List getBeneficiaryByHealthIDNoAbhaIdNo(String healthIDNo) - throws NoResultException, QueryTimeoutException, Exception { - List beneficiaryList = new ArrayList<>(); - try { - List regIDList = v_BenAdvanceSearchRepo.getBenRegIDByHealthIDNoAbhaIdNo(healthIDNo); - if (regIDList != null && !regIDList.isEmpty()) { - for (BigInteger benRegID : regIDList) { - if (benRegID != null) { - List searchList = this.getBeneficiariesByBenRegId(benRegID); - beneficiaryList.addAll(searchList); - } - } - } - } catch (Exception e) { - logger.error("error in beneficiary search for health ID No / ABHA ID No : " + healthIDNo + " error : " - + e.getLocalizedMessage()); - } - return beneficiaryList; - } - - public List searhBeneficiaryByFamilyId(String familyId) - throws NoResultException, QueryTimeoutException { - List beneficiaryList = new ArrayList<>(); - try { - - // find benmap ids - List benMapObjArr = null; - List benDetailsVanSerialNoList = new ArrayList<>(); - int vanID = 0; - - List benDetailsList = detailRepo.searchByFamilyId(familyId); - - if (benDetailsList == null || benDetailsList.isEmpty()) - return beneficiaryList; - else { - // considering as of now family creation is possible through facility modules - // only - vanID = benDetailsList.get(0).getVanID(); - - for (MBeneficiarydetail benDetails : benDetailsList) { - benDetailsVanSerialNoList.add(benDetails.getVanSerialNo()); - } - - benMapObjArr = mappingRepo.getBenMappingByBenDetailsIds(benDetailsVanSerialNoList, vanID); - - for (Object[] benMapOBJ : benMapObjArr) { - beneficiaryList.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); - } - - } - - } catch (Exception e) { - logger.error( - "error in beneficiary search for familyId : " + familyId + " error : " + e.getLocalizedMessage()); - } - return beneficiaryList; - } - - public List searchBeneficiaryByVillageIdAndLastModifyDate(List villageIDs, - Timestamp lastModifiedDate) { - - List beneficiaryList = new ArrayList<>(); - try { - // find benmap ids - List benMappingsList = mappingRepo - .findByBeneficiaryDetailsByVillageIDAndLastModifyDate(villageIDs, lastModifiedDate); - if (benMappingsList != null && !benMappingsList.isEmpty()) { - for (MBeneficiarymapping benMapOBJ : benMappingsList) { - beneficiaryList.add(this.getBeneficiariesDTO(benMapOBJ)); - } - } - - } catch (Exception e) { - logger.error("error in beneficiary search to sync to CHO App with villageIDs: {} ", - villageIDs + " error : " + e.getLocalizedMessage()); - } - return beneficiaryList; - } - - public Long countBeneficiaryByVillageIdAndLastModifyDate(List villageIDs, Timestamp lastModifiedDate) { - Long beneficiaryCount = 0L; - try { - beneficiaryCount = mappingRepo.getBeneficiaryCountsByVillageIDAndLastModifyDate(villageIDs, - lastModifiedDate); - } catch (Exception e) { - logger.error("error in getting beneficiary count to sync to CHO App with villageIDs: {},error :{} ", - villageIDs, e.getLocalizedMessage()); - } - return beneficiaryCount; - } - - public List searhBeneficiaryByGovIdentity(String identity) - throws NoResultException, QueryTimeoutException { - List beneficiaryList = new ArrayList<>(); - try { - - List benMapObjArr = new ArrayList<>(); - - // find identity no - List benIdentityList = identityRepo.searchByIdentityNo(identity); - - // find benmap ids - if (benIdentityList == null || benIdentityList.isEmpty()) - return beneficiaryList; - else { - for (MBeneficiaryidentity identityObj : benIdentityList) { - benMapObjArr.addAll( - mappingRepo.getBenMappingByVanSerialNo(identityObj.getBenMapId(), identityObj.getVanID())); - } - - for (Object[] benMapOBJ : benMapObjArr) { - beneficiaryList.add(this.getBeneficiariesDTO(this.getBeneficiariesDTONew(benMapOBJ))); - } - - } - - } catch (Exception e) { - logger.error("error in beneficiary search for gov identity : " + identity + " error : " - + e.getLocalizedMessage()); - } - return beneficiaryList; - } - - private MBeneficiarymapping getBeneficiariesDTONew(Object[] benMapArr) { - MBeneficiarymapping mapping = new MBeneficiarymapping(); - if (benMapArr != null && benMapArr.length == 12 && benMapArr[8] != null && benMapArr[9] != null) { - mapping.setBenMapId(getBigIntegerValueFromObject(benMapArr[0])); - mapping.setCreatedBy(String.valueOf(benMapArr[10])); - mapping.setCreatedDate((Timestamp) benMapArr[11]); - mapping = mappingRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[9]), (Integer) benMapArr[8]); - MBeneficiaryaddress address = addressRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[1]), (Integer) benMapArr[8]); - MBeneficiaryconsent consent = consentRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[2]), (Integer) benMapArr[8]); - MBeneficiarycontact contact = contactRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[3]), (Integer) benMapArr[8]); - MBeneficiarydetail details = detailRepo.getWith_vanSerialNo_vanID(getBigIntegerValueFromObject(benMapArr[4]), (Integer) benMapArr[8]); - MBeneficiaryregidmapping regidmapping = regIdRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[5]), (Integer) benMapArr[8]); - MBeneficiaryAccount account = accountRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[7]), (Integer) benMapArr[8]); - MBeneficiaryImage image = imageRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[6]), (Integer) benMapArr[8]); - List servicemap = serviceMapRepo.getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); - List identity = identityRepo.findByBenMapIdAndVanID(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); - List familymapping = familyMapRepo.findByBenMapIdAndVanIDOrderByBenFamilyMapIdAsc(getBigIntegerValueFromObject(benMapArr[0]), (Integer) benMapArr[8]); - - mapping.setMBeneficiaryaddress(address); - mapping.setMBeneficiaryconsent(consent); - mapping.setMBeneficiarycontact(contact); - mapping.setMBeneficiarydetail(details); - mapping.setMBeneficiaryfamilymappings(familymapping); - mapping.setMBeneficiaryidentities(identity); - mapping.setMBeneficiaryImage(image); - mapping.setMBeneficiaryregidmapping(regidmapping); - mapping.setMBeneficiaryservicemappings(servicemap); - mapping.setMBeneficiaryAccount(account); - //benMapOBJ = mappingRepo.getMapping(getBigIntegerValueFromObject(benMapArr[9]), (Integer) benMapArr[8]); - - - - BigInteger benRegId = new BigInteger(benMapArr[5].toString()); - RMNCHBeneficiaryDetailsRmnch obj = rMNCHBeneficiaryDetailsRmnchRepo - .getByRegID(benRegId); - - if (obj != null) { - if (obj.getHouseoldId() != null) - mapping.setHouseHoldID(obj.getHouseoldId()); - if (obj.getGuidelineId() != null) - mapping.setGuideLineID(obj.getGuidelineId()); - if (obj.getRchid() != null) - mapping.setRchID(obj.getRchid()); - } - - } - return mapping; - } - private MBeneficiarymapping getBeneficiariesDTONewPartial(Object[] benMapArr) { - MBeneficiarymapping benMapOBJ = new MBeneficiarymapping(); - - benMapOBJ.setBenMapId(getBigIntegerValueFromObject(benMapArr[0])); - benMapOBJ.setCreatedBy(String.valueOf(benMapArr[10])); - benMapOBJ.setCreatedDate((Timestamp) benMapArr[11]); - - if (benMapArr.length == 12 && benMapArr[8] != null && benMapArr[9] != null) { - - benMapOBJ.setMBeneficiarydetail(detailRepo - .getWith_vanSerialNo_vanID(getBigIntegerValueFromObject(benMapArr[4]), (Integer) benMapArr[8])); - benMapOBJ.setMBeneficiaryregidmapping(regIdRepo - .getWithVanSerialNoVanID(getBigIntegerValueFromObject(benMapArr[5]), (Integer) benMapArr[8])); - } - return benMapOBJ; - } - - // 03-10-2018 - // get ben mapping object from v_benadvancesearch - private MBeneficiarymapping getBeneficiariesDTONew1(VBenAdvanceSearch benAdvanceSearchOBJ) { - MBeneficiarymapping benMapOBJ = new MBeneficiarymapping(); - - // for createdBy & createdDate - if (benAdvanceSearchOBJ != null) { - MBeneficiarydetail benDetailsOBJ = detailRepo - .getWith_vanSerialNo_vanID(benAdvanceSearchOBJ.getBenDetailsID(), benAdvanceSearchOBJ.getVanID()); - - if (benAdvanceSearchOBJ.getHouseHoldID() != null) - benMapOBJ.setHouseHoldID(benAdvanceSearchOBJ.getHouseHoldID()); - if (benAdvanceSearchOBJ.getGuideLineID() != null) - benMapOBJ.setGuideLineID(benAdvanceSearchOBJ.getGuideLineID()); - if (benAdvanceSearchOBJ.getRchID() != null) - benMapOBJ.setRchID(benAdvanceSearchOBJ.getRchID()); - - benMapOBJ.setBenMapId(benAdvanceSearchOBJ.getBenMapID()); - benMapOBJ.setCreatedBy(benDetailsOBJ.getCreatedBy()); - benMapOBJ.setCreatedDate(benDetailsOBJ.getCreatedDate()); - - benMapOBJ.setMBeneficiaryaddress(addressRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenAddressID(), - benAdvanceSearchOBJ.getVanID())); - benMapOBJ.setMBeneficiaryconsent(consentRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenConsentID(), - benAdvanceSearchOBJ.getVanID())); - benMapOBJ.setMBeneficiarycontact(contactRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenContactID(), - benAdvanceSearchOBJ.getVanID())); - benMapOBJ.setMBeneficiarydetail(benDetailsOBJ); - benMapOBJ.setMBeneficiaryregidmapping(regIdRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenRegID(), - benAdvanceSearchOBJ.getVanID())); - benMapOBJ.setMBeneficiaryImage(imageRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenImageID(), - benAdvanceSearchOBJ.getVanID())); - benMapOBJ.setMBeneficiaryAccount(accountRepo.getWithVanSerialNoVanID(benAdvanceSearchOBJ.getBenAccountID(), - benAdvanceSearchOBJ.getVanID())); - - benMapOBJ.setMBeneficiaryfamilymappings( - familyMapRepo.findByBenMapIdOrderByBenFamilyMapIdAsc(benAdvanceSearchOBJ.getVanSerialNo())); - benMapOBJ.setMBeneficiaryidentities(identityRepo.findByBenMapId(benAdvanceSearchOBJ.getVanSerialNo())); - } - - return benMapOBJ; - } - - private BigInteger getBigIntegerValueFromObject(Object value) { - BigInteger ret = null; - if (value != null) { - if (value instanceof BigInteger) { - ret = (BigInteger) value; - } else if (value instanceof String) { - ret = new BigInteger((String) value); - } else if (value instanceof BigDecimal) { - ret = ((BigDecimal) value).toBigInteger(); - } else if (value instanceof Number) { - ret = BigInteger.valueOf(((Number) value).longValue()); - } else { - throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() - + " into a BigInteger."); - } - } else - throw new ClassCastException("given value is null"); - return ret; - } - - /** - * The following parameters can be changed/edited once created: - First Name - - * Middle Name - Last Name - DOB/Age - Address (Current, Permanent, Emergency) - - * Contact Numbers/Email Ids - Spouse Name - Preferred Num - Preferred SMS Num - - * Email Id - Identity - * - * Following changes need Additional Authorization - First Name - Middle Name - - * Last Name - Father Name - Spouse Name - Identity - * - * @param identity - * @return - * @throws MissingMandatoryFieldsException - */ - public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFieldsException { - logger.info("IdentityService.editIdentity - start"); - if (identity.getBeneficiaryRegId() == null && null == identity.getBeneficaryId()) { - throw new MissingMandatoryFieldsException("Either of BeneficiaryID or Beneficiary Reg Id is mandatory."); - } - - MBeneficiarymapping benMapping = mappingRepo.findByBenRegIdOrderByBenMapIdAsc(identity.getBeneficiaryRegId()); - - // change in self details is implement here and other details here - logger.debug("identity.getChangeInSelfDetails = " + identity.getChangeInSelfDetails()); - logger.debug("identity.getChangeInOtherDetails = " + identity.getChangeInOtherDetails()); - logger.debug("identity.getChangeInAssociations = " + identity.getChangeInAssociations()); - if (Boolean.TRUE.equals(identity.getChangeInSelfDetails()) - || Boolean.TRUE.equals(identity.getChangeInOtherDetails()) - || Boolean.TRUE.equals(identity.getChangeInAssociations())) { - - // MBeneficiarydetail mbDetl = - // editMapper.identityEditDTOToMBeneficiarydetail(identity); - // MBeneficiarydetail mbDetl = - // convertIdentityEditDTOToMBeneficiarydetail(identity); - /** - * new logic for data sync, 26-09-2018 - */ - // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID - MBeneficiarydetail benDetails = detailRepo.findBenDetailsByVanSerialNoAndVanID( - benMapping.getMBeneficiarydetail().getBeneficiaryDetailsId(), benMapping.getVanID()); - // next statement is new one, setting correct beneficiaryDetailsId - if (benDetails != null) { - // Create a new instance of MBeneficiarydetail or use the existing one - MBeneficiarydetail mbDetl = convertIdentityEditDTOToMBeneficiarydetail(identity); - - // Set fields from the existing benDetails to mbDetl - mbDetl.setBeneficiaryDetailsId(benDetails.getBeneficiaryDetailsId()); - if (benDetails.getFamilyId() != null) - mbDetl.setFamilyId(benDetails.getFamilyId()); - if (benDetails.getHeadOfFamily_RelationID() != null) - mbDetl.setHeadOfFamily_RelationID(benDetails.getHeadOfFamily_RelationID()); - if (benDetails.getHeadOfFamily_Relation() != null) - mbDetl.setHeadOfFamily_Relation(benDetails.getHeadOfFamily_Relation()); - if (benDetails.getOther() != null) - mbDetl.setOther(benDetails.getOther()); - - // Extract and set extra fields + + /** + * The following parameters can be changed/edited once created: - First Name + * - Middle Name - Last Name - DOB/Age - Address (Current, Permanent, + * Emergency) - Contact Numbers/Email Ids - Spouse Name - Preferred Num - + * Preferred SMS Num - Email Id - Identity + * + * Following changes need Additional Authorization - First Name - Middle + * Name - Last Name - Father Name - Spouse Name - Identity + * + * @param identity + * @return + * @throws MissingMandatoryFieldsException + */ + public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFieldsException { + logger.info("IdentityService.editIdentity - start"); + if (identity.getBeneficiaryRegId() == null && null == identity.getBeneficaryId()) { + throw new MissingMandatoryFieldsException("Either of BeneficiaryID or Beneficiary Reg Id is mandatory."); + } + + MBeneficiarymapping benMapping = mappingRepo.findByBenRegIdOrderByBenMapIdAsc(identity.getBeneficiaryRegId()); + + // change in self details is implement here and other details here + logger.debug("identity.getChangeInSelfDetails = " + identity.getChangeInSelfDetails()); + logger.debug("identity.getChangeInOtherDetails = " + identity.getChangeInOtherDetails()); + logger.debug("identity.getChangeInAssociations = " + identity.getChangeInAssociations()); + if (Boolean.TRUE.equals(identity.getChangeInSelfDetails()) + || Boolean.TRUE.equals(identity.getChangeInOtherDetails()) + || Boolean.TRUE.equals(identity.getChangeInAssociations())) { + + // MBeneficiarydetail mbDetl = + // editMapper.identityEditDTOToMBeneficiarydetail(identity); + // MBeneficiarydetail mbDetl = + // convertIdentityEditDTOToMBeneficiarydetail(identity); + /** + * new logic for data sync, 26-09-2018 + */ + // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID + MBeneficiarydetail benDetails = detailRepo.findBenDetailsByVanSerialNoAndVanID( + benMapping.getMBeneficiarydetail().getBeneficiaryDetailsId(), benMapping.getVanID()); + // next statement is new one, setting correct beneficiaryDetailsId + if (benDetails != null) { + // Create a new instance of MBeneficiarydetail or use the existing one + MBeneficiarydetail mbDetl = convertIdentityEditDTOToMBeneficiarydetail(identity); + + // Set fields from the existing benDetails to mbDetl + mbDetl.setBeneficiaryDetailsId(benDetails.getBeneficiaryDetailsId()); + if (benDetails.getFamilyId() != null) { + mbDetl.setFamilyId(benDetails.getFamilyId()); + } + if (benDetails.getHeadOfFamily_RelationID() != null) { + mbDetl.setHeadOfFamily_RelationID(benDetails.getHeadOfFamily_RelationID()); + } + if (benDetails.getHeadOfFamily_Relation() != null) { + mbDetl.setHeadOfFamily_Relation(benDetails.getHeadOfFamily_Relation()); + } + if (benDetails.getOther() != null) { + mbDetl.setOther(benDetails.getOther()); + } + + // Extract and set extra fields // String identityJson = new Gson().toJson(json); // JsonObject identityJsonObject = new Gson().fromJson(identityJson, JsonObject.class); // JsonObject otherFieldsJson = new JsonObject(); @@ -1045,1018 +958,1088 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields // } // } // String otherFieldsJsonString = otherFieldsJson.toString(); - // mbDetl.setOtherFields(benDetails.getOtherFields()); - logger.debug("Beneficiary details to update = " + new OutputMapper().gson().toJson(mbDetl)); - if (benDetails.getEmergencyRegistration() != null && benDetails.getEmergencyRegistration()) { - mbDetl.setEmergencyRegistration(true); - } else { - mbDetl.setEmergencyRegistration(false); - } - detailRepo.save(mbDetl); - } - } - // edition in current emergency and permanent is implement below - logger.debug("identity.getChangeInAddress = " + identity.getChangeInAddress()); - if (Boolean.TRUE.equals(identity.getChangeInAddress())) { - - MBeneficiaryaddress mbAddr = editMapper.identityEditDTOToMBeneficiaryaddress(identity); - - /** - * new logic for data sync, 26-09-2018 - */ - - // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID - BigInteger benAddressID = addressRepo.findIdByVanSerialNoAndVanID( - benMapping.getMBeneficiaryaddress().getBenAddressID(), benMapping.getVanID()); - // next statement is new one, setting correct beneficiaryDetailsId - if (benAddressID != null) - mbAddr.setBenAddressID(benAddressID); - else - throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); - - /** - * END - */ - - logger.debug("Beneficiary address to update = " + OutputMapper.gson().toJson(mbAddr)); - addressRepo.save(mbAddr); - } - - // edition in beneficiary contacts is updated here - logger.debug("identity.getChangeInContacts = " + identity.getChangeInContacts()); - if (Boolean.TRUE.equals(identity.getChangeInContacts())) { - - MBeneficiarycontact benCon = editMapper.identityEdiDTOToMBeneficiarycontact(identity); - - /** - * new logic for data sync, 26-09-2018 - */ - - // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID - BigInteger benContactsID = contactRepo.findIdByVanSerialNoAndVanID( - benMapping.getMBeneficiarycontact().getBenContactsID(), benMapping.getVanID()); - // next statement is new one, setting correct beneficiaryDetailsId - if (benContactsID != null) - benCon.setBenContactsID(benContactsID); - else - throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); - - /** - * END - */ - - logger.debug("Beneficiary contact to update = " + OutputMapper.gson().toJson(benCon)); - contactRepo.save(benCon); - } - - // change in identities are added here - logger.debug("identity.getChangeInIdentities = " + identity.getChangeInIdentities()); - if (Boolean.TRUE.equals(identity.getChangeInIdentities())) { - - MBeneficiaryidentity beneficiaryidentity; - List identities = editMapper - .identityEditDTOListToMBeneficiaryidentityList(identity.getIdentities()); - logger.debug("identities to upsert = " + OutputMapper.gson().toJson(identities)); - - // new logic for getting beneficiary identities, 26-09-2018 - List idList = identityRepo.findByBenMapId(benMapping.getVanSerialNo()); - - logger.debug("existing identies = " + OutputMapper.gson().toJson(idList)); - ListIterator iterator = identities.listIterator(); - int index = 0; - while (iterator.hasNext()) { - beneficiaryidentity = iterator.next(); - - // new logic, 26-09-2018 - beneficiaryidentity.setBenMapId(benMapping.getVanSerialNo()); - logger.debug("Beneficiary identity to update = " + OutputMapper.gson().toJson(beneficiaryidentity)); - if (index < idList.size() && beneficiaryidentity.getBenIdentityId() == null) { - beneficiaryidentity.setBenIdentityId(idList.get(index).getBenIdentityId()); - } - - // new code to set vanID & parkingPlaceID for new record, 26-09-2018 - if (index >= idList.size() && beneficiaryidentity.getBenIdentityId() == null) { - beneficiaryidentity.setVanID(benMapping.getVanID()); - beneficiaryidentity.setParkingPlaceID(benMapping.getParkingPlaceID()); - } - - // new logic, 26-09-2018 - MBeneficiaryidentity m = identityRepo.save(beneficiaryidentity); - - // new code, update van serial no for new entry, 26-09-2018 - if (index >= idList.size() && beneficiaryidentity.getBenIdentityId() == null) { - identityRepo.updateVanSerialNo(m.getBenIdentityId()); - } - - index++; - } - } - - // family detail changes are performing here - logger.debug("identity.getChangeInFamilyDetails = " + identity.getChangeInFamilyDetails()); - if (Boolean.TRUE.equals(identity.getChangeInFamilyDetails())) { - List fbMaps = editMapper - .identityEditDTOListToMBeneficiaryfamilymappingList(identity.getBenFamilyDTOs()); - - logger.debug("family map to upsert = " + OutputMapper.gson().toJson(fbMaps)); - - // new logic, 26-09-2018 - List fmList = familyMapRepo - .findByBenMapIdOrderByBenFamilyMapIdAsc(benMapping.getVanSerialNo()); - - logger.debug("family map stored = " + OutputMapper.gson().toJson(fmList)); - ListIterator iterator = fbMaps.listIterator(); - MBeneficiaryfamilymapping familymapping; - int index = 0; - while (iterator.hasNext()) { - - familymapping = iterator.next(); - // new logic, 26-09-2018 - familymapping.setBenMapId(benMapping.getVanSerialNo()); - - logger.debug("family mapping to update = " + OutputMapper.gson().toJson(familymapping)); - if (index < fmList.size()) { - familymapping.setBenFamilyMapId(fmList.get(index).getBenFamilyMapId()); - } - - if (index >= fmList.size() && familymapping.getBenFamilyMapId() == null) { - familymapping.setVanID(benMapping.getVanID()); - familymapping.setParkingPlaceID(benMapping.getParkingPlaceID()); - } - - // new logic, 26-09-2018 - MBeneficiaryfamilymapping m = familyMapRepo.save(familymapping); - - // new code, update van serial no for new entry, 26-09-2018 - if (familymapping.getBenFamilyMapId() == null) { - familyMapRepo.updateVanSerialNo(m.getBenFamilyMapId()); - } - - index++; - } - } - - // start - // Feature used in outreach - if (Boolean.TRUE.equals(identity.getChangeInBankDetails())) { - MBeneficiaryAccount beneficiaryAccount = editMapper.identityEditDTOToMBeneficiaryAccount(identity); - - /** - * new logic for data sync, 26-09-2018 - */ - // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID - BigInteger benAccountID = accountRepo.findIdByVanSerialNoAndVanID( - benMapping.getMBeneficiaryAccount().getBenAccountID(), benMapping.getVanID()); - // next statement is new one, setting correct beneficiaryDetailsId - if (benAccountID != null) - beneficiaryAccount.setBenAccountID(benAccountID); - else - throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); - - /** - * END - */ - - logger.debug("Account to upsert = " + OutputMapper.gson().toJson(beneficiaryAccount)); - accountRepo.save(beneficiaryAccount); - } - - if (Boolean.TRUE.equals(identity.getChangeInBenImage())) { - MBeneficiaryImage beneficiaryImage = editMapper.identityEditDTOToMBeneficiaryImage(identity); - - /** - * new logic for data sync, 26-09-2018 - */ - // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID - BigInteger benImageId = imageRepo.findIdByVanSerialNoAndVanID( - benMapping.getMBeneficiaryImage().getBenImageId(), benMapping.getVanID()); - // next statement is new one, setting correct beneficiaryDetailsId - if (benImageId != null) - beneficiaryImage.setBenImageId(benImageId); - else - throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); - - /** - * END - */ - - logger.debug("Image to upsert = " + OutputMapper.gson().toJson(beneficiaryImage)); - beneficiaryImage.setProcessed("N"); - imageRepo.save(beneficiaryImage); - } + // mbDetl.setOtherFields(benDetails.getOtherFields()); + logger.debug("Beneficiary details to update = " + new OutputMapper().gson().toJson(mbDetl)); + if (benDetails.getEmergencyRegistration() != null && benDetails.getEmergencyRegistration()) { + mbDetl.setEmergencyRegistration(true); + } else { + mbDetl.setEmergencyRegistration(false); + } + detailRepo.save(mbDetl); + } + } + // edition in current emergency and permanent is implement below + logger.debug("identity.getChangeInAddress = " + identity.getChangeInAddress()); + if (Boolean.TRUE.equals(identity.getChangeInAddress())) { + + MBeneficiaryaddress mbAddr = editMapper.identityEditDTOToMBeneficiaryaddress(identity); + + /** + * new logic for data sync, 26-09-2018 + */ + // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID + BigInteger benAddressID = addressRepo.findIdByVanSerialNoAndVanID( + benMapping.getMBeneficiaryaddress().getBenAddressID(), benMapping.getVanID()); + // next statement is new one, setting correct beneficiaryDetailsId + if (benAddressID != null) { + mbAddr.setBenAddressID(benAddressID); + }else { + throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); + } -// Trigger async sync to Elasticsearch -if (identity.getBeneficiaryRegId() != null) { - logger.info("Triggering Elasticsearch sync for benRegId: {}", identity.getBeneficiaryRegId()); - realtimeSyncService.syncBeneficiaryAsync(identity.getBeneficiaryRegId()); -} - logger.info("IdentityService.editIdentity - end. id = " + benMapping.getBenMapId()); - } - - private MBeneficiarydetail convertIdentityEditDTOToMBeneficiarydetail(IdentityEditDTO dto) { - MBeneficiarydetail beneficiarydetail = new MBeneficiarydetail(); - - // Directly set values without using @Mapping - beneficiarydetail.setAreaId(dto.getAreaId()); - beneficiarydetail.setBeneficiaryRegID(dto.getBeneficiaryRegId()); - beneficiarydetail.setCommunity(dto.getCommunity()); - beneficiarydetail.setLiteracyStatus(dto.getLiteracyStatus()); - beneficiarydetail.setCommunityId(dto.getCommunityId()); - beneficiarydetail.setDob(dto.getDob()); - beneficiarydetail.setEducation(dto.getEducation()); - beneficiarydetail.setEducationId(dto.getEducationId()); - beneficiarydetail.setHealthCareWorkerId(dto.getHealthCareWorkerId()); - beneficiarydetail.setHealthCareWorker(dto.getHealthCareWorker()); - beneficiarydetail.setFatherName(dto.getFatherName()); - beneficiarydetail.setMotherName(dto.getMotherName()); - beneficiarydetail.setFirstName(dto.getFirstName()); - beneficiarydetail.setGender(dto.getGender()); - beneficiarydetail.setGenderId(dto.getGenderId()); - beneficiarydetail.setIncomeStatusId(dto.getIncomeStatusId()); - beneficiarydetail.setIncomeStatus(dto.getIncomeStatus()); - beneficiarydetail.setMonthlyFamilyIncome(dto.getMonthlyFamilyIncome()); - if (dto.getLastName() != null) - beneficiarydetail.setLastName(dto.getLastName()); - beneficiarydetail.setMaritalStatusId(dto.getMaritalStatusId()); - beneficiarydetail.setMaritalStatus(dto.getMaritalStatus()); - beneficiarydetail.setMiddleName(dto.getMiddleName()); - beneficiarydetail.setOccupation(dto.getOccupationName()); - beneficiarydetail.setOccupationId(dto.getOccupationId()); - beneficiarydetail.setPhcId(dto.getPhcId()); - beneficiarydetail.setPlaceOfWork(dto.getPlaceOfWork()); - beneficiarydetail.setPreferredLanguage(dto.getPreferredLanguage()); - beneficiarydetail.setReligion(dto.getReligion()); - if (dto.getReligionId() != null) - beneficiarydetail.setReligionId(BigInteger.valueOf(dto.getReligionId())); - beneficiarydetail.setRemarks(dto.getRemarks()); - beneficiarydetail.setServicePointId(dto.getServicePointId()); - beneficiarydetail.setSourceOfInfo(dto.getSourceOfInfo()); - beneficiarydetail.setSpouseName(dto.getSpouseName()); - beneficiarydetail.setStatus(dto.getStatus()); - beneficiarydetail.setTitle(dto.getTitle()); - beneficiarydetail.setTitleId(dto.getTitleId()); - beneficiarydetail.setZoneId(dto.getZoneId()); - beneficiarydetail.setCreatedBy(dto.getAgentName()); - beneficiarydetail.setCreatedDate(dto.getEventTypeDate()); - beneficiarydetail.setIsHIVPositive(MBeneficiarydetail.setIsHIVPositive(dto.getIsHIVPositive())); - beneficiarydetail.setAgeAtMarriage( - MBeneficiarydetail.getAgeAtMarriageCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); - beneficiarydetail.setMarriageDate( - MBeneficiarydetail.getMarriageDateCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); - if(dto.getOtherFields() != null) - beneficiarydetail.setOtherFields(dto.getOtherFields()); - - return beneficiarydetail; - } - - /** - * @param identity - * @return - */ - - ArrayDeque queue = new ArrayDeque<>(); - - public BeneficiaryCreateResp createIdentity(IdentityDTO identity) { - logger.info("IdentityService.createIdentity - start"); - - List list = null; - MBeneficiaryregidmapping regMap = null; - synchronized (queue) { - if (queue.isEmpty()) { - logger.info("fetching 10000 rows"); - list = regIdRepo.findTop10000ByProvisionedAndReserved(false, false); - logger.info("Adding SynchronousQueue start-- "); - for (MBeneficiaryregidmapping map : list) { - queue.add(map); - } - logger.info("Adding SynchronousQueue end-- "); - } - regMap = queue.removeFirst(); - } - regMap.setReserved(true); - if (regMap.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - regMap.setCreatedDate(ts); - } - - regIdRepo.save(regMap); - - regMap.setProvisioned(true); - - logger.info("IdentityService.createIdentity - saving Address"); - ObjectMapper objectMapper = new ObjectMapper(); - MBeneficiaryaddress mAddr = identityDTOToMBeneficiaryaddress(identity); - // MBeneficiaryaddress mAddr1 = - // mapper.identityDTOToMBeneficiaryaddress(identity); - logger.info("identity.getIsPermAddrSameAsCurrAddr = " + identity.getIsPermAddrSameAsCurrAddr()); - if (Boolean.TRUE.equals(identity.getIsPermAddrSameAsCurrAddr())) { - logger.debug("identity.getCurrentAddress = " + identity.getCurrentAddress()); - mAddr.setPermanentAddress(identity.getCurrentAddress()); - } - - logger.info("identity.getIsPermAddrSameAsEmerAddr = " + identity.getIsPermAddrSameAsEmerAddr()); - if (Boolean.TRUE.equals(identity.getIsPermAddrSameAsEmerAddr())) { - logger.debug("identity.getEmergencyAddress = " + identity.getEmergencyAddress()); - mAddr.setPermanentAddress(identity.getEmergencyAddress()); - } - - logger.info("identity.getIsEmerAddrSameAsCurrAddr = " + identity.getIsEmerAddrSameAsCurrAddr()); - if (Boolean.TRUE.equals(identity.getIsEmerAddrSameAsCurrAddr())) { - logger.debug("identity.getCurrentAddress = " + identity.getCurrentAddress()); - mAddr.setEmergencyAddress(identity.getCurrentAddress()); - } - - logger.info("identity.getIsEmerAddrSameAsPermAddr = " + identity.getIsEmerAddrSameAsPermAddr()); - if (Boolean.TRUE.equals(identity.getIsEmerAddrSameAsPermAddr())) { - logger.debug("identity.getPermanentAddress = " + identity.getPermanentAddress()); - mAddr.setEmergencyAddress(identity.getPermanentAddress()); - } - if (mAddr.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - mAddr.setCreatedDate(ts); - } - - mAddr = addressRepo.save(mAddr); - logger.info("IdentityService.createIdentity - Address saved - id = " + mAddr.getBenAddressID()); - - // Update van serial no for data sync - addressRepo.updateVanSerialNo(mAddr.getBenAddressID()); - - MBeneficiaryconsent mConsnt = mapper.identityDTOToDefaultMBeneficiaryconsent(identity, true, false); - logger.info("IdentityService.createIdentity - saving Consent"); - if (mConsnt.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - mConsnt.setCreatedDate(ts); - } - mConsnt = consentRepo.save(mConsnt); - logger.info("IdentityService.createIdentity - Consent saved - id = " + mConsnt.getBenConsentID()); - - // Update van serial no for data sync - consentRepo.updateVanSerialNo(mConsnt.getBenConsentID()); - - logger.info("IdentityService.createIdentity - saving Contacts"); - MBeneficiarycontact mContc = identityDTOToMBeneficiarycontact(identity); - // MBeneficiarycontact mContc = - // mapper.identityDTOToMBeneficiarycontact(identity); - if (mContc.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - mContc.setCreatedDate(ts); - } - mContc = contactRepo.save(mContc); - logger.info("IdentityService.createIdentity - Contacts saved - id = " + mContc.getBenContactsID()); - - // Update van serial no for data sync - contactRepo.updateVanSerialNo(mContc.getBenContactsID()); - - logger.info("IdentityService.createIdentity - saving Details"); - // MBeneficiarydetail mDetl = mapper.identityDTOToMBeneficiarydetail(identity); - MBeneficiarydetail mDetl = convertIdentityDTOToMBeneficiarydetail(identity); - - if (mDetl.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - mDetl.setCreatedDate(ts); - } - mDetl = detailRepo.save(mDetl); - logger.info("IdentityService.createIdentity - Details saved - id = " + mDetl.getBeneficiaryDetailsId()); - - // Update van serial no for data sync - detailRepo.updateVanSerialNo(mDetl.getBeneficiaryDetailsId()); - - MBeneficiaryAccount bankOBJ = mapper.identityDTOToMBeneficiaryAccount(identity); - if (bankOBJ.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - bankOBJ.setCreatedDate(ts); - } - bankOBJ = accountRepo.save(bankOBJ); - // Update van serial no for data sync - accountRepo.updateVanSerialNo(bankOBJ.getBenAccountID()); - - // MBeneficiaryImage benImageOBJ = mapper.identityDTOToMBeneficiaryImage(identity); - MBeneficiaryImage benImageOBJ = identityDTOToMBeneficiaryImage(identity); - - if (benImageOBJ.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - benImageOBJ.setCreatedDate(ts); - } - benImageOBJ = imageRepo.save(benImageOBJ); - - // Update van serial no for data sync - imageRepo.updateVanSerialNo(benImageOBJ.getBenImageId()); - - logger.info("IdentityService.createIdentity - saving Mapping"); - MBeneficiarymapping benMapping = mapper.identityDTOToMBeneficiarymapping(identity); - - benMapping.setMBeneficiarycontact(mContc); - benMapping.setMBeneficiaryaddress(mAddr); - benMapping.setMBeneficiaryconsent(mConsnt); - benMapping.setMBeneficiarydetail(mDetl); - benMapping.setMBeneficiaryregidmapping(regMap); - benMapping.setMBeneficiaryImage(benImageOBJ); - benMapping.setMBeneficiaryAccount(bankOBJ); - - regMap.setProviderServiceMapID(identity.getProviderServiceMapId()); - // added columns for data sync - // 17-09-2018 - if (identity.getVanID() != null) - regMap.setVanID(identity.getVanID()); - if (identity.getParkingPlaceId() != null) - regMap.setParkingPlaceID(identity.getParkingPlaceId()); - regMap.setVanSerialNo(regMap.getBenRegId()); - // END - - regIdRepo.save(regMap); - if (benMapping.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - benMapping.setCreatedDate(ts); - } - - if (identity.getBenFamilyDTOs().get(0).getVanID() != null) - benMapping.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); - - benMapping = mappingRepo.save(benMapping); - // Update van serial no for data sync - mappingRepo.updateVanSerialNo(benMapping.getBenMapId()); - - final MBeneficiarymapping benMapping2 = benMapping; - logger.info("IdentityService.createIdentity - saving FamilyMaps"); - List fIdenList = null; - List fList = null; - - // new logic (18-09-2018, Neeraj kumar) - if (null != identity.getBenFamilyDTOs()) { - fIdenList = mapper.identityDTOListToMBeneficiaryfamilymappingList(identity.getBenFamilyDTOs()); - if (fIdenList != null) { - for (MBeneficiaryfamilymapping bfMapping : fIdenList) { - bfMapping.setBenMapId(benMapping2.getBenMapId()); - - if (bfMapping.getVanID() == null && identity.getVanID() != null) - bfMapping.setVanID(identity.getVanID()); - if (bfMapping.getVanID() == null && identity.getBenFamilyDTOs().get(0).getVanID() != null) - bfMapping.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); - - if (bfMapping.getParkingPlaceID() == null && identity.getParkingPlaceId() != null) - bfMapping.setParkingPlaceID(identity.getParkingPlaceId()); - - if (bfMapping.getAssociatedBenRegId() == null) { - bfMapping.setAssociatedBenRegId(benMapping2.getBenRegId()); - } - } - fList = (List) familyMapRepo.saveAll(fIdenList); - // Update van serial no for data sync - if (fList != null && !fList.isEmpty()) { - for (MBeneficiaryfamilymapping obj : fList) { - familyMapRepo.updateVanSerialNo(obj.getBenFamilyMapId()); - } - } - } - } - - logger.info("IdentityService.createIdentity - FamilyMap saved "); - logger.info("IdentityService.createIdentity - saving Service Map"); - MBeneficiaryservicemapping sMap = mapper.identityDTOToMBeneficiaryservicemapping(identity); - sMap.setBenMapId(benMapping.getBenMapId()); - if (sMap.getCreatedDate() == null) { - SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); - String dateToStoreInDataBase = sdf.format(new Date()); - Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); - sMap.setCreatedDate(ts); - } - sMap = serviceMapRepo.save(sMap); - logger.info("IdentityService.createIdentity - ServiceMap saved - id = " + sMap.getBenServiceMapID()); - - // Update van serial no for data sync - serviceMapRepo.updateVanSerialNo(sMap.getBenServiceMapID()); - - List sList = new ArrayList<>(); - sList.add(sMap); - logger.info("IdentityService.createIdentity - saving Identity"); - List mIdenList2 = new ArrayList<>(); - if (null != identity.getIdentities()) { - List mIdenList = mapper - .identityDTOListToMBeneficiaryidentityList(identity.getIdentities()); - mIdenList.forEach(mIden -> { - mIden.setBenMapId(benMapping2.getBenMapId()); - mIden.setCreatedBy(identity.getAgentName()); - mIden.setCreatedDate(identity.getEventTypeDate()); - - // set new column(vanID, parkingPlaceID) value for data sync - if (identity.getVanID() != null) - mIden.setVanID(identity.getVanID()); - if (identity.getParkingPlaceId() != null) - mIden.setParkingPlaceID(identity.getParkingPlaceId()); - - MBeneficiaryidentity m = identityRepo.save(mIden); - - // Update van serial no for data sync - identityRepo.updateVanSerialNo(m.getBenIdentityId()); - - mIdenList2.add(m); - logger.info("IdentityService.createIdentity - Identity saved - id = " + m.getBenIdentityId()); - }); - } - - - // return partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); - logger.info("IdentityService.createIdentity - end. id = " + benMapping.getBenMapId()); - -BeneficiaryCreateResp response = partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); + /** + * END + */ + logger.debug("Beneficiary address to update = " + OutputMapper.gson().toJson(mbAddr)); + addressRepo.save(mbAddr); + } + + // edition in beneficiary contacts is updated here + logger.debug("identity.getChangeInContacts = " + identity.getChangeInContacts()); + if (Boolean.TRUE.equals(identity.getChangeInContacts())) { + + MBeneficiarycontact benCon = editMapper.identityEdiDTOToMBeneficiarycontact(identity); + + /** + * new logic for data sync, 26-09-2018 + */ + // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID + BigInteger benContactsID = contactRepo.findIdByVanSerialNoAndVanID( + benMapping.getMBeneficiarycontact().getBenContactsID(), benMapping.getVanID()); + // next statement is new one, setting correct beneficiaryDetailsId + if (benContactsID != null) { + benCon.setBenContactsID(benContactsID); + }else { + throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); + } + + /** + * END + */ + logger.debug("Beneficiary contact to update = " + OutputMapper.gson().toJson(benCon)); + contactRepo.save(benCon); + } + + // change in identities are added here + logger.debug("identity.getChangeInIdentities = " + identity.getChangeInIdentities()); + if (Boolean.TRUE.equals(identity.getChangeInIdentities())) { + + MBeneficiaryidentity beneficiaryidentity; + List identities = editMapper + .identityEditDTOListToMBeneficiaryidentityList(identity.getIdentities()); + logger.debug("identities to upsert = " + OutputMapper.gson().toJson(identities)); + + // new logic for getting beneficiary identities, 26-09-2018 + List idList = identityRepo.findByBenMapId(benMapping.getVanSerialNo()); + + logger.debug("existing identies = " + OutputMapper.gson().toJson(idList)); + ListIterator iterator = identities.listIterator(); + int index = 0; + while (iterator.hasNext()) { + beneficiaryidentity = iterator.next(); + + // new logic, 26-09-2018 + beneficiaryidentity.setBenMapId(benMapping.getVanSerialNo()); + logger.debug("Beneficiary identity to update = " + OutputMapper.gson().toJson(beneficiaryidentity)); + if (index < idList.size() && beneficiaryidentity.getBenIdentityId() == null) { + beneficiaryidentity.setBenIdentityId(idList.get(index).getBenIdentityId()); + } + + // new code to set vanID & parkingPlaceID for new record, 26-09-2018 + if (index >= idList.size() && beneficiaryidentity.getBenIdentityId() == null) { + beneficiaryidentity.setVanID(benMapping.getVanID()); + beneficiaryidentity.setParkingPlaceID(benMapping.getParkingPlaceID()); + } + + // new logic, 26-09-2018 + MBeneficiaryidentity m = identityRepo.save(beneficiaryidentity); + + // new code, update van serial no for new entry, 26-09-2018 + if (index >= idList.size() && beneficiaryidentity.getBenIdentityId() == null) { + identityRepo.updateVanSerialNo(m.getBenIdentityId()); + } + + index++; + } + } + + // family detail changes are performing here + logger.debug("identity.getChangeInFamilyDetails = " + identity.getChangeInFamilyDetails()); + if (Boolean.TRUE.equals(identity.getChangeInFamilyDetails())) { + List fbMaps = editMapper + .identityEditDTOListToMBeneficiaryfamilymappingList(identity.getBenFamilyDTOs()); + + logger.debug("family map to upsert = " + OutputMapper.gson().toJson(fbMaps)); + + // new logic, 26-09-2018 + List fmList = familyMapRepo + .findByBenMapIdOrderByBenFamilyMapIdAsc(benMapping.getVanSerialNo()); + + logger.debug("family map stored = " + OutputMapper.gson().toJson(fmList)); + ListIterator iterator = fbMaps.listIterator(); + MBeneficiaryfamilymapping familymapping; + int index = 0; + while (iterator.hasNext()) { + + familymapping = iterator.next(); + // new logic, 26-09-2018 + familymapping.setBenMapId(benMapping.getVanSerialNo()); + + logger.debug("family mapping to update = " + OutputMapper.gson().toJson(familymapping)); + if (index < fmList.size()) { + familymapping.setBenFamilyMapId(fmList.get(index).getBenFamilyMapId()); + } + + if (index >= fmList.size() && familymapping.getBenFamilyMapId() == null) { + familymapping.setVanID(benMapping.getVanID()); + familymapping.setParkingPlaceID(benMapping.getParkingPlaceID()); + } + + // new logic, 26-09-2018 + MBeneficiaryfamilymapping m = familyMapRepo.save(familymapping); + + // new code, update van serial no for new entry, 26-09-2018 + if (familymapping.getBenFamilyMapId() == null) { + familyMapRepo.updateVanSerialNo(m.getBenFamilyMapId()); + } + + index++; + } + } + + // start + // Feature used in outreach + if (Boolean.TRUE.equals(identity.getChangeInBankDetails())) { + MBeneficiaryAccount beneficiaryAccount = editMapper.identityEditDTOToMBeneficiaryAccount(identity); + + /** + * new logic for data sync, 26-09-2018 + */ + // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID + BigInteger benAccountID = accountRepo.findIdByVanSerialNoAndVanID( + benMapping.getMBeneficiaryAccount().getBenAccountID(), benMapping.getVanID()); + // next statement is new one, setting correct beneficiaryDetailsId + if (benAccountID != null) { + beneficiaryAccount.setBenAccountID(benAccountID); + }else { + throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); + } + + /** + * END + */ + logger.debug("Account to upsert = " + OutputMapper.gson().toJson(beneficiaryAccount)); + accountRepo.save(beneficiaryAccount); + } + + if (Boolean.TRUE.equals(identity.getChangeInBenImage())) { + MBeneficiaryImage beneficiaryImage = editMapper.identityEditDTOToMBeneficiaryImage(identity); + + /** + * new logic for data sync, 26-09-2018 + */ + // getting correct beneficiaryDetailsId by passing vanSerialNo & vanID + BigInteger benImageId = imageRepo.findIdByVanSerialNoAndVanID( + benMapping.getMBeneficiaryImage().getBenImageId(), benMapping.getVanID()); + // next statement is new one, setting correct beneficiaryDetailsId + if (benImageId != null) { + beneficiaryImage.setBenImageId(benImageId); + }else { + throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); + } + + /** + * END + */ + logger.debug("Image to upsert = " + OutputMapper.gson().toJson(beneficiaryImage)); + beneficiaryImage.setProcessed("N"); + imageRepo.save(beneficiaryImage); + } + + // Trigger async sync to Elasticsearch + if (identity.getBeneficiaryRegId() != null) { + logger.info("Triggering Elasticsearch sync for benRegId: {}", identity.getBeneficiaryRegId()); + syncService.syncBeneficiaryAsync(identity.getBeneficiaryRegId()); + } + logger.info("IdentityService.editIdentity - end. id = " + benMapping.getBenMapId()); + } + + private MBeneficiarydetail convertIdentityEditDTOToMBeneficiarydetail(IdentityEditDTO dto) { + MBeneficiarydetail beneficiarydetail = new MBeneficiarydetail(); + + // Directly set values without using @Mapping + beneficiarydetail.setAreaId(dto.getAreaId()); + beneficiarydetail.setBeneficiaryRegID(dto.getBeneficiaryRegId()); + beneficiarydetail.setCommunity(dto.getCommunity()); + beneficiarydetail.setLiteracyStatus(dto.getLiteracyStatus()); + beneficiarydetail.setCommunityId(dto.getCommunityId()); + beneficiarydetail.setDob(dto.getDob()); + beneficiarydetail.setEducation(dto.getEducation()); + beneficiarydetail.setEducationId(dto.getEducationId()); + beneficiarydetail.setHealthCareWorkerId(dto.getHealthCareWorkerId()); + beneficiarydetail.setHealthCareWorker(dto.getHealthCareWorker()); + beneficiarydetail.setFatherName(dto.getFatherName()); + beneficiarydetail.setMotherName(dto.getMotherName()); + beneficiarydetail.setFirstName(dto.getFirstName()); + beneficiarydetail.setGender(dto.getGender()); + beneficiarydetail.setGenderId(dto.getGenderId()); + beneficiarydetail.setIncomeStatusId(dto.getIncomeStatusId()); + beneficiarydetail.setIncomeStatus(dto.getIncomeStatus()); + beneficiarydetail.setMonthlyFamilyIncome(dto.getMonthlyFamilyIncome()); + if (dto.getLastName() != null) { + beneficiarydetail.setLastName(dto.getLastName()); + } + beneficiarydetail.setMaritalStatusId(dto.getMaritalStatusId()); + beneficiarydetail.setMaritalStatus(dto.getMaritalStatus()); + beneficiarydetail.setMiddleName(dto.getMiddleName()); + beneficiarydetail.setOccupation(dto.getOccupationName()); + beneficiarydetail.setOccupationId(dto.getOccupationId()); + beneficiarydetail.setPhcId(dto.getPhcId()); + beneficiarydetail.setPlaceOfWork(dto.getPlaceOfWork()); + beneficiarydetail.setPreferredLanguage(dto.getPreferredLanguage()); + beneficiarydetail.setReligion(dto.getReligion()); + if (dto.getReligionId() != null) { + beneficiarydetail.setReligionId(BigInteger.valueOf(dto.getReligionId())); + } + beneficiarydetail.setRemarks(dto.getRemarks()); + beneficiarydetail.setServicePointId(dto.getServicePointId()); + beneficiarydetail.setSourceOfInfo(dto.getSourceOfInfo()); + beneficiarydetail.setSpouseName(dto.getSpouseName()); + beneficiarydetail.setStatus(dto.getStatus()); + beneficiarydetail.setTitle(dto.getTitle()); + beneficiarydetail.setTitleId(dto.getTitleId()); + beneficiarydetail.setZoneId(dto.getZoneId()); + beneficiarydetail.setCreatedBy(dto.getAgentName()); + beneficiarydetail.setCreatedDate(dto.getEventTypeDate()); + beneficiarydetail.setIsHIVPositive(MBeneficiarydetail.setIsHIVPositive(dto.getIsHIVPositive())); + beneficiarydetail.setAgeAtMarriage( + MBeneficiarydetail.getAgeAtMarriageCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); + beneficiarydetail.setMarriageDate( + MBeneficiarydetail.getMarriageDateCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); + if (dto.getOtherFields() != null) { + beneficiarydetail.setOtherFields(dto.getOtherFields()); + } + + return beneficiarydetail; + } + + /** + * @param identity + * @return + */ + ArrayDeque queue = new ArrayDeque<>(); + + public BeneficiaryCreateResp createIdentity(IdentityDTO identity) { + logger.info("IdentityService.createIdentity - start"); + + List list = null; + MBeneficiaryregidmapping regMap = null; + synchronized (queue) { + if (queue.isEmpty()) { + logger.info("fetching 10000 rows"); + list = regIdRepo.findTop10000ByProvisionedAndReserved(false, false); + logger.info("Adding SynchronousQueue start-- "); + for (MBeneficiaryregidmapping map : list) { + queue.add(map); + } + logger.info("Adding SynchronousQueue end-- "); + } + regMap = queue.removeFirst(); + } + regMap.setReserved(true); + if (regMap.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + regMap.setCreatedDate(ts); + } + + regIdRepo.save(regMap); + + regMap.setProvisioned(true); + + logger.info("IdentityService.createIdentity - saving Address"); + ObjectMapper objectMapper = new ObjectMapper(); + MBeneficiaryaddress mAddr = identityDTOToMBeneficiaryaddress(identity); + // MBeneficiaryaddress mAddr1 = + // mapper.identityDTOToMBeneficiaryaddress(identity); + logger.info("identity.getIsPermAddrSameAsCurrAddr = " + identity.getIsPermAddrSameAsCurrAddr()); + if (Boolean.TRUE.equals(identity.getIsPermAddrSameAsCurrAddr())) { + logger.debug("identity.getCurrentAddress = " + identity.getCurrentAddress()); + mAddr.setPermanentAddress(identity.getCurrentAddress()); + } + + logger.info("identity.getIsPermAddrSameAsEmerAddr = " + identity.getIsPermAddrSameAsEmerAddr()); + if (Boolean.TRUE.equals(identity.getIsPermAddrSameAsEmerAddr())) { + logger.debug("identity.getEmergencyAddress = " + identity.getEmergencyAddress()); + mAddr.setPermanentAddress(identity.getEmergencyAddress()); + } + + logger.info("identity.getIsEmerAddrSameAsCurrAddr = " + identity.getIsEmerAddrSameAsCurrAddr()); + if (Boolean.TRUE.equals(identity.getIsEmerAddrSameAsCurrAddr())) { + logger.debug("identity.getCurrentAddress = " + identity.getCurrentAddress()); + mAddr.setEmergencyAddress(identity.getCurrentAddress()); + } + + logger.info("identity.getIsEmerAddrSameAsPermAddr = " + identity.getIsEmerAddrSameAsPermAddr()); + if (Boolean.TRUE.equals(identity.getIsEmerAddrSameAsPermAddr())) { + logger.debug("identity.getPermanentAddress = " + identity.getPermanentAddress()); + mAddr.setEmergencyAddress(identity.getPermanentAddress()); + } + if (mAddr.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + mAddr.setCreatedDate(ts); + } + + mAddr = addressRepo.save(mAddr); + logger.info("IdentityService.createIdentity - Address saved - id = " + mAddr.getBenAddressID()); + + // Update van serial no for data sync + addressRepo.updateVanSerialNo(mAddr.getBenAddressID()); + + MBeneficiaryconsent mConsnt = mapper.identityDTOToDefaultMBeneficiaryconsent(identity, true, false); + logger.info("IdentityService.createIdentity - saving Consent"); + if (mConsnt.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + mConsnt.setCreatedDate(ts); + } + mConsnt = consentRepo.save(mConsnt); + logger.info("IdentityService.createIdentity - Consent saved - id = " + mConsnt.getBenConsentID()); + + // Update van serial no for data sync + consentRepo.updateVanSerialNo(mConsnt.getBenConsentID()); + + logger.info("IdentityService.createIdentity - saving Contacts"); + MBeneficiarycontact mContc = identityDTOToMBeneficiarycontact(identity); + + if (mContc.getEmergencyContactNum() != null) { + mContc.setEmergencyContactNum(cleanPhoneNumber(mContc.getEmergencyContactNum())); + } + + if (mContc.getPhoneNum1() != null) { + mContc.setPhoneNum1(cleanPhoneNumber(mContc.getPhoneNum1())); + } + + if (mContc.getPhoneNum2() != null) { + mContc.setPhoneNum2(cleanPhoneNumber(mContc.getPhoneNum2())); + } + if (mContc.getPhoneNum3() != null) { + mContc.setPhoneNum3(cleanPhoneNumber(mContc.getPhoneNum3())); + } + + if (mContc.getPhoneNum4() != null) { + mContc.setPhoneNum4(cleanPhoneNumber(mContc.getPhoneNum4())); + } + if (mContc.getPhoneNum5() != null) { + mContc.setPhoneNum5(cleanPhoneNumber(mContc.getPhoneNum5())); + } + if (mContc.getPreferredSMSPhoneNum() != null) { + mContc.setPreferredSMSPhoneNum(cleanPhoneNumber(mContc.getPreferredSMSPhoneNum())); + } + if (mContc.getPreferredPhoneNum() != null) { + mContc.setPreferredPhoneNum(cleanPhoneNumber(mContc.getPreferredPhoneNum())); + } + + // MBeneficiarycontact mContc = + // mapper.identityDTOToMBeneficiarycontact(identity); + if (mContc.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + mContc.setCreatedDate(ts); + } + mContc = contactRepo.save(mContc); + logger.info("IdentityService.createIdentity - Contacts saved - id = " + mContc.getBenContactsID()); + + // Update van serial no for data sync + contactRepo.updateVanSerialNo(mContc.getBenContactsID()); + + logger.info("IdentityService.createIdentity - saving Details"); + // MBeneficiarydetail mDetl = mapper.identityDTOToMBeneficiarydetail(identity); + MBeneficiarydetail mDetl = convertIdentityDTOToMBeneficiarydetail(identity); + + if (mDetl.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + mDetl.setCreatedDate(ts); + } + mDetl = detailRepo.save(mDetl); + logger.info("IdentityService.createIdentity - Details saved - id = " + mDetl.getBeneficiaryDetailsId()); + + // Update van serial no for data sync + detailRepo.updateVanSerialNo(mDetl.getBeneficiaryDetailsId()); + + MBeneficiaryAccount bankOBJ = mapper.identityDTOToMBeneficiaryAccount(identity); + if (bankOBJ.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + bankOBJ.setCreatedDate(ts); + } + bankOBJ = accountRepo.save(bankOBJ); + // Update van serial no for data sync + accountRepo.updateVanSerialNo(bankOBJ.getBenAccountID()); + + // MBeneficiaryImage benImageOBJ = mapper.identityDTOToMBeneficiaryImage(identity); + MBeneficiaryImage benImageOBJ = identityDTOToMBeneficiaryImage(identity); + + if (benImageOBJ.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + benImageOBJ.setCreatedDate(ts); + } + benImageOBJ = imageRepo.save(benImageOBJ); + + // Update van serial no for data sync + imageRepo.updateVanSerialNo(benImageOBJ.getBenImageId()); + + logger.info("IdentityService.createIdentity - saving Mapping"); + MBeneficiarymapping benMapping = mapper.identityDTOToMBeneficiarymapping(identity); + + benMapping.setMBeneficiarycontact(mContc); + benMapping.setMBeneficiaryaddress(mAddr); + benMapping.setMBeneficiaryconsent(mConsnt); + benMapping.setMBeneficiarydetail(mDetl); + benMapping.setMBeneficiaryregidmapping(regMap); + benMapping.setMBeneficiaryImage(benImageOBJ); + benMapping.setMBeneficiaryAccount(bankOBJ); + + regMap.setProviderServiceMapID(identity.getProviderServiceMapId()); + // added columns for data sync + // 17-09-2018 + if (identity.getVanID() != null) { + regMap.setVanID(identity.getVanID()); + } + if (identity.getParkingPlaceId() != null) { + regMap.setParkingPlaceID(identity.getParkingPlaceId()); + } + regMap.setVanSerialNo(regMap.getBenRegId()); + // END + + regIdRepo.save(regMap); + if (benMapping.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + benMapping.setCreatedDate(ts); + } + + if (identity.getBenFamilyDTOs().get(0).getVanID() != null) { + benMapping.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); + } + + benMapping = mappingRepo.save(benMapping); + // Update van serial no for data sync + mappingRepo.updateVanSerialNo(benMapping.getBenMapId()); + + final MBeneficiarymapping benMapping2 = benMapping; + logger.info("IdentityService.createIdentity - saving FamilyMaps"); + List fIdenList = null; + List fList = null; + + // new logic (18-09-2018, Neeraj kumar) + if (null != identity.getBenFamilyDTOs()) { + fIdenList = mapper.identityDTOListToMBeneficiaryfamilymappingList(identity.getBenFamilyDTOs()); + if (fIdenList != null) { + for (MBeneficiaryfamilymapping bfMapping : fIdenList) { + bfMapping.setBenMapId(benMapping2.getBenMapId()); + + if (bfMapping.getVanID() == null && identity.getVanID() != null) { + bfMapping.setVanID(identity.getVanID()); + } + if (bfMapping.getVanID() == null && identity.getBenFamilyDTOs().get(0).getVanID() != null) { + bfMapping.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); + } + + if (bfMapping.getParkingPlaceID() == null && identity.getParkingPlaceId() != null) { + bfMapping.setParkingPlaceID(identity.getParkingPlaceId()); + } + + if (bfMapping.getAssociatedBenRegId() == null) { + bfMapping.setAssociatedBenRegId(benMapping2.getBenRegId()); + } + } + fList = (List) familyMapRepo.saveAll(fIdenList); + // Update van serial no for data sync + if (fList != null && !fList.isEmpty()) { + for (MBeneficiaryfamilymapping obj : fList) { + familyMapRepo.updateVanSerialNo(obj.getBenFamilyMapId()); + } + } + } + } + + logger.info("IdentityService.createIdentity - FamilyMap saved "); + logger.info("IdentityService.createIdentity - saving Service Map"); + MBeneficiaryservicemapping sMap = mapper.identityDTOToMBeneficiaryservicemapping(identity); + sMap.setBenMapId(benMapping.getBenMapId()); + if (sMap.getCreatedDate() == null) { + SimpleDateFormat sdf = new SimpleDateFormat(CREATED_DATE_FORMAT); + String dateToStoreInDataBase = sdf.format(new Date()); + Timestamp ts = Timestamp.valueOf(dateToStoreInDataBase); + sMap.setCreatedDate(ts); + } + sMap = serviceMapRepo.save(sMap); + logger.info("IdentityService.createIdentity - ServiceMap saved - id = " + sMap.getBenServiceMapID()); + + // Update van serial no for data sync + serviceMapRepo.updateVanSerialNo(sMap.getBenServiceMapID()); + + List sList = new ArrayList<>(); + sList.add(sMap); + logger.info("IdentityService.createIdentity - saving Identity"); + List mIdenList2 = new ArrayList<>(); + if (null != identity.getIdentities()) { + List mIdenList = mapper + .identityDTOListToMBeneficiaryidentityList(identity.getIdentities()); + mIdenList.forEach(mIden -> { + mIden.setBenMapId(benMapping2.getBenMapId()); + mIden.setCreatedBy(identity.getAgentName()); + mIden.setCreatedDate(identity.getEventTypeDate()); + + // set new column(vanID, parkingPlaceID) value for data sync + if (identity.getVanID() != null) { + mIden.setVanID(identity.getVanID()); + } + if (identity.getParkingPlaceId() != null) { + mIden.setParkingPlaceID(identity.getParkingPlaceId()); + } + + MBeneficiaryidentity m = identityRepo.save(mIden); + + // Update van serial no for data sync + identityRepo.updateVanSerialNo(m.getBenIdentityId()); + + mIdenList2.add(m); + logger.info("IdentityService.createIdentity - Identity saved - id = " + m.getBenIdentityId()); + }); + } + + // return partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); + logger.info("IdentityService.createIdentity - end. id = " + benMapping.getBenMapId()); + + BeneficiaryCreateResp response = partialMapper.mBeneficiarymappingToBeneficiaryCreateResp(benMapping); // Trigger async sync to Elasticsearch -if (regMap != null && regMap.getBenRegId() != null) { - logger.info("Triggering Elasticsearch sync for benRegId: {}", regMap.getBenRegId()); - realtimeSyncService.syncBeneficiaryAsync(regMap.getBenRegId()); -} - -return response; - } - - private MBeneficiarydetail convertIdentityDTOToMBeneficiarydetail(IdentityDTO dto) { - MBeneficiarydetail beneficiarydetail = new MBeneficiarydetail(); - beneficiarydetail.setAreaId(dto.getAreaId()); - if (null != dto.getBeneficiaryRegId()) - beneficiarydetail.setBeneficiaryRegID(BigInteger.valueOf(dto.getBeneficiaryRegId())); - beneficiarydetail.setCommunity(dto.getCommunity()); - beneficiarydetail.setCommunityId(dto.getCommunityId()); - beneficiarydetail.setDob(dto.getDob()); - beneficiarydetail.setEducation(dto.getEducation()); - beneficiarydetail.setEducationId(dto.getEducationId()); - beneficiarydetail.setEmergencyRegistration(dto.getEmergencyRegistration()); - beneficiarydetail.setHealthCareWorkerId(dto.getHealthCareWorkerId()); - beneficiarydetail.setHealthCareWorker(dto.getHealthCareWorker()); - beneficiarydetail.setFatherName(dto.getFatherName()); - beneficiarydetail.setMotherName(dto.getMotherName()); - beneficiarydetail.setFirstName(dto.getFirstName()); - beneficiarydetail.setGender(dto.getGender()); - beneficiarydetail.setGenderId(dto.getGenderId()); - beneficiarydetail.setIncomeStatus(dto.getIncomeStatus()); - beneficiarydetail.setMonthlyFamilyIncome(dto.getMonthlyFamilyIncome()); - beneficiarydetail.setIncomeStatusId(dto.getIncomeStatusId()); - beneficiarydetail.setLastName(dto.getLastName()); - beneficiarydetail.setMaritalStatusId(dto.getMaritalStatusId()); - beneficiarydetail.setMaritalStatus(dto.getMaritalStatus()); - beneficiarydetail.setMiddleName(dto.getMiddleName()); - beneficiarydetail.setOccupation(dto.getOccupationName()); - beneficiarydetail.setOccupationId(dto.getOccupationId()); - beneficiarydetail.setPhcId(dto.getPhcId()); - beneficiarydetail.setPlaceOfWork(dto.getPlaceOfWork()); - beneficiarydetail.setPreferredLanguageId(dto.getPreferredLanguageId()); - beneficiarydetail.setPreferredLanguage(dto.getPreferredLanguage()); - beneficiarydetail.setReligion(dto.getReligion()); - if (dto.getFaceEmbedding() != null) - beneficiarydetail.setFaceEmbedding(dto.getFaceEmbedding().toString()); - if (dto.getReligionId() != null) - beneficiarydetail.setReligionId(BigInteger.valueOf(dto.getReligionId())); - beneficiarydetail.setRemarks(dto.getRemarks()); - if (dto.getServicePointId() != null) - beneficiarydetail.setServicePointId(BigInteger.valueOf(dto.getServicePointId())); - beneficiarydetail.setSourceOfInfo(dto.getSourceOfInfo()); - beneficiarydetail.setSpouseName(dto.getSpouseName()); - beneficiarydetail.setStatus(dto.getStatus()); - beneficiarydetail.setTitle(dto.getTitle()); - beneficiarydetail.setTitleId(dto.getTitleId()); - beneficiarydetail.setZoneId(dto.getZoneId()); - beneficiarydetail.setCreatedBy(dto.getAgentName()); - beneficiarydetail.setCreatedDate(dto.getCreatedDate()); - beneficiarydetail.setIsHIVPositive(MBeneficiarydetail.setIsHIVPositive(dto.getIsHIVPositive())); - beneficiarydetail.setAgeAtMarriage( - MBeneficiarydetail.getAgeAtMarriageCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); - beneficiarydetail.setMarriageDate( - MBeneficiarydetail.getMarriageDateCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); - beneficiarydetail.setVanID(dto.getVanID()); - beneficiarydetail.setParkingPlaceID(dto.getParkingPlaceId()); - if(dto.getOtherFields() != null) - beneficiarydetail.setOtherFields(dto.getOtherFields()); - if(dto.getLiteracyStatus() != null) - beneficiarydetail.setLiteracyStatus(dto.getLiteracyStatus()); - return beneficiarydetail; - } - - private MBeneficiaryImage identityDTOToMBeneficiaryImage(IdentityDTO identity) { - MBeneficiaryImage beneficiaryImage = new MBeneficiaryImage(); - - beneficiaryImage.setBenImage(identity.getBenImage()); - beneficiaryImage.setCreatedBy(identity.getAgentName()); - beneficiaryImage.setCreatedDate(identity.getCreatedDate()); - if (identity.getVanID() != null) - beneficiaryImage.setVanID(identity.getVanID()); - if (identity.getBenFamilyDTOs() != null) - beneficiaryImage.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); - - beneficiaryImage.setParkingPlaceID(identity.getParkingPlaceId()); - - return beneficiaryImage; - } - - private MBeneficiarycontact identityDTOToMBeneficiarycontact(IdentityDTO dto) { - MBeneficiarycontact beneficiaryContact = new MBeneficiarycontact(); - if (dto.getContact() != null) { - beneficiaryContact.setPreferredPhoneNum(dto.getContact().getPreferredPhoneNum()); - beneficiaryContact.setPreferredPhoneTyp(dto.getContact().getPreferredPhoneTyp()); - beneficiaryContact.setPreferredSMSPhoneNum(dto.getContact().getPreferredSMSPhoneNum()); - beneficiaryContact.setPreferredSMSPhoneTyp(dto.getContact().getPreferredSMSPhoneTyp()); - beneficiaryContact.setEmergencyContactNum(dto.getContact().getEmergencyContactNum()); - beneficiaryContact.setPhoneNum1(dto.getContact().getPhoneNum1()); - beneficiaryContact.setPhoneTyp1(dto.getContact().getPhoneTyp1()); - beneficiaryContact.setPhoneNum2(dto.getContact().getPhoneNum2()); - beneficiaryContact.setPhoneTyp2(dto.getContact().getPhoneTyp2()); - beneficiaryContact.setPhoneNum3(dto.getContact().getPhoneNum3()); - beneficiaryContact.setPhoneTyp3(dto.getContact().getPhoneTyp3()); - beneficiaryContact.setPhoneNum4(dto.getContact().getPhoneNum4()); - beneficiaryContact.setPhoneTyp4(dto.getContact().getPhoneTyp4()); - beneficiaryContact.setPhoneNum5(dto.getContact().getPhoneNum5()); - beneficiaryContact.setPhoneTyp5(dto.getContact().getPhoneTyp5()); - } - beneficiaryContact.setEmailId(dto.getPreferredEmailId()); - beneficiaryContact.setCreatedBy(dto.getAgentName()); - beneficiaryContact.setCreatedDate(dto.getCreatedDate()); - beneficiaryContact.setVanID(dto.getVanID()); - beneficiaryContact.setParkingPlaceID(dto.getParkingPlaceId()); - return beneficiaryContact; - } - - private MBeneficiaryaddress identityDTOToMBeneficiaryaddress(IdentityDTO dto) { - MBeneficiaryaddress beneficiaryAddress = new MBeneficiaryaddress(); - if (dto.getCurrentAddress() != null) { - beneficiaryAddress.setCurrAddrLine1(dto.getCurrentAddress().getAddrLine1()); - beneficiaryAddress.setCurrAddrLine2(dto.getCurrentAddress().getAddrLine2()); - beneficiaryAddress.setCurrAddrLine3(dto.getCurrentAddress().getAddrLine3()); - beneficiaryAddress.setCurrCountryId(dto.getCurrentAddress().getCountryId()); - beneficiaryAddress.setCurrCountry(dto.getCurrentAddress().getCountry()); - beneficiaryAddress.setCurrStateId(dto.getCurrentAddress().getStateId()); - beneficiaryAddress.setCurrState(dto.getCurrentAddress().getState()); - beneficiaryAddress.setCurrDistrictId(dto.getCurrentAddress().getDistrictId()); - beneficiaryAddress.setCurrDistrict(dto.getCurrentAddress().getDistrict()); - beneficiaryAddress.setCurrSubDistrictId(dto.getCurrentAddress().getSubDistrictId()); - beneficiaryAddress.setCurrSubDistrict(dto.getCurrentAddress().getSubDistrict()); - beneficiaryAddress.setCurrVillageId(dto.getCurrentAddress().getVillageId()); - beneficiaryAddress.setCurrVillage(dto.getCurrentAddress().getVillage()); - beneficiaryAddress.setCurrAddressValue(dto.getCurrentAddress().getAddressValue()); - beneficiaryAddress.setCurrPinCode(dto.getCurrentAddress().getPinCode()); - beneficiaryAddress.setCurrZoneID(dto.getCurrentAddress().getZoneID()); - beneficiaryAddress.setCurrZone(dto.getCurrentAddress().getZoneName()); - beneficiaryAddress.setCurrAreaId(dto.getCurrentAddress().getParkingPlaceID()); - beneficiaryAddress.setCurrArea(dto.getCurrentAddress().getParkingPlaceName()); - beneficiaryAddress.setCurrServicePointId(dto.getCurrentAddress().getServicePointID()); - beneficiaryAddress.setCurrServicePoint(dto.getCurrentAddress().getServicePointName()); - beneficiaryAddress.setCurrHabitation(dto.getCurrentAddress().getHabitation()); - } - if (dto.getEmergencyAddress() != null) { - beneficiaryAddress.setEmerAddrLine1(dto.getEmergencyAddress().getAddrLine1()); - beneficiaryAddress.setEmerAddrLine2(dto.getEmergencyAddress().getAddrLine2()); - beneficiaryAddress.setEmerAddrLine3(dto.getEmergencyAddress().getAddrLine3()); - beneficiaryAddress.setEmerCountryId(dto.getEmergencyAddress().getCountryId()); - beneficiaryAddress.setEmerCountry(dto.getEmergencyAddress().getCountry()); - beneficiaryAddress.setEmerStateId(dto.getEmergencyAddress().getStateId()); - beneficiaryAddress.setEmerState(dto.getEmergencyAddress().getState()); - beneficiaryAddress.setEmerDistrictId(dto.getEmergencyAddress().getDistrictId()); - beneficiaryAddress.setEmerDistrict(dto.getEmergencyAddress().getDistrict()); - beneficiaryAddress.setEmerSubDistrictId(dto.getEmergencyAddress().getSubDistrictId()); - beneficiaryAddress.setEmerSubDistrict(dto.getEmergencyAddress().getSubDistrict()); - beneficiaryAddress.setEmerVillageId(dto.getEmergencyAddress().getVillageId()); - beneficiaryAddress.setEmerVillage(dto.getEmergencyAddress().getVillage()); - beneficiaryAddress.setEmerAddressValue(dto.getEmergencyAddress().getAddressValue()); - beneficiaryAddress.setEmerPinCode(dto.getEmergencyAddress().getPinCode()); - beneficiaryAddress.setEmerZoneID(dto.getEmergencyAddress().getZoneID()); - beneficiaryAddress.setEmerZone(dto.getEmergencyAddress().getZoneName()); - beneficiaryAddress.setEmerAreaId(dto.getEmergencyAddress().getParkingPlaceID()); - beneficiaryAddress.setEmerArea(dto.getEmergencyAddress().getParkingPlaceName()); - beneficiaryAddress.setEmerServicePointId(dto.getEmergencyAddress().getServicePointID()); - beneficiaryAddress.setEmerServicePoint(dto.getEmergencyAddress().getServicePointName()); - beneficiaryAddress.setEmerHabitation(dto.getEmergencyAddress().getHabitation()); - } - - if (dto.getPermanentAddress() != null) { - beneficiaryAddress.setPermAddrLine1(dto.getPermanentAddress().getAddrLine1()); - beneficiaryAddress.setPermAddrLine2(dto.getPermanentAddress().getAddrLine2()); - beneficiaryAddress.setPermAddrLine3(dto.getPermanentAddress().getAddrLine3()); - beneficiaryAddress.setPermCountryId(dto.getPermanentAddress().getCountryId()); - beneficiaryAddress.setPermCountry(dto.getPermanentAddress().getCountry()); - beneficiaryAddress.setPermStateId(dto.getPermanentAddress().getStateId()); - beneficiaryAddress.setPermState(dto.getPermanentAddress().getState()); - beneficiaryAddress.setPermDistrictId(dto.getPermanentAddress().getDistrictId()); - beneficiaryAddress.setPermDistrict(dto.getPermanentAddress().getDistrict()); - beneficiaryAddress.setPermSubDistrictId(dto.getPermanentAddress().getSubDistrictId()); - beneficiaryAddress.setPermSubDistrict(dto.getPermanentAddress().getSubDistrict()); - beneficiaryAddress.setPermVillageId(dto.getPermanentAddress().getVillageId()); - beneficiaryAddress.setPermVillage(dto.getPermanentAddress().getVillage()); - beneficiaryAddress.setPermAddressValue(dto.getPermanentAddress().getAddressValue()); - beneficiaryAddress.setPermPinCode(dto.getPermanentAddress().getPinCode()); - - beneficiaryAddress.setPermZoneID(dto.getPermanentAddress().getZoneID()); - beneficiaryAddress.setPermZone(dto.getPermanentAddress().getZoneName()); - beneficiaryAddress.setPermAreaId(dto.getPermanentAddress().getParkingPlaceID()); - beneficiaryAddress.setPermArea(dto.getPermanentAddress().getParkingPlaceName()); - beneficiaryAddress.setPermServicePointId(dto.getPermanentAddress().getServicePointID()); - beneficiaryAddress.setPermServicePoint(dto.getPermanentAddress().getServicePointName()); - beneficiaryAddress.setPermHabitation(dto.getPermanentAddress().getHabitation()); - } - if (dto.getAgentName() != null) { - beneficiaryAddress.setCreatedBy(dto.getAgentName()); - } - if (dto.getCreatedDate() != null) { - beneficiaryAddress.setCreatedDate(dto.getCreatedDate()); - } - if (dto.getVanID() != null) { - beneficiaryAddress.setVanID(dto.getVanID()); - } - if (dto.getParkingPlaceId() != null) { - beneficiaryAddress.setParkingPlaceID(dto.getParkingPlaceId()); - } - - return beneficiaryAddress; - } - - public String getReservedIdList() { - - return "success"; - } - - /** - * - * @param reserveIdentityDTO - * @return - */ - public String reserveIdentity(ReserveIdentityDTO reserveIdentityDTO) { - - Long availableCount = regIdRepo.countByProviderServiceMapIDAndVehicalNoOrderByBenRegIdAsc( - reserveIdentityDTO.getProviderServiceMapID(), reserveIdentityDTO.getVehicalNo()); - - if (reserveIdentityDTO.getReserveCount() < availableCount) { - - MBeneficiaryregidmapping beneficiaryregidmapping; - Long countToBeAllocate = reserveIdentityDTO.getReserveCount() - availableCount; - countToBeAllocate++; - for (int index = 0; index < countToBeAllocate; index++) { - - beneficiaryregidmapping = regIdRepo.findFirstByProviderServiceMapIDAndVehicalNoOrderByBenRegIdAsc(null, - null); - beneficiaryregidmapping.setProviderServiceMapID(reserveIdentityDTO.getProviderServiceMapID()); - beneficiaryregidmapping.setVehicalNo(reserveIdentityDTO.getVehicalNo()); - regIdRepo.save(beneficiaryregidmapping); - } - } - - return "Successfully Completed"; - } - - public String unReserveIdentity(ReserveIdentityDTO unReserve) { - - regIdRepo.unreserveBeneficiaryIds(unReserve.getProviderServiceMapID(), unReserve.getVehicalNo()); - return "Successfully Completed"; - } - - /** - * Get partial details of beneficiaries (first name middle name and last name) - * list on benId's list - * - * @param BenRegIds - * @return {@link List} Beneficiaries - */ - - public List getBeneficiariesPartialDeatilsByBenRegIdList(List benRegIds) { - - logger.info("IdentityService.getBeneficiariesPartialDeatilsByBenRegId - end"); - List list = new ArrayList<>(); - - // new logic, 19-12-2018 - List benMapObjArr = null; - if (benRegIds != null && !benRegIds.isEmpty()) { - benMapObjArr = mappingRepo.getBenMappingByRegIDList(benRegIds); - if (benMapObjArr != null && !benMapObjArr.isEmpty()) { - for (Object[] objArr : benMapObjArr) { - MBeneficiarymapping benMap = this.getBeneficiariesDTONewPartial(objArr); - - list.add(partialMapper.mBeneficiarymappingToBeneficiariesPartialDTO(benMap)); - } - } - logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); - - } - // end - logger.info("IdetityService.getBeneficiariesPartialDeatilsByBenRegId - end"); - - return list; - } - - /** - * Get partial details of beneficiaries (first name middle name and last name) - * list on benId's list - * - * @param benRegIds - * @return {@link List} Beneficiaries - */ - public List getBeneficiariesDeatilsByBenRegIdList(List benRegIds) { - - logger.info("IdentityService.getBeneficiariesDeatilsByBenRegIdList - end"); - List list = new ArrayList<>(); - - // new logic, 19-12-2018 - List benMapObjArr = null; - if (benRegIds != null && !benRegIds.isEmpty()) { - benMapObjArr = mappingRepo.getBenMappingByRegIDList(benRegIds); - if (benMapObjArr != null && !benMapObjArr.isEmpty()) { - for (Object[] objArr : benMapObjArr) { - MBeneficiarymapping benMap = this.getBeneficiariesDTONew(objArr); - list.add(this.getBeneficiariesDTO(benMap)); - } - } - logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); - } - // end - logger.info("IdetityService.getBeneficiariesPartialDeatilsByBenRegId - end"); - - return list; - } - - /** - * - * @param benMap - * @return - */ - private BeneficiariesDTO getBeneficiariesDTO(MBeneficiarymapping benMap) { - BeneficiariesDTO bdto = mapper.mBeneficiarymappingToBeneficiariesDTO(benMap); - if (null != benMap && null != benMap.getMBeneficiarydetail() - && !StringUtils.isEmpty(benMap.getMBeneficiarydetail().getFaceEmbedding())) { - String faceEmbedding = benMap.getMBeneficiarydetail().getFaceEmbedding(); - String trimmedInput = faceEmbedding.replaceAll("[\\[\\]]", ""); - List floatList = new ArrayList<>(); - if(!StringUtils.isEmpty(trimmedInput)) { - String[] stringNumbers = trimmedInput.split(",\\s*"); - for (String str : stringNumbers) { - floatList.add(Float.parseFloat(str)); - } - } - bdto.setFaceEmbedding(floatList); - } - // bdto.setOtherFields(benMap.getMBeneficiarydetail().getOtherFields()); - bdto.setBeneficiaryFamilyTags( - mapper.mapToMBeneficiaryfamilymappingWithBenFamilyDTOList(benMap.getMBeneficiaryfamilymappings())); - bdto.setBeneficiaryIdentites( - mapper.mBeneficiaryidentityListToBenIdentityDTOList(benMap.getMBeneficiaryidentities())); - - List abhaList = v_BenAdvanceSearchRepo.getBenAbhaDetailsByBenRegID(bdto.getBenRegId()); - if (abhaList != null && !abhaList.isEmpty()) { - List abhaDTOList = new ArrayList<>(); - AbhaAddressDTO abhaDTO; - for (Object[] objArr : abhaList) { - abhaDTO = new AbhaAddressDTO(); - abhaDTO.setBeneficiaryRegID(bdto.getBenRegId()); - if (objArr[1] != null) - abhaDTO.setHealthID(objArr[1].toString()); - if (objArr[2] != null) - abhaDTO.setHealthIDNumber(objArr[2].toString()); - if (objArr[3] != null) - abhaDTO.setAuthenticationMode(objArr[3].toString()); - if (objArr[4] != null) - abhaDTO.setCreatedDate((Timestamp) objArr[4]); - - abhaDTOList.add(abhaDTO); - - } - bdto.setAbhaDetails(abhaDTOList); - } - return bdto; - } - - private BeneficiariesDTO mBeneficiarymappingToBeneficiariesDTO(MBeneficiarymapping benMap) { - // TODO Auto-generated method stub - return null; - } - - /** - * finite search - * - * @param identityDTO - * @return - */ - public List getBeneficiaries(IdentityDTO identityDTO) { - List list = new ArrayList<>(); - - List benMapList = mappingRepo.finiteSearch(identityDTO); - for (MBeneficiarymapping benMap : benMapList) { - list.add(this.getBeneficiariesDTO(benMap)); - logger.info("benMapId: " + benMap.getBenMapId() + " :: BenId: " - + benMap.getMBeneficiaryregidmapping().getBeneficiaryID()); - } - - return list; - } - - /*** - * - * @return beneficiary image for beneficiary Reg ID. - */ - public String getBeneficiaryImage(String requestOBJ) { - OutputResponse response = new OutputResponse(); - try { - Map benImageMap = new HashMap<>(); - if (requestOBJ != null) { - - JsonElement jsnElmnt = JsonParser.parseString(requestOBJ); - JsonObject obj = jsnElmnt.getAsJsonObject(); - - if (obj != null && obj.has("beneficiaryRegID") && obj.get("beneficiaryRegID") != null) { - MBeneficiarymapping benMap = mappingRepo - .getBenImageIdByBenRegID(obj.get("beneficiaryRegID").getAsBigInteger()); - - if (benMap != null && benMap.getBenImageId() != null && benMap.getVanID() != null) { - MBeneficiaryImage benImageOBJ = imageRepo.getBenImageByBenImageID(benMap.getBenImageId(), - benMap.getVanID()); - benImageMap.put("benImage", benImageOBJ.getBenImage()); - benImageMap.put("createdDate", benImageOBJ.getCreatedDate()); - response.setResponse( - new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create() - .toJson(benImageMap)); - } else { - response.setResponse("Image not available"); - } - } else { - response.setError(5000, "Invalid request"); - } - } - } catch (Exception e) { - logger.error("Error while beneficiary image fetching" + e); - response.setError(e); - } - - return response.toString(); - } - - public void editIdentityEducationOrCommunity(IdentityEditDTO identity) throws MissingMandatoryFieldsException { - logger.info("IdentityService.editIdentityEducationorCommunity - start"); - if (identity.getBeneficiaryRegId() == null && null == identity.getBeneficaryId()) { - throw new MissingMandatoryFieldsException("Either of BeneficiaryID or Beneficiary Reg Id is mandatory."); - } - - // new logic : 13-11-2018 - MBeneficiarymapping benMapping = mappingRepo.findByBenRegIdOrderByBenMapIdAsc(identity.getBeneficiaryRegId()); - if (benMapping != null && benMapping.getBenDetailsId() != null && benMapping.getVanID() != null) { - if (identity.getCommunityId() != null) { - detailRepo.updateCommunity(benMapping.getBenDetailsId(), benMapping.getVanID(), - identity.getCommunityId()); - } - if (identity.getEducationId() != null) { - detailRepo.updateEducation(benMapping.getBenDetailsId(), benMapping.getVanID(), - identity.getEducationId()); - } - } - - } - - public int importBenIdToLocalServer(List benIdImportDTOList) { - logger.info("IdentityService.importBenIdToLocalServer - start"); - logger.info("IdentityService.importBenIdToLocalServer - benIdImportDTOList size : " - + (benIdImportDTOList == null ? "size:0" : benIdImportDTOList.size())); - if (!benIdImportDTOList.isEmpty()) { - ArrayList mBeneficiaryregidmappingList = benIdImportMapper - .benIdImportDTOToMBeneficiaryregidmappings(benIdImportDTOList); -logger.info("Inside if block of importBenIdToLocalServer"); - jdbcTemplate = getJdbcTemplate(); - List dataList = new ArrayList<>(); - Object[] objArr; - String query = " INSERT INTO m_beneficiaryregidmapping(BenRegId, BeneficiaryID, " - + " Provisioned, CreatedDate, CreatedBy, Reserved) VALUES (?,?,?,?,?,?) "; -logger.info("query : " + query); - for (MBeneficiaryregidmapping obj : mBeneficiaryregidmappingList) { - objArr = new Object[6]; - - objArr[0] = obj.getBenRegId(); - objArr[1] = obj.getBeneficiaryID(); - objArr[2] = false; - objArr[3] = obj.getCreatedDate(); - objArr[4] = obj.getCreatedBy(); - objArr[5] = false; - - dataList.add(objArr); - logger.info("regid :" + obj.getBenRegId() + " - benid :" + obj.getBeneficiaryID()); - } - - int[] i = jdbcTemplate.batchUpdate(query, dataList); - - return i.length; - } else - return 0; - - } - - public Long checkBenIDAvailabilityLocal() { - return regIdRepo.countByProvisioned(false); - - } -} + if (regMap != null && regMap.getBenRegId() != null) { + logger.info("Triggering Elasticsearch sync for benRegId: {}", regMap.getBenRegId()); + syncService.syncBeneficiaryAsync(regMap.getBenRegId()); + } + + return response; + } + + private String cleanPhoneNumber(String phoneNumber) { + if (phoneNumber == null || phoneNumber.trim().isEmpty()) { + return phoneNumber; + } + + // Remove +91 prefix + String cleaned = phoneNumber.trim(); + if (cleaned.startsWith("+91")) { + cleaned = cleaned.substring(3); + } else if (cleaned.startsWith("91") && cleaned.length() == 12) { + // Handle case where + is already removed but 91 remains + cleaned = cleaned.substring(2); + } + + return cleaned.trim(); + } + + private MBeneficiarydetail convertIdentityDTOToMBeneficiarydetail(IdentityDTO dto) { + MBeneficiarydetail beneficiarydetail = new MBeneficiarydetail(); + beneficiarydetail.setAreaId(dto.getAreaId()); + if (null != dto.getBeneficiaryRegId()) { + beneficiarydetail.setBeneficiaryRegID(BigInteger.valueOf(dto.getBeneficiaryRegId())); + } + beneficiarydetail.setCommunity(dto.getCommunity()); + beneficiarydetail.setCommunityId(dto.getCommunityId()); + beneficiarydetail.setDob(dto.getDob()); + beneficiarydetail.setEducation(dto.getEducation()); + beneficiarydetail.setEducationId(dto.getEducationId()); + beneficiarydetail.setEmergencyRegistration(dto.getEmergencyRegistration()); + beneficiarydetail.setHealthCareWorkerId(dto.getHealthCareWorkerId()); + beneficiarydetail.setHealthCareWorker(dto.getHealthCareWorker()); + beneficiarydetail.setFatherName(dto.getFatherName()); + beneficiarydetail.setMotherName(dto.getMotherName()); + beneficiarydetail.setFirstName(dto.getFirstName()); + beneficiarydetail.setGender(dto.getGender()); + beneficiarydetail.setGenderId(dto.getGenderId()); + beneficiarydetail.setIncomeStatus(dto.getIncomeStatus()); + beneficiarydetail.setMonthlyFamilyIncome(dto.getMonthlyFamilyIncome()); + beneficiarydetail.setIncomeStatusId(dto.getIncomeStatusId()); + beneficiarydetail.setLastName(dto.getLastName()); + beneficiarydetail.setMaritalStatusId(dto.getMaritalStatusId()); + beneficiarydetail.setMaritalStatus(dto.getMaritalStatus()); + beneficiarydetail.setMiddleName(dto.getMiddleName()); + beneficiarydetail.setOccupation(dto.getOccupationName()); + beneficiarydetail.setOccupationId(dto.getOccupationId()); + beneficiarydetail.setPhcId(dto.getPhcId()); + beneficiarydetail.setPlaceOfWork(dto.getPlaceOfWork()); + beneficiarydetail.setPreferredLanguageId(dto.getPreferredLanguageId()); + beneficiarydetail.setPreferredLanguage(dto.getPreferredLanguage()); + beneficiarydetail.setReligion(dto.getReligion()); + if (dto.getFaceEmbedding() != null) { + beneficiarydetail.setFaceEmbedding(dto.getFaceEmbedding().toString()); + } + if (dto.getReligionId() != null) { + beneficiarydetail.setReligionId(BigInteger.valueOf(dto.getReligionId())); + } + beneficiarydetail.setRemarks(dto.getRemarks()); + if (dto.getServicePointId() != null) { + beneficiarydetail.setServicePointId(BigInteger.valueOf(dto.getServicePointId())); + } + beneficiarydetail.setSourceOfInfo(dto.getSourceOfInfo()); + beneficiarydetail.setSpouseName(dto.getSpouseName()); + beneficiarydetail.setStatus(dto.getStatus()); + beneficiarydetail.setTitle(dto.getTitle()); + beneficiarydetail.setTitleId(dto.getTitleId()); + beneficiarydetail.setZoneId(dto.getZoneId()); + beneficiarydetail.setCreatedBy(dto.getAgentName()); + beneficiarydetail.setCreatedDate(dto.getCreatedDate()); + beneficiarydetail.setIsHIVPositive(MBeneficiarydetail.setIsHIVPositive(dto.getIsHIVPositive())); + beneficiarydetail.setAgeAtMarriage( + MBeneficiarydetail.getAgeAtMarriageCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); + beneficiarydetail.setMarriageDate( + MBeneficiarydetail.getMarriageDateCalc(dto.getDob(), dto.getMarriageDate(), dto.getAgeAtMarriage())); + beneficiarydetail.setVanID(dto.getVanID()); + beneficiarydetail.setParkingPlaceID(dto.getParkingPlaceId()); + if (dto.getOtherFields() != null) { + beneficiarydetail.setOtherFields(dto.getOtherFields()); + } + if (dto.getLiteracyStatus() != null) { + beneficiarydetail.setLiteracyStatus(dto.getLiteracyStatus()); + } + return beneficiarydetail; + } + + private MBeneficiaryImage identityDTOToMBeneficiaryImage(IdentityDTO identity) { + MBeneficiaryImage beneficiaryImage = new MBeneficiaryImage(); + + beneficiaryImage.setBenImage(identity.getBenImage()); + beneficiaryImage.setCreatedBy(identity.getAgentName()); + beneficiaryImage.setCreatedDate(identity.getCreatedDate()); + if (identity.getVanID() != null) { + beneficiaryImage.setVanID(identity.getVanID()); + } + if (identity.getBenFamilyDTOs() != null) { + beneficiaryImage.setVanID(identity.getBenFamilyDTOs().get(0).getVanID()); + } + + beneficiaryImage.setParkingPlaceID(identity.getParkingPlaceId()); + + return beneficiaryImage; + } + + private MBeneficiarycontact identityDTOToMBeneficiarycontact(IdentityDTO dto) { + MBeneficiarycontact beneficiaryContact = new MBeneficiarycontact(); + if (dto.getContact() != null) { + beneficiaryContact.setPreferredPhoneNum(dto.getContact().getPreferredPhoneNum()); + beneficiaryContact.setPreferredPhoneTyp(dto.getContact().getPreferredPhoneTyp()); + beneficiaryContact.setPreferredSMSPhoneNum(dto.getContact().getPreferredSMSPhoneNum()); + beneficiaryContact.setPreferredSMSPhoneTyp(dto.getContact().getPreferredSMSPhoneTyp()); + beneficiaryContact.setEmergencyContactNum(dto.getContact().getEmergencyContactNum()); + beneficiaryContact.setPhoneNum1(dto.getContact().getPhoneNum1()); + beneficiaryContact.setPhoneTyp1(dto.getContact().getPhoneTyp1()); + beneficiaryContact.setPhoneNum2(dto.getContact().getPhoneNum2()); + beneficiaryContact.setPhoneTyp2(dto.getContact().getPhoneTyp2()); + beneficiaryContact.setPhoneNum3(dto.getContact().getPhoneNum3()); + beneficiaryContact.setPhoneTyp3(dto.getContact().getPhoneTyp3()); + beneficiaryContact.setPhoneNum4(dto.getContact().getPhoneNum4()); + beneficiaryContact.setPhoneTyp4(dto.getContact().getPhoneTyp4()); + beneficiaryContact.setPhoneNum5(dto.getContact().getPhoneNum5()); + beneficiaryContact.setPhoneTyp5(dto.getContact().getPhoneTyp5()); + } + beneficiaryContact.setEmailId(dto.getPreferredEmailId()); + beneficiaryContact.setCreatedBy(dto.getAgentName()); + beneficiaryContact.setCreatedDate(dto.getCreatedDate()); + beneficiaryContact.setVanID(dto.getVanID()); + beneficiaryContact.setParkingPlaceID(dto.getParkingPlaceId()); + return beneficiaryContact; + } + + private MBeneficiaryaddress identityDTOToMBeneficiaryaddress(IdentityDTO dto) { + MBeneficiaryaddress beneficiaryAddress = new MBeneficiaryaddress(); + if (dto.getCurrentAddress() != null) { + beneficiaryAddress.setCurrAddrLine1(dto.getCurrentAddress().getAddrLine1()); + beneficiaryAddress.setCurrAddrLine2(dto.getCurrentAddress().getAddrLine2()); + beneficiaryAddress.setCurrAddrLine3(dto.getCurrentAddress().getAddrLine3()); + beneficiaryAddress.setCurrCountryId(dto.getCurrentAddress().getCountryId()); + beneficiaryAddress.setCurrCountry(dto.getCurrentAddress().getCountry()); + beneficiaryAddress.setCurrStateId(dto.getCurrentAddress().getStateId()); + beneficiaryAddress.setCurrState(dto.getCurrentAddress().getState()); + beneficiaryAddress.setCurrDistrictId(dto.getCurrentAddress().getDistrictId()); + beneficiaryAddress.setCurrDistrict(dto.getCurrentAddress().getDistrict()); + beneficiaryAddress.setCurrSubDistrictId(dto.getCurrentAddress().getSubDistrictId()); + beneficiaryAddress.setCurrSubDistrict(dto.getCurrentAddress().getSubDistrict()); + beneficiaryAddress.setCurrVillageId(dto.getCurrentAddress().getVillageId()); + beneficiaryAddress.setCurrVillage(dto.getCurrentAddress().getVillage()); + beneficiaryAddress.setCurrAddressValue(dto.getCurrentAddress().getAddressValue()); + beneficiaryAddress.setCurrPinCode(dto.getCurrentAddress().getPinCode()); + beneficiaryAddress.setCurrZoneID(dto.getCurrentAddress().getZoneID()); + beneficiaryAddress.setCurrZone(dto.getCurrentAddress().getZoneName()); + beneficiaryAddress.setCurrAreaId(dto.getCurrentAddress().getParkingPlaceID()); + beneficiaryAddress.setCurrArea(dto.getCurrentAddress().getParkingPlaceName()); + beneficiaryAddress.setCurrServicePointId(dto.getCurrentAddress().getServicePointID()); + beneficiaryAddress.setCurrServicePoint(dto.getCurrentAddress().getServicePointName()); + beneficiaryAddress.setCurrHabitation(dto.getCurrentAddress().getHabitation()); + } + if (dto.getEmergencyAddress() != null) { + beneficiaryAddress.setEmerAddrLine1(dto.getEmergencyAddress().getAddrLine1()); + beneficiaryAddress.setEmerAddrLine2(dto.getEmergencyAddress().getAddrLine2()); + beneficiaryAddress.setEmerAddrLine3(dto.getEmergencyAddress().getAddrLine3()); + beneficiaryAddress.setEmerCountryId(dto.getEmergencyAddress().getCountryId()); + beneficiaryAddress.setEmerCountry(dto.getEmergencyAddress().getCountry()); + beneficiaryAddress.setEmerStateId(dto.getEmergencyAddress().getStateId()); + beneficiaryAddress.setEmerState(dto.getEmergencyAddress().getState()); + beneficiaryAddress.setEmerDistrictId(dto.getEmergencyAddress().getDistrictId()); + beneficiaryAddress.setEmerDistrict(dto.getEmergencyAddress().getDistrict()); + beneficiaryAddress.setEmerSubDistrictId(dto.getEmergencyAddress().getSubDistrictId()); + beneficiaryAddress.setEmerSubDistrict(dto.getEmergencyAddress().getSubDistrict()); + beneficiaryAddress.setEmerVillageId(dto.getEmergencyAddress().getVillageId()); + beneficiaryAddress.setEmerVillage(dto.getEmergencyAddress().getVillage()); + beneficiaryAddress.setEmerAddressValue(dto.getEmergencyAddress().getAddressValue()); + beneficiaryAddress.setEmerPinCode(dto.getEmergencyAddress().getPinCode()); + beneficiaryAddress.setEmerZoneID(dto.getEmergencyAddress().getZoneID()); + beneficiaryAddress.setEmerZone(dto.getEmergencyAddress().getZoneName()); + beneficiaryAddress.setEmerAreaId(dto.getEmergencyAddress().getParkingPlaceID()); + beneficiaryAddress.setEmerArea(dto.getEmergencyAddress().getParkingPlaceName()); + beneficiaryAddress.setEmerServicePointId(dto.getEmergencyAddress().getServicePointID()); + beneficiaryAddress.setEmerServicePoint(dto.getEmergencyAddress().getServicePointName()); + beneficiaryAddress.setEmerHabitation(dto.getEmergencyAddress().getHabitation()); + } + + if (dto.getPermanentAddress() != null) { + beneficiaryAddress.setPermAddrLine1(dto.getPermanentAddress().getAddrLine1()); + beneficiaryAddress.setPermAddrLine2(dto.getPermanentAddress().getAddrLine2()); + beneficiaryAddress.setPermAddrLine3(dto.getPermanentAddress().getAddrLine3()); + beneficiaryAddress.setPermCountryId(dto.getPermanentAddress().getCountryId()); + beneficiaryAddress.setPermCountry(dto.getPermanentAddress().getCountry()); + beneficiaryAddress.setPermStateId(dto.getPermanentAddress().getStateId()); + beneficiaryAddress.setPermState(dto.getPermanentAddress().getState()); + beneficiaryAddress.setPermDistrictId(dto.getPermanentAddress().getDistrictId()); + beneficiaryAddress.setPermDistrict(dto.getPermanentAddress().getDistrict()); + beneficiaryAddress.setPermSubDistrictId(dto.getPermanentAddress().getSubDistrictId()); + beneficiaryAddress.setPermSubDistrict(dto.getPermanentAddress().getSubDistrict()); + beneficiaryAddress.setPermVillageId(dto.getPermanentAddress().getVillageId()); + beneficiaryAddress.setPermVillage(dto.getPermanentAddress().getVillage()); + beneficiaryAddress.setPermAddressValue(dto.getPermanentAddress().getAddressValue()); + beneficiaryAddress.setPermPinCode(dto.getPermanentAddress().getPinCode()); + + beneficiaryAddress.setPermZoneID(dto.getPermanentAddress().getZoneID()); + beneficiaryAddress.setPermZone(dto.getPermanentAddress().getZoneName()); + beneficiaryAddress.setPermAreaId(dto.getPermanentAddress().getParkingPlaceID()); + beneficiaryAddress.setPermArea(dto.getPermanentAddress().getParkingPlaceName()); + beneficiaryAddress.setPermServicePointId(dto.getPermanentAddress().getServicePointID()); + beneficiaryAddress.setPermServicePoint(dto.getPermanentAddress().getServicePointName()); + beneficiaryAddress.setPermHabitation(dto.getPermanentAddress().getHabitation()); + } + if (dto.getAgentName() != null) { + beneficiaryAddress.setCreatedBy(dto.getAgentName()); + } + if (dto.getCreatedDate() != null) { + beneficiaryAddress.setCreatedDate(dto.getCreatedDate()); + } + if (dto.getVanID() != null) { + beneficiaryAddress.setVanID(dto.getVanID()); + } + if (dto.getParkingPlaceId() != null) { + beneficiaryAddress.setParkingPlaceID(dto.getParkingPlaceId()); + } + + return beneficiaryAddress; + } + + public String getReservedIdList() { + + return "success"; + } + + /** + * + * @param reserveIdentityDTO + * @return + */ + public String reserveIdentity(ReserveIdentityDTO reserveIdentityDTO) { + + Long availableCount = regIdRepo.countByProviderServiceMapIDAndVehicalNoOrderByBenRegIdAsc( + reserveIdentityDTO.getProviderServiceMapID(), reserveIdentityDTO.getVehicalNo()); + + if (reserveIdentityDTO.getReserveCount() < availableCount) { + + MBeneficiaryregidmapping beneficiaryregidmapping; + Long countToBeAllocate = reserveIdentityDTO.getReserveCount() - availableCount; + countToBeAllocate++; + for (int index = 0; index < countToBeAllocate; index++) { + + beneficiaryregidmapping = regIdRepo.findFirstByProviderServiceMapIDAndVehicalNoOrderByBenRegIdAsc(null, + null); + beneficiaryregidmapping.setProviderServiceMapID(reserveIdentityDTO.getProviderServiceMapID()); + beneficiaryregidmapping.setVehicalNo(reserveIdentityDTO.getVehicalNo()); + regIdRepo.save(beneficiaryregidmapping); + } + } + + return "Successfully Completed"; + } + + public String unReserveIdentity(ReserveIdentityDTO unReserve) { + + regIdRepo.unreserveBeneficiaryIds(unReserve.getProviderServiceMapID(), unReserve.getVehicalNo()); + return "Successfully Completed"; + } + + /** + * Get partial details of beneficiaries (first name middle name and last + * name) list on benId's list + * + * @param BenRegIds + * @return {@link List} Beneficiaries + */ + public List getBeneficiariesPartialDeatilsByBenRegIdList(List benRegIds) { + + logger.info("IdentityService.getBeneficiariesPartialDeatilsByBenRegId - end"); + List list = new ArrayList<>(); + + // new logic, 19-12-2018 + List benMapObjArr = null; + if (benRegIds != null && !benRegIds.isEmpty()) { + benMapObjArr = mappingRepo.getBenMappingByRegIDList(benRegIds); + if (benMapObjArr != null && !benMapObjArr.isEmpty()) { + for (Object[] objArr : benMapObjArr) { + MBeneficiarymapping benMap = this.getBeneficiariesDTONewPartial(objArr); + + list.add(partialMapper.mBeneficiarymappingToBeneficiariesPartialDTO(benMap)); + } + } + logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); + + } + // end + logger.info("IdetityService.getBeneficiariesPartialDeatilsByBenRegId - end"); + + return list; + } + + /** + * Get partial details of beneficiaries (first name middle name and last + * name) list on benId's list + * + * @param benRegIds + * @return {@link List} Beneficiaries + */ + public List getBeneficiariesDeatilsByBenRegIdList(List benRegIds) { + + logger.info("IdentityService.getBeneficiariesDeatilsByBenRegIdList - end"); + List list = new ArrayList<>(); + + // new logic, 19-12-2018 + List benMapObjArr = null; + if (benRegIds != null && !benRegIds.isEmpty()) { + benMapObjArr = mappingRepo.getBenMappingByRegIDList(benRegIds); + if (benMapObjArr != null && !benMapObjArr.isEmpty()) { + for (Object[] objArr : benMapObjArr) { + MBeneficiarymapping benMap = this.getBeneficiariesDTONew(objArr); + list.add(this.getBeneficiariesDTO(benMap)); + } + } + logger.info("benMap size" + (list.isEmpty() ? "No Beneficiary Found" : list.size())); + } + // end + logger.info("IdetityService.getBeneficiariesPartialDeatilsByBenRegId - end"); + + return list; + } + + /** + * + * @param benMap + * @return + */ + private BeneficiariesDTO getBeneficiariesDTO(MBeneficiarymapping benMap) { + BeneficiariesDTO bdto = mapper.mBeneficiarymappingToBeneficiariesDTO(benMap); + if (null != benMap && null != benMap.getMBeneficiarydetail() + && !StringUtils.isEmpty(benMap.getMBeneficiarydetail().getFaceEmbedding())) { + String faceEmbedding = benMap.getMBeneficiarydetail().getFaceEmbedding(); + String trimmedInput = faceEmbedding.replaceAll("[\\[\\]]", ""); + List floatList = new ArrayList<>(); + if (!StringUtils.isEmpty(trimmedInput)) { + String[] stringNumbers = trimmedInput.split(",\\s*"); + for (String str : stringNumbers) { + floatList.add(Float.parseFloat(str)); + } + } + bdto.setFaceEmbedding(floatList); + } + // bdto.setOtherFields(benMap.getMBeneficiarydetail().getOtherFields()); + bdto.setBeneficiaryFamilyTags( + mapper.mapToMBeneficiaryfamilymappingWithBenFamilyDTOList(benMap.getMBeneficiaryfamilymappings())); + bdto.setBeneficiaryIdentites( + mapper.mBeneficiaryidentityListToBenIdentityDTOList(benMap.getMBeneficiaryidentities())); + + List abhaList = v_BenAdvanceSearchRepo.getBenAbhaDetailsByBenRegID(bdto.getBenRegId()); + if (abhaList != null && !abhaList.isEmpty()) { + List abhaDTOList = new ArrayList<>(); + AbhaAddressDTO abhaDTO; + for (Object[] objArr : abhaList) { + abhaDTO = new AbhaAddressDTO(); + abhaDTO.setBeneficiaryRegID(bdto.getBenRegId()); + if (objArr[1] != null) { + abhaDTO.setHealthID(objArr[1].toString()); + } + if (objArr[2] != null) { + abhaDTO.setHealthIDNumber(objArr[2].toString()); + } + if (objArr[3] != null) { + abhaDTO.setAuthenticationMode(objArr[3].toString()); + } + if (objArr[4] != null) { + abhaDTO.setCreatedDate((Timestamp) objArr[4]); + } + + abhaDTOList.add(abhaDTO); + + } + bdto.setAbhaDetails(abhaDTOList); + } + return bdto; + } + + private BeneficiariesDTO mBeneficiarymappingToBeneficiariesDTO(MBeneficiarymapping benMap) { + // TODO Auto-generated method stub + return null; + } + + /** + * finite search + * + * @param identityDTO + * @return + */ + public List getBeneficiaries(IdentityDTO identityDTO) { + List list = new ArrayList<>(); + + List benMapList = mappingRepo.finiteSearch(identityDTO); + for (MBeneficiarymapping benMap : benMapList) { + list.add(this.getBeneficiariesDTO(benMap)); + logger.info("benMapId: " + benMap.getBenMapId() + " :: BenId: " + + benMap.getMBeneficiaryregidmapping().getBeneficiaryID()); + } + + return list; + } + + /** + * * + * + * @return beneficiary image for beneficiary Reg ID. + */ + public String getBeneficiaryImage(String requestOBJ) { + OutputResponse response = new OutputResponse(); + try { + Map benImageMap = new HashMap<>(); + if (requestOBJ != null) { + + JsonElement jsnElmnt = JsonParser.parseString(requestOBJ); + JsonObject obj = jsnElmnt.getAsJsonObject(); + + if (obj != null && obj.has("beneficiaryRegID") && obj.get("beneficiaryRegID") != null) { + MBeneficiarymapping benMap = mappingRepo + .getBenImageIdByBenRegID(obj.get("beneficiaryRegID").getAsBigInteger()); + + if (benMap != null && benMap.getBenImageId() != null && benMap.getVanID() != null) { + MBeneficiaryImage benImageOBJ = imageRepo.getBenImageByBenImageID(benMap.getBenImageId(), + benMap.getVanID()); + benImageMap.put("benImage", benImageOBJ.getBenImage()); + benImageMap.put("createdDate", benImageOBJ.getCreatedDate()); + response.setResponse( + new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create() + .toJson(benImageMap)); + } else { + response.setResponse("Image not available"); + } + } else { + response.setError(5000, "Invalid request"); + } + } + } catch (Exception e) { + logger.error("Error while beneficiary image fetching" + e); + response.setError(e); + } + + return response.toString(); + } + + public void editIdentityEducationOrCommunity(IdentityEditDTO identity) throws MissingMandatoryFieldsException { + logger.info("IdentityService.editIdentityEducationorCommunity - start"); + if (identity.getBeneficiaryRegId() == null && null == identity.getBeneficaryId()) { + throw new MissingMandatoryFieldsException("Either of BeneficiaryID or Beneficiary Reg Id is mandatory."); + } + + // new logic : 13-11-2018 + MBeneficiarymapping benMapping = mappingRepo.findByBenRegIdOrderByBenMapIdAsc(identity.getBeneficiaryRegId()); + if (benMapping != null && benMapping.getBenDetailsId() != null && benMapping.getVanID() != null) { + if (identity.getCommunityId() != null) { + detailRepo.updateCommunity(benMapping.getBenDetailsId(), benMapping.getVanID(), + identity.getCommunityId()); + } + if (identity.getEducationId() != null) { + detailRepo.updateEducation(benMapping.getBenDetailsId(), benMapping.getVanID(), + identity.getEducationId()); + } + } + + } + + public int importBenIdToLocalServer(List benIdImportDTOList) { + logger.info("IdentityService.importBenIdToLocalServer - start"); + logger.info("IdentityService.importBenIdToLocalServer - benIdImportDTOList size : " + + (benIdImportDTOList == null ? "size:0" : benIdImportDTOList.size())); + if (!benIdImportDTOList.isEmpty()) { + ArrayList mBeneficiaryregidmappingList = benIdImportMapper + .benIdImportDTOToMBeneficiaryregidmappings(benIdImportDTOList); + logger.info("Inside if block of importBenIdToLocalServer"); + jdbcTemplate = getJdbcTemplate(); + List dataList = new ArrayList<>(); + Object[] objArr; + String query = " INSERT INTO m_beneficiaryregidmapping(BenRegId, BeneficiaryID, " + + " Provisioned, CreatedDate, CreatedBy, Reserved) VALUES (?,?,?,?,?,?) "; + logger.info("query : " + query); + for (MBeneficiaryregidmapping obj : mBeneficiaryregidmappingList) { + logger.info("inside for check->", obj); + + logger.info("In for loop of importBenIdToLocalServer" + obj.getVanID()); + objArr = new Object[7]; + + objArr[0] = obj.getBenRegId(); + objArr[1] = obj.getBeneficiaryID(); + objArr[2] = false; + objArr[3] = obj.getCreatedDate(); + objArr[4] = obj.getCreatedBy(); + objArr[5] = false; + objArr[6] = obj.getVanID(); + + dataList.add(objArr); + logger.info("regid :" + obj.getBenRegId() + " - benid :" + obj.getBeneficiaryID()); + } + + int[] i = jdbcTemplate.batchUpdate(query, dataList); + + return i.length; + } else { + return 0; + } + + } + + public Long checkBenIDAvailabilityLocal() { + return regIdRepo.countByProvisioned(false); + + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java index 2e9ca773..d1f919ea 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDataService.java @@ -28,8 +28,8 @@ public class BeneficiaryDataService { * Fetch beneficiary data directly from database by benRegId * This bypasses any Elasticsearch caching to get fresh database data * - * @param benRegId The beneficiary registration ID - * @return BeneficiariesDTO or null if not found + * @param benRegId + * @return */ public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { int maxRetries = 3; @@ -39,7 +39,6 @@ public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { try { logger.debug("Fetching beneficiary from database: benRegId={}, attempt={}", benRegId, retryCount + 1); - // Fetch with details MBeneficiarymapping mapping = mappingRepo.findByBenRegIdWithDetails(benRegId); if (mapping == null) { @@ -47,7 +46,6 @@ public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { return null; } - // Convert to DTO BeneficiariesDTO dto = convertToDTO(mapping); logger.debug("Successfully fetched beneficiary: benRegId={}", benRegId); @@ -63,9 +61,8 @@ public BeneficiariesDTO getBeneficiaryFromDatabase(BigInteger benRegId) { return null; } - // Wait before retry try { - Thread.sleep(1000 * retryCount); // Exponential backoff + Thread.sleep(1000 * retryCount); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); return null; @@ -88,29 +85,23 @@ private BeneficiariesDTO convertToDTO(MBeneficiarymapping mapping) { BeneficiariesDTO dto = new BeneficiariesDTO(); try { - // Basic IDs dto.setBenRegId(mapping.getBenRegId()); dto.setBenMapId(mapping.getBenMapId()); - // Use benRegId as benId if benId is not available if (mapping.getBenRegId() != null) { dto.setBenId(mapping.getBenRegId()); } - // Phone number from contact if (mapping.getMBeneficiarycontact() != null) { dto.setPreferredPhoneNum(mapping.getMBeneficiarycontact().getPreferredPhoneNum()); } - // Beneficiary details if (mapping.getMBeneficiarydetail() != null) { BenDetailDTO detailDTO = new BenDetailDTO(); detailDTO.setFirstName(mapping.getMBeneficiarydetail().getFirstName()); detailDTO.setLastName(mapping.getMBeneficiarydetail().getLastName()); - // detailDTO.setGender(mapping.getMBeneficiarydetail().getGenderName()); - // Calculate age if DOB is available if (mapping.getMBeneficiarydetail().getDob() != null) { detailDTO.setBeneficiaryAge(calculateAge(mapping.getMBeneficiarydetail().getDob())); } diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDocumentDataService.java similarity index 94% rename from src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java rename to src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDocumentDataService.java index 3517d4e2..2ca3f8fb 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/OptimizedBeneficiaryDataService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryDocumentDataService.java @@ -18,9 +18,9 @@ * Uses the new complete data query from BenMappingRepo */ @Service -public class OptimizedBeneficiaryDataService { +public class BeneficiaryDocumentDataService { - private static final Logger logger = LoggerFactory.getLogger(OptimizedBeneficiaryDataService.class); + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryDocumentDataService.class); @Autowired private BenMappingRepo mappingRepo; @@ -38,7 +38,6 @@ public List getBeneficiariesBatch(List benRegId try { logger.debug("Fetching {} beneficiaries with complete data", benRegIds.size()); - // Use the new complete query from repo List results = mappingRepo.findCompleteDataByBenRegIds(benRegIds); logger.info("Fetched {} complete beneficiary records", results.size()); @@ -100,33 +99,28 @@ private BeneficiaryDocument mapRowToDocument(Object[] row) { } doc.setBeneficiaryID(beneficiaryRegID); - // Personal Info (2-10) doc.setFirstName(getString(row[idx++])); doc.setLastName(getString(row[idx++])); doc.setGenderID(getInteger(row[idx++])); doc.setGenderName(getString(row[idx++])); - doc.setGender(doc.getGenderName()); // Use genderName for gender + doc.setGender(doc.getGenderName()); doc.setDOB(getDate(row[idx++])); doc.setAge(getInteger(row[idx++])); doc.setFatherName(getString(row[idx++])); doc.setSpouseName(getString(row[idx++])); doc.setIsHIVPos(getString(row[idx++])); - // Metadata (11-14) doc.setCreatedBy(getString(row[idx++])); doc.setCreatedDate(getDate(row[idx++])); doc.setLastModDate(getLong(row[idx++])); doc.setBenAccountID(getLong(row[idx++])); - // Contact (15) doc.setPhoneNum(getString(row[idx++])); - // Health IDs (16-18) doc.setHealthID(getString(row[idx++])); doc.setAbhaID(getString(row[idx++])); doc.setFamilyID(getString(row[idx++])); - // Current Address (19-30) doc.setStateID(getInteger(row[idx++])); doc.setStateName(getString(row[idx++])); doc.setDistrictID(getInteger(row[idx++])); @@ -140,7 +134,6 @@ private BeneficiaryDocument mapRowToDocument(Object[] row) { doc.setServicePointName(getString(row[idx++])); doc.setParkingPlaceID(getInteger(row[idx++])); - // Permanent Address (31-38) doc.setPermStateID(getInteger(row[idx++])); doc.setPermStateName(getString(row[idx++])); doc.setPermDistrictID(getInteger(row[idx++])); @@ -150,7 +143,6 @@ private BeneficiaryDocument mapRowToDocument(Object[] row) { doc.setPermVillageID(getInteger(row[idx++])); doc.setPermVillageName(getString(row[idx++])); - // Identity (39-40) // doc.setGovtIdentityNo(getString(row[idx++])); // String aadhar = getString(row[idx]); // doc.setAadharNo(aadhar != null ? aadhar : doc.getGovtIdentityNo()); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexService.java similarity index 89% rename from src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java rename to src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexService.java index 1e6fabee..de229831 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/AsyncElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexService.java @@ -24,9 +24,9 @@ import com.iemr.common.identity.repo.elasticsearch.SyncJobRepo; @Service -public class AsyncElasticsearchSyncService { +public class BeneficiaryElasticsearchIndexService { - private static final Logger logger = LoggerFactory.getLogger(AsyncElasticsearchSyncService.class); + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryElasticsearchIndexService.class); private static final int BATCH_SIZE = 100; private static final int ES_BULK_SIZE = 50; private static final int STATUS_UPDATE_FREQUENCY = 10; @@ -35,10 +35,10 @@ public class AsyncElasticsearchSyncService { private ElasticsearchClient esClient; @Autowired - private TransactionalSyncWrapper transactionalWrapper; + private BeneficiaryTransactionHelper transactionalWrapper; @Autowired - private OptimizedBeneficiaryDataService optimizedDataService; + private BeneficiaryDocumentDataService dataService; @Autowired private SyncJobRepo syncJobRepository; @@ -89,7 +89,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { try { logger.info("=== BATCH {} START: offset={} ===", batchCounter + 1, offset); - // Get beneficiary IDs logger.debug("Calling getBeneficiaryIdsBatch(offset={}, limit={})", offset, BATCH_SIZE); List batchIds = transactionalWrapper.getBeneficiaryIdsBatch(offset, BATCH_SIZE); @@ -100,14 +99,12 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { break; } - // Debug: Log first few IDs if (batchIds.size() > 0) { logger.debug("First ID type: {}, value: {}", batchIds.get(0)[0].getClass().getName(), batchIds.get(0)[0]); } - // FIXED: Convert Long to BigInteger properly logger.debug("Converting {} IDs to BigInteger", batchIds.size()); List benRegIds = batchIds.stream() .map(arr -> toBigInteger(arr[0])) @@ -122,7 +119,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { continue; } - // Debug: Log first few converted IDs if (benRegIds.size() > 0) { logger.debug("First converted BigInteger: {}", benRegIds.get(0)); } @@ -130,12 +126,10 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { logger.info("Fetching complete data for {} beneficiaries...", benRegIds.size()); // *** CRITICAL: This returns COMPLETE BeneficiaryDocument with 38+ fields *** - // NO CONVERSION NEEDED - documents are ready to index! - List documents = optimizedDataService.getBeneficiariesBatch(benRegIds); + List documents = dataService.getBeneficiariesBatch(benRegIds); logger.info("✓ Fetched {} complete documents for batch at offset {}", documents.size(), offset); - // Debug: Log first document details if (!documents.isEmpty() && documents.get(0) != null) { BeneficiaryDocument firstDoc = documents.get(0); logger.info("Sample doc - benId: {}, name: {} {}, district: {}, state: {}", @@ -146,7 +140,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { firstDoc.getStateName()); } - // Add documents directly to ES batch (NO CONVERSION!) int docsAddedInThisBatch = 0; for (BeneficiaryDocument doc : documents) { try { @@ -154,7 +147,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { esBatch.add(doc); docsAddedInThisBatch++; - // Bulk index when batch is full if (esBatch.size() >= ES_BULK_SIZE) { logger.info("ES batch full ({} docs), indexing now...", esBatch.size()); int indexed = bulkIndexDocuments(esBatch); @@ -180,7 +172,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { logger.info("Added {} documents to ES batch in this iteration", docsAddedInThisBatch); - // Account for any beneficiaries not fetched int notFetched = benRegIds.size() - documents.size(); if (notFetched > 0) { failureCount += notFetched; @@ -194,14 +185,12 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { logger.info("=== BATCH {} END: Processed={}, Success={}, Failed={} ===", batchCounter, processedCount, successCount, failureCount); - // Update job status periodically if (batchCounter % STATUS_UPDATE_FREQUENCY == 0) { logger.info("Updating job progress (every {} batches)...", STATUS_UPDATE_FREQUENCY); updateJobProgress(job, processedCount, successCount, failureCount, offset, totalCount, startTime); } - // Small pause every 10 batches if (batchCounter % 10 == 0) { logger.debug("Pausing for 500ms after {} batches", batchCounter); Thread.sleep(500); @@ -218,13 +207,11 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { job.setFailureCount(failureCount); syncJobRepository.save(job); - // Skip this batch and continue offset += BATCH_SIZE; Thread.sleep(2000); } } - // Index remaining documents if (!esBatch.isEmpty()) { logger.info("Indexing final batch of {} documents", esBatch.size()); int indexed = bulkIndexDocuments(esBatch); @@ -233,7 +220,6 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { processedCount += esBatch.size(); } - // Mark job as completed job.setStatus("COMPLETED"); job.setCompletedAt(new Timestamp(System.currentTimeMillis())); job.setProcessedRecords(processedCount); @@ -246,17 +232,13 @@ public void syncAllBeneficiariesAsync(Long jobId, String triggeredBy) { syncJobRepository.save(job); - logger.info("========================================"); logger.info("Async sync job COMPLETED: jobId={}", jobId); logger.info("Total: {}, Processed: {}, Success: {}, Failed: {}", totalCount, processedCount, successCount, failureCount); logger.info("All 38+ beneficiary fields synced to Elasticsearch!"); - logger.info("========================================"); } catch (Exception e) { - logger.error("========================================"); logger.error("CRITICAL ERROR in async sync: jobId={}, error={}", jobId, e.getMessage(), e); - logger.error("========================================"); job.setStatus("FAILED"); job.setCompletedAt(new Timestamp(System.currentTimeMillis())); @@ -315,9 +297,6 @@ private void updateJobProgress(ElasticsearchSyncJob job, long processed, long su syncJobRepository.save(job); - logger.info("Progress: {}/{} ({:.2f}%) - Speed: {:.2f} rec/sec - ETA: {} sec", - processed, total, (processed * 100.0) / total, speed, - job.getEstimatedTimeRemaining() != null ? job.getEstimatedTimeRemaining() : 0); } private int bulkIndexDocuments(List documents) { diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexUpdater.java similarity index 92% rename from src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java rename to src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexUpdater.java index a6d82599..82d68587 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/RealtimeElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryElasticsearchIndexUpdater.java @@ -20,15 +20,15 @@ * Triggers automatically when beneficiaries are created/updated in database */ @Service -public class RealtimeElasticsearchSyncService { +public class BeneficiaryElasticsearchIndexUpdater { - private static final Logger logger = LoggerFactory.getLogger(RealtimeElasticsearchSyncService.class); + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryElasticsearchIndexUpdater.class); @Autowired private ElasticsearchClient esClient; @Autowired - private OptimizedBeneficiaryDataService dataService; + private BeneficiaryDocumentDataService dataService; @Value("${elasticsearch.index.beneficiary}") private String beneficiaryIndex; @@ -50,7 +50,6 @@ public void syncBeneficiaryAsync(BigInteger benRegId) { try { logger.info("Starting async sync for benRegId: {}", benRegId); - // Fetch beneficiary from database - returns BeneficiaryDocument directly BeneficiaryDocument doc = dataService.getBeneficiaryFromDatabase(benRegId); if (doc == null) { @@ -63,7 +62,6 @@ public void syncBeneficiaryAsync(BigInteger benRegId) { return; } - // Index to Elasticsearch IndexRequest request = IndexRequest.of(i -> i .index(beneficiaryIndex) .id(doc.getBenId()) diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryTransactionHelper.java similarity index 96% rename from src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java rename to src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryTransactionHelper.java index dbcf0f3d..233338f0 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/TransactionalSyncWrapper.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/BeneficiaryTransactionHelper.java @@ -17,9 +17,9 @@ * This prevents connection timeout issues during long-running sync operations */ @Service -public class TransactionalSyncWrapper { +public class BeneficiaryTransactionHelper { - private static final Logger logger = LoggerFactory.getLogger(TransactionalSyncWrapper.class); + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryTransactionHelper.class); @Autowired private BenMappingRepo mappingRepo; diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java index 4e60354f..941a0555 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchIndexingService.java @@ -23,7 +23,7 @@ public class ElasticsearchIndexingService { @Autowired private ElasticsearchSyncService syncService; - @Value("${elasticsearch.index.beneficiary:beneficiary_index_v5}") + @Value("${elasticsearch.index.beneficiary}") private String beneficiaryIndex; /** @@ -62,12 +62,10 @@ public void createIndexWithMapping() throws Exception { .properties("lastModDate", Property.of(p -> p.long_(l -> l))) .properties("benAccountID", Property.of(p -> p.long_(l -> l))) - // Health IDs .properties("healthID", Property.of(p -> p.keyword(k -> k))) .properties("abhaID", Property.of(p -> p.keyword(k -> k))) .properties("familyID", Property.of(p -> p.keyword(k -> k))) - // Current Address .properties("stateID", Property.of(p -> p.integer(i -> i))) .properties("stateName", Property.of(p -> p.keyword(k -> k))) .properties("districtID", Property.of(p -> p.integer(i -> i))) @@ -81,7 +79,6 @@ public void createIndexWithMapping() throws Exception { .properties("servicePointName", Property.of(p -> p.keyword(k -> k))) .properties("parkingPlaceID", Property.of(p -> p.integer(i -> i))) - // Permanent Address .properties("permStateID", Property.of(p -> p.integer(i -> i))) .properties("permStateName", Property.of(p -> p.keyword(k -> k))) .properties("permDistrictID", Property.of(p -> p.integer(i -> i))) @@ -91,7 +88,6 @@ public void createIndexWithMapping() throws Exception { .properties("permVillageID", Property.of(p -> p.integer(i -> i))) .properties("permVillageName", Property.of(p -> p.keyword(k -> k))) - // Identity .properties("aadharNo", Property.of(p -> p.keyword(k -> k))) .properties("govtIdentityNo", Property.of(p -> p.keyword(k -> k))) ); @@ -112,7 +108,6 @@ public Map indexAllBeneficiaries() { logger.info("Starting full indexing via sync service..."); try { - // Use the existing, battle-tested sync service ElasticsearchSyncService.SyncResult result = syncService.syncAllBeneficiaries(); Map response = new HashMap<>(); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index 9e5ac270..f522e9bd 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -34,7 +34,7 @@ public class ElasticsearchService { @Autowired private BenAddressRepo benAddressRepo; - @Value("${elasticsearch.index.beneficiary:beneficiary_index_v5}") + @Value("${elasticsearch.index.beneficiary}") private String beneficiaryIndex; /** @@ -44,7 +44,6 @@ public List> universalSearch(String query, Integer userId) { logger.info("Universal ES search for: {} with userId: {}", query, userId); try { - // Get user location if userId provided Map userLocation = null; if (userId != null) { userLocation = getUserLocation(userId); @@ -124,7 +123,6 @@ public List> universalSearch(String query, Integer userId) { allResults = rankByLocation(allResults, userLocation); } - // Limit to top 20 results List> results = allResults.stream() .limit(20) .collect(Collectors.toList()); @@ -182,7 +180,7 @@ private List> rankByLocation(List> resul .sorted((r1, r2) -> { int score1 = calculateLocationScore(r1, userBlockId, userVillageId); int score2 = calculateLocationScore(r2, userBlockId, userVillageId); - return Integer.compare(score2, score1); // Higher score first + return Integer.compare(score2, score1); }) .collect(Collectors.toList()); } @@ -202,7 +200,6 @@ private int calculateLocationScore(Map beneficiary, return score; } - // Check current address Integer currBlockId = getIntegerFromMap(demographics, "blockID"); Integer currVillageId = getIntegerFromMap(demographics, "m_districtblock", "blockID"); @@ -216,16 +213,15 @@ private int calculateLocationScore(Map beneficiary, score += 50; } - // Check permanent address as fallback Integer permBlockId = getIntegerFromMap(beneficiary, "permBlockID"); Integer permVillageId = getIntegerFromMap(beneficiary, "permVillageID"); if (userVillageId != null && userVillageId.equals(permVillageId)) { - score += 75; // Slightly lower than current village + score += 75; } if (userBlockId != null && userBlockId.equals(permBlockId)) { - score += 25; // Lower than current block + score += 25; } } catch (Exception e) { @@ -270,38 +266,22 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat result.put("genderName", esData.getGenderName()); result.put("dob", esData.getDOB()); result.put("age", esData.getAge()); - // result.put("actualAge", esData.getAge()); - // result.put("ageUnits", "Years"); result.put("fatherName", esData.getFatherName() != null ? esData.getFatherName() : ""); result.put("spouseName", esData.getSpouseName() != null ? esData.getSpouseName() : ""); - // result.put("isHIVPos", esData.getIsHIVPos() != null ? esData.getIsHIVPos() : ""); result.put("createdBy", esData.getCreatedBy()); result.put("createdDate", esData.getCreatedDate()); result.put("lastModDate", esData.getLastModDate()); result.put("benAccountID", esData.getBenAccountID()); - // Health IDs result.put("healthID", esData.getHealthID()); result.put("abhaID", esData.getAbhaID()); result.put("familyID", esData.getFamilyID()); - // Permanent address fields at root level - // result.put("permStateID", esData.getPermStateID()); - // result.put("permStateName", esData.getPermStateName()); - // result.put("permDistrictID", esData.getPermDistrictID()); - // result.put("permDistrictName", esData.getPermDistrictName()); - // result.put("permBlockID", esData.getPermBlockID()); - // result.put("permBlockName", esData.getPermBlockName()); - // result.put("permVillageID", esData.getPermVillageID()); - // result.put("permVillageName", esData.getPermVillageName()); - - // Gender object Map mGender = new HashMap<>(); mGender.put("genderID", esData.getGenderID()); mGender.put("genderName", esData.getGenderName()); result.put("m_gender", mGender); - // Demographics object from ES with COMPLETE address data Map demographics = new HashMap<>(); demographics.put("beneficiaryRegID", esData.getBenRegId()); demographics.put("stateID", esData.getStateID()); @@ -319,7 +299,6 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat demographics.put("servicePointName", esData.getServicePointName()); demographics.put("createdBy", esData.getCreatedBy()); - // Nested m_state Map mState = new HashMap<>(); mState.put("stateID", esData.getStateID()); mState.put("stateName", esData.getStateName()); @@ -327,14 +306,12 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat mState.put("countryID", 1); demographics.put("m_state", mState); - // Nested m_district Map mDistrict = new HashMap<>(); mDistrict.put("districtID", esData.getDistrictID()); mDistrict.put("districtName", esData.getDistrictName()); mDistrict.put("stateID", esData.getStateID()); demographics.put("m_district", mDistrict); - // Nested m_districtblock Map mBlock = new HashMap<>(); mBlock.put("blockID", esData.getBlockID()); mBlock.put("blockName", esData.getBlockName()); @@ -342,7 +319,6 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat mBlock.put("stateID", esData.getStateID()); demographics.put("m_districtblock", mBlock); - // Nested m_districtbranchmapping Map mBranch = new HashMap<>(); mBranch.put("districtBranchID", null); mBranch.put("blockID", esData.getBlockID()); @@ -352,7 +328,6 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat result.put("i_bendemographics", demographics); - // Phone numbers from ES List> benPhoneMaps = new ArrayList<>(); if (esData.getPhoneNum() != null && !esData.getPhoneNum().isEmpty()) { Map phoneMap = new HashMap<>(); @@ -371,7 +346,6 @@ private Map mapESResultToExpectedFormat(BeneficiariesESDTO esDat } result.put("benPhoneMaps", benPhoneMaps); - // Default values result.put("isConsent", false); result.put("m_title", new HashMap<>()); result.put("maritalStatus", new HashMap<>()); @@ -421,7 +395,6 @@ private Map mapToExpectedFormat(Object[] row) { Map result = new HashMap<>(); try { - // Basic fields Long beneficiaryRegID = getLong(row[0]); String beneficiaryID = getString(row[1]); String firstName = getString(row[2]); @@ -438,7 +411,6 @@ private Map mapToExpectedFormat(Object[] row) { Long lastModDate = getLong(row[13]); Long benAccountID = getLong(row[14]); - // Demographics Integer stateID = getInteger(row[15]); String stateName = getString(row[16]); Integer districtID = getInteger(row[17]); @@ -451,7 +423,6 @@ private Map mapToExpectedFormat(Object[] row) { Integer parkingPlaceID = getInteger(row[24]); String phoneNum = getString(row[25]); - // Build result result.put("beneficiaryRegID", beneficiaryRegID); result.put("beneficiaryID", beneficiaryID); result.put("firstName", firstName); @@ -461,23 +432,18 @@ private Map mapToExpectedFormat(Object[] row) { result.put("dOB", dob); result.put("dob", dob); result.put("age", age); - // result.put("actualAge", age); - // result.put("ageUnits", "Years"); result.put("fatherName", fatherName != null ? fatherName : ""); result.put("spouseName", spouseName != null ? spouseName : ""); - // result.put("isHIVPos", isHIVPos != null ? isHIVPos : ""); result.put("createdBy", createdBy); result.put("createdDate", createdDate); result.put("lastModDate", lastModDate); result.put("benAccountID", benAccountID); - // Gender object Map mGender = new HashMap<>(); mGender.put("genderID", genderID); mGender.put("genderName", genderName); result.put("m_gender", mGender); - // Demographics object Map demographics = new HashMap<>(); demographics.put("beneficiaryRegID", beneficiaryRegID); demographics.put("stateID", stateID); @@ -493,7 +459,6 @@ private Map mapToExpectedFormat(Object[] row) { demographics.put("servicePointName", servicePointName); demographics.put("createdBy", createdBy); - // Nested m_state Map mState = new HashMap<>(); mState.put("stateID", stateID); mState.put("stateName", stateName); @@ -501,14 +466,12 @@ private Map mapToExpectedFormat(Object[] row) { mState.put("countryID", 1); demographics.put("m_state", mState); - // Nested m_district Map mDistrict = new HashMap<>(); mDistrict.put("districtID", districtID); mDistrict.put("districtName", districtName); mDistrict.put("stateID", stateID); demographics.put("m_district", mDistrict); - // Nested m_districtblock Map mBlock = new HashMap<>(); mBlock.put("blockID", blockID); mBlock.put("blockName", blockName); @@ -516,7 +479,6 @@ private Map mapToExpectedFormat(Object[] row) { mBlock.put("stateID", stateID); demographics.put("m_districtblock", mBlock); - // Nested m_districtbranchmapping Map mBranch = new HashMap<>(); mBranch.put("districtBranchID", null); mBranch.put("blockID", blockID); @@ -526,11 +488,9 @@ private Map mapToExpectedFormat(Object[] row) { result.put("i_bendemographics", demographics); - // Phone numbers List> benPhoneMaps = fetchPhoneNumbers(beneficiaryRegID); result.put("benPhoneMaps", benPhoneMaps); - // Default values result.put("isConsent", false); result.put("m_title", new HashMap<>()); result.put("maritalStatus", new HashMap<>()); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java index 21e96ad4..24d10775 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java @@ -43,7 +43,7 @@ public class ElasticsearchSyncService { private BeneficiaryDataService beneficiaryDataService; @Autowired - private TransactionalSyncWrapper transactionalWrapper; + private BeneficiaryTransactionHelper transactionalWrapper; @Value("${elasticsearch.index.beneficiary}") private String beneficiaryIndex; @@ -53,9 +53,7 @@ public class ElasticsearchSyncService { * This should be run as a one-time operation or scheduled job */ public SyncResult syncAllBeneficiaries() { - logger.info("========================================"); logger.info("Starting full beneficiary sync to Elasticsearch..."); - logger.info("========================================"); SyncResult result = new SyncResult(); @@ -168,12 +166,10 @@ public SyncResult syncAllBeneficiaries() { processedCount.addAndGet(esBatch.size()); } - logger.info("========================================"); logger.info("Sync completed successfully!"); logger.info("Total Processed: {}", processedCount.get()); logger.info("Successfully Indexed: {}", result.getSuccessCount()); logger.info("Failed: {}", result.getFailureCount()); - logger.info("========================================"); } catch (Exception e) { logger.error("========================================"); @@ -191,7 +187,6 @@ public SyncResult syncAllBeneficiaries() { */ public boolean syncSingleBeneficiary(String benRegId) { try { - logger.info("========================================"); logger.info("Syncing single beneficiary with BenRegId: {}", benRegId); BigInteger benRegIdBig = new BigInteger(benRegId); @@ -200,7 +195,6 @@ public boolean syncSingleBeneficiary(String benRegId) { boolean exists = transactionalWrapper.existsByBenRegId(benRegIdBig); if (!exists) { logger.error("Beneficiary does not exist in database: BenRegId={}", benRegId); - logger.info("========================================"); return false; } @@ -211,7 +205,6 @@ public boolean syncSingleBeneficiary(String benRegId) { if (benDTO == null) { logger.error("Failed to fetch beneficiary details from database: BenRegId={}", benRegId); - logger.info("========================================"); return false; } @@ -226,7 +219,6 @@ public boolean syncSingleBeneficiary(String benRegId) { if (doc == null || doc.getBenId() == null) { logger.error("Failed to convert beneficiary to document: BenRegId={}", benRegId); - logger.info("========================================"); return false; } @@ -240,17 +232,13 @@ public boolean syncSingleBeneficiary(String benRegId) { .document(doc) ); - logger.info("========================================"); logger.info("SUCCESS! Beneficiary synced to Elasticsearch"); logger.info("BenRegId: {}, BenId: {}", benRegId, doc.getBenId()); - logger.info("========================================"); return true; } catch (Exception e) { - logger.error("========================================"); logger.error("ERROR syncing beneficiary {}: {}", benRegId, e.getMessage(), e); - logger.error("========================================"); return false; } } @@ -282,7 +270,6 @@ private int bulkIndexDocuments(List documents) { int successCount = 0; - // Log errors if any if (result.errors()) { logger.warn("Bulk indexing had some errors"); for (BulkResponseItem item : result.items()) { diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java index 06e4d283..e4dc44f8 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java @@ -24,7 +24,7 @@ public class SyncJobService { private SyncJobRepo syncJobRepository; @Autowired - private AsyncElasticsearchSyncService asyncSyncService; + private BeneficiaryElasticsearchIndexService syncService; /** * Start a new full sync job @@ -52,7 +52,7 @@ public ElasticsearchSyncJob startFullSyncJob(String triggeredBy) { logger.info("Created new full sync job: jobId={}, triggeredBy={}", job.getJobId(), triggeredBy); // Start async processing - asyncSyncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); + syncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); return job; } @@ -75,7 +75,7 @@ public ElasticsearchSyncJob resumeJob(Long jobId, String triggeredBy) { job = syncJobRepository.save(job); // Restart async processing from last offset - asyncSyncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); + syncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); return job; } From 580b55eaeee580ea35199c9c7b5bca160b28815a Mon Sep 17 00:00:00 2001 From: Vanitha Date: Tue, 16 Dec 2025 19:29:30 +0530 Subject: [PATCH 07/13] fix: update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 658d21d0..57f74646 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.iemr.common.identity identity-api - 3.4.0 + 3.6.1 war From bcdf7302e97242f61f31a70e0bfb1f80707374d0 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Tue, 16 Dec 2025 19:30:51 +0530 Subject: [PATCH 08/13] fix: revert advancesearch --- .../com/iemr/common/identity/controller/IdentityController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityController.java b/src/main/java/com/iemr/common/identity/controller/IdentityController.java index 18854dd3..4ac603ae 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityController.java @@ -96,7 +96,7 @@ public String getBeneficiaries( JsonElement json = JsonParser.parseString(searchFilter); IdentitySearchDTO searchParams = InputMapper.getInstance().gson().fromJson(json, IdentitySearchDTO.class); - List list = svc.getBeneficiarieswithES(searchParams); + List list = svc.getBeneficiaries(searchParams); list.removeIf(Objects::isNull); Collections.sort(list); response = getSuccessResponseString(list, 200, "success", "getBeneficiariesByAdvanceSearch"); From 3b17ee7ed54de47754db621c817c6d5842cf8eec Mon Sep 17 00:00:00 2001 From: Vanitha Date: Tue, 16 Dec 2025 22:12:11 +0530 Subject: [PATCH 09/13] fix: add properties --- src/main/environment/common_ci.properties | 6 +++ src/main/environment/common_docker.properties | 6 +++ .../environment/common_example.properties | 7 ++++ .../identity/dto/IdentitySearchDTO.java | 1 - .../common/identity/utils/CookieUtil.java | 42 +++++++++---------- .../utils/JwtUserIdValidationFilter.java | 2 +- src/main/resources/application.properties | 23 ---------- 7 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 75ad8759..6067ad48 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -23,3 +23,9 @@ fhir-url=@env.FHIR_API@ spring.redis.host=@env.REDIS_HOST@ cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ + +#Elasticsearch Config +elasticsearch.index.beneficiary=@env.ELASTICSEARCH_BENEFICIARY_INDEX@ + +# Enable/Disable ES (for gradual rollout) +elasticsearch.enabled=@env.ELASTICSEARCH_ENABLED@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 90942c28..a407aab8 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -23,3 +23,9 @@ fhir-url=${FHIR_API} spring.redis.host=${REDIS_HOST} cors.allowed-origins=${CORS_ALLOWED_ORIGINS} + +#Elasticsearch Config +elasticsearch.index.beneficiary=${ELASTICSEARCH_BENEFICIARY_INDEX} + +# Enable/Disable ES (for gradual rollout) +elasticsearch.enabled=${ELASTICSEARCH_ENABLED} \ No newline at end of file diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index ad15db00..aeeb320e 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -18,3 +18,10 @@ fhir-url=http://localhost:8093/ # Redis Config spring.redis.host=localhost cors.allowed-origins=http://localhost:* + +#Elasticsearch Config +elasticsearch.index.beneficiary=beneficiary_index_v5 + +# Enable/Disable ES (for gradual rollout) +elasticsearch.enabled=true + diff --git a/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java b/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java index 855f8dba..db4bf011 100644 --- a/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java +++ b/src/main/java/com/iemr/common/identity/dto/IdentitySearchDTO.java @@ -30,7 +30,6 @@ import lombok.Data; @Data -@lombok.Builder public class IdentitySearchDTO { private BigInteger beneficiaryId; diff --git a/src/main/java/com/iemr/common/identity/utils/CookieUtil.java b/src/main/java/com/iemr/common/identity/utils/CookieUtil.java index dd4a02db..88be3f09 100644 --- a/src/main/java/com/iemr/common/identity/utils/CookieUtil.java +++ b/src/main/java/com/iemr/common/identity/utils/CookieUtil.java @@ -10,28 +10,26 @@ @Service public class CookieUtil { - - public Optional getCookieValue(HttpServletRequest request, String cookieName) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookieName.equals(cookie.getName())) { - return Optional.of(cookie.getValue()); - } - } - } - return Optional.empty(); - } - public static String getJwtTokenFromCookie(HttpServletRequest request) { - Cookie[] cookies = request.getCookies(); - if (cookies == null) { - return null; // No cookies present, return null safely - } - return Arrays.stream(cookies).filter(cookie -> "Jwttoken".equals(cookie.getName())).map(Cookie::getValue) - .findFirst().orElse(null); - } + public Optional getCookieValue(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookieName.equals(cookie.getName())) { + return Optional.of(cookie.getValue()); + } + } + } + return Optional.empty(); + } + + public static String getJwtTokenFromCookie(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; // No cookies present, return null safely + } + return Arrays.stream(cookies).filter(cookie -> "Jwttoken".equals(cookie.getName())).map(Cookie::getValue) + .findFirst().orElse(null); + } } - - diff --git a/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java index 1156daa1..2a91a2da 100644 --- a/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/identity/utils/JwtUserIdValidationFilter.java @@ -162,7 +162,7 @@ private boolean isMobileClient(String userAgent) { return userAgent.contains("okhttp") || userAgent.contains("java/"); // iOS (custom clients) } - @SuppressWarnings("static-access") + @SuppressWarnings("static-access") private String getJwtTokenFromCookies(HttpServletRequest request) { return cookieUtil.getJwtTokenFromCookie(request); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7813571c..6bda87d0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -36,10 +36,6 @@ spring.datasource.hikari.max-lifetime=600000 spring.datasource.hikari.keepalive-tquickime=120000 spring.datasource.hikari.validationTimeout=5000 -# REMOVE CONNECTION TEST QUERY -# spring.datasource.hikari.connection-test-query=SELECT 1 - -# BETTER THAN autoReconnect + test-on-borrow spring.datasource.hikari.health-check-properties.mysql5Validation=true spring.datasource.hikari.data-source-properties.cachePrepStmts=true @@ -48,21 +44,6 @@ spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048 spring.datasource.hikari.data-source-properties.useServerPrepStmts=true spring.datasource.hikari.data-source-properties.tcpKeepAlive=true - -# spring.datasource.hikari.connection-test-query=SELECT 1 - - -# OLD Tomcat settings - REMOVE THESE (they conflict with HikariCP) -# spring.datasource.tomcat.initial-size=5 -# spring.datasource.tomcat.max-idle=15 -# spring.datasource.tomcat.max-active=30 -# spring.datasource.tomcat.min-idle=5 -# spring.datasource.tomcat.min-evictable-idle-time-millis=15000 -# spring.datasource.tomcat.remove-abandoned=true -# spring.datasource.tomcat.logAbandoned=true -# spring.datasource.continue-on-error=true -# spring.datasource.tomcat.remove-abandoned-timeout=600 - #Below lines are added for security reasons spring.session.store-type=redis spring.redis.password= @@ -99,10 +80,6 @@ elasticsearch.host=localhost elasticsearch.port=9200 elasticsearch.username=elastic elasticsearch.password=piramalES -elasticsearch.index.beneficiary=beneficiary_index_v5 - -# Enable/Disable ES (for gradual rollout) -elasticsearch.enabled=true # Connection pool settings elasticsearch.connection.timeout=5000 From 5d6295304184a3844a5032d672127ca6db4fff1d Mon Sep 17 00:00:00 2001 From: Vanitha Date: Tue, 16 Dec 2025 22:54:14 +0530 Subject: [PATCH 10/13] fix: coderabbit comments --- src/main/environment/common_ci.properties | 6 +++++- src/main/environment/common_docker.properties | 6 +++++- src/main/environment/common_example.properties | 6 +++++- .../elasticsearch/ElasticsearchSyncController.java | 2 -- .../service/elasticsearch/ElasticsearchService.java | 1 - .../service/elasticsearch/ElasticsearchSyncService.java | 8 +++----- .../identity/service/elasticsearch/SyncJobService.java | 2 +- src/main/resources/application.properties | 6 ------ 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 6067ad48..f8b32d0e 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -24,7 +24,11 @@ spring.redis.host=@env.REDIS_HOST@ cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ -#Elasticsearch Config +# Elasticsearch Configuration +elasticsearch.host=@env.ELASTICSEARCH_HOST@ +elasticsearch.port=@env.ELASTICSEARCH_PORT@ +elasticsearch.username=@env.ELASTICSEARCH_USERNAME@ +elasticsearch.password=@env.ELASTICSEARCH_PASSWORD@ elasticsearch.index.beneficiary=@env.ELASTICSEARCH_BENEFICIARY_INDEX@ # Enable/Disable ES (for gradual rollout) diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index a407aab8..f870b4b9 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -24,7 +24,11 @@ spring.redis.host=${REDIS_HOST} cors.allowed-origins=${CORS_ALLOWED_ORIGINS} -#Elasticsearch Config +# Elasticsearch Configuration +elasticsearch.host=${ELASTICSEARCH_HOST} +elasticsearch.port=${ELASTICSEARCH_PORT} +elasticsearch.username=${ELASTICSEARCH_USERNAME} +elasticsearch.password=${ELASTICSEARCH_PASSWORD} elasticsearch.index.beneficiary=${ELASTICSEARCH_BENEFICIARY_INDEX} # Enable/Disable ES (for gradual rollout) diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index aeeb320e..8e07ebea 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -19,7 +19,11 @@ fhir-url=http://localhost:8093/ spring.redis.host=localhost cors.allowed-origins=http://localhost:* -#Elasticsearch Config +# Elasticsearch Configuration +elasticsearch.host=localhost +elasticsearch.port=9200 +elasticsearch.username=elastic +elasticsearch.password=piramalES elasticsearch.index.beneficiary=beneficiary_index_v5 # Enable/Disable ES (for gradual rollout) diff --git a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java index 075e7239..b8ed204d 100644 --- a/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java +++ b/src/main/java/com/iemr/common/identity/controller/elasticsearch/ElasticsearchSyncController.java @@ -237,7 +237,6 @@ public ResponseEntity> syncAllBeneficiaries() { @PostMapping("/single/{benRegId}") public ResponseEntity> syncSingleBeneficiary( @PathVariable String benRegId) { - logger.info("Received request to sync single beneficiary: {}", benRegId); Map response = new HashMap<>(); @@ -310,7 +309,6 @@ public ResponseEntity> healthCheck() { @GetMapping("/debug/check/{benRegId}") public ResponseEntity> checkBeneficiaryExists( @PathVariable String benRegId) { - logger.info("Checking if beneficiary exists: {}", benRegId); Map response = new HashMap<>(); diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index f522e9bd..3c9f0f5e 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -41,7 +41,6 @@ public class ElasticsearchService { * Universal search with optional user location for ranking */ public List> universalSearch(String query, Integer userId) { - logger.info("Universal ES search for: {} with userId: {}", query, userId); try { Map userLocation = null; diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java index 24d10775..d868c3e1 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchSyncService.java @@ -187,14 +187,13 @@ public SyncResult syncAllBeneficiaries() { */ public boolean syncSingleBeneficiary(String benRegId) { try { - logger.info("Syncing single beneficiary with BenRegId: {}", benRegId); BigInteger benRegIdBig = new BigInteger(benRegId); // Check if beneficiary exists in database first using transactional wrapper boolean exists = transactionalWrapper.existsByBenRegId(benRegIdBig); if (!exists) { - logger.error("Beneficiary does not exist in database: BenRegId={}", benRegId); + logger.error("Beneficiary does not exist in database}"); return false; } @@ -204,7 +203,7 @@ public boolean syncSingleBeneficiary(String benRegId) { BeneficiariesDTO benDTO = beneficiaryDataService.getBeneficiaryFromDatabase(benRegIdBig); if (benDTO == null) { - logger.error("Failed to fetch beneficiary details from database: BenRegId={}", benRegId); + logger.error("Failed to fetch beneficiary details from database"); return false; } @@ -218,7 +217,7 @@ public boolean syncSingleBeneficiary(String benRegId) { BeneficiaryDocument doc = convertToDocument(benDTO); if (doc == null || doc.getBenId() == null) { - logger.error("Failed to convert beneficiary to document: BenRegId={}", benRegId); + logger.error("Failed to convert beneficiary to document"); return false; } @@ -233,7 +232,6 @@ public boolean syncSingleBeneficiary(String benRegId) { ); logger.info("SUCCESS! Beneficiary synced to Elasticsearch"); - logger.info("BenRegId: {}, BenId: {}", benRegId, doc.getBenId()); return true; diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java index e4dc44f8..57360a7f 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/SyncJobService.java @@ -49,7 +49,7 @@ public ElasticsearchSyncJob startFullSyncJob(String triggeredBy) { // Save job to database job = syncJobRepository.save(job); - logger.info("Created new full sync job: jobId={}, triggeredBy={}", job.getJobId(), triggeredBy); + logger.info("Created new full sync job"); // Start async processing syncService.syncAllBeneficiariesAsync(job.getJobId(), triggeredBy); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6bda87d0..22a9ff6c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -75,12 +75,6 @@ spring.main.allow-circular-references=true jwt.access.expiration=86400000 jwt.refresh.expiration=604800000 -# Elasticsearch Configuration -elasticsearch.host=localhost -elasticsearch.port=9200 -elasticsearch.username=elastic -elasticsearch.password=piramalES - # Connection pool settings elasticsearch.connection.timeout=5000 elasticsearch.socket.timeout=60000 From 5b5c3a1678ab428a12b9dfb31379d1e605adfbd4 Mon Sep 17 00:00:00 2001 From: Vanitha Date: Thu, 18 Dec 2025 09:26:45 +0530 Subject: [PATCH 11/13] fix: remove comment code --- .../common/identity/ScheduledSyncJob.java | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java b/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java index 82120338..568d072c 100644 --- a/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java +++ b/src/main/java/com/iemr/common/identity/ScheduledSyncJob.java @@ -12,9 +12,9 @@ /** * Scheduled jobs for Elasticsearch sync - * - * To enable scheduled sync, set: - * elasticsearch.sync.scheduled.enabled=true in application.properties + * + * To enable scheduled sync, set: elasticsearch.sync.scheduled.enabled=true in + * application.properties */ @Component public class ScheduledSyncJob { @@ -28,8 +28,8 @@ public class ScheduledSyncJob { private boolean scheduledSyncEnabled; /** - * Run full sync every day at 2 AM - * Cron: second, minute, hour, day, month, weekday + * Run full sync every day at 2 AM Cron: second, minute, hour, day, month, + * weekday */ @Scheduled(cron = "${elasticsearch.sync.scheduled.cron:0 0 2 * * ?}") public void scheduledFullSync() { @@ -38,11 +38,8 @@ public void scheduledFullSync() { return; } - logger.info("========================================"); - logger.info("Starting scheduled full sync job"); - logger.info("========================================"); - - try { + logger.info("Starting scheduled full sync job"); + try { // Check if there's already a sync running if (syncJobService.isFullSyncRunning()) { logger.warn("Full sync already running. Skipping scheduled sync."); @@ -59,8 +56,7 @@ public void scheduledFullSync() { } /** - * Clean up old completed jobs (keep last 30 days) - * Runs every Sunday at 3 AM + * Clean up old completed jobs (keep last 30 days) Runs every Sunday at 3 AM */ @Scheduled(cron = "0 0 3 * * SUN") public void cleanupOldJobs() { @@ -69,8 +65,6 @@ public void cleanupOldJobs() { } logger.info("Running cleanup of old sync jobs..."); - - // TODO: Implement cleanup logic - // Delete jobs older than 30 days with status COMPLETED or FAILED + } -} \ No newline at end of file +} From b8e868242acaf6477d59870a21a7cd444b882076 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 23 Dec 2025 09:15:25 +0530 Subject: [PATCH 12/13] fix: accept numeric values for search --- .../environment/common_example.properties | 2 +- .../elasticsearch/ElasticsearchService.java | 188 ++++++++++-------- 2 files changed, 105 insertions(+), 85 deletions(-) diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index 8e07ebea..45018aa6 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -24,7 +24,7 @@ elasticsearch.host=localhost elasticsearch.port=9200 elasticsearch.username=elastic elasticsearch.password=piramalES -elasticsearch.index.beneficiary=beneficiary_index_v5 +elasticsearch.index.beneficiary=beneficiary_index # Enable/Disable ES (for gradual rollout) elasticsearch.enabled=true diff --git a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java index 3c9f0f5e..a8c4004b 100644 --- a/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java +++ b/src/main/java/com/iemr/common/identity/service/elasticsearch/ElasticsearchService.java @@ -41,101 +41,121 @@ public class ElasticsearchService { * Universal search with optional user location for ranking */ public List> universalSearch(String query, Integer userId) { - - try { - Map userLocation = null; - if (userId != null) { - userLocation = getUserLocation(userId); - logger.info("User location: {}", userLocation); - } - - boolean isNumeric = query.matches("\\d+"); - - SearchResponse response = esClient.search(s -> s - .index(beneficiaryIndex) - .query(q -> q - .bool(b -> { - // Fuzzy multi-match for text fields + try { + Map userLocation = null; + if (userId != null) { + userLocation = getUserLocation(userId); + logger.info("User location: {}", userLocation); + } + + boolean isNumeric = query.matches("\\d+"); + + SearchResponse response = esClient.search(s -> s + .index(beneficiaryIndex) + .query(q -> q + .bool(b -> { + if (!isNumeric) { b.should(s1 -> s1.multiMatch(mm -> mm .query(query) - .fields("firstName^3", "lastName^2", "fatherName", "spouseName") + .fields("firstName", "lastName", "fatherName", "spouseName") .type(TextQueryType.BestFields) .fuzziness("AUTO") )); + + b.should(s2 -> s2.term(t -> t.field("firstName.keyword").value(query))); + b.should(s3 -> s3.term(t -> t.field("lastName.keyword").value(query))); + b.should(s4 -> s4.term(t -> t.field("fatherName.keyword").value(query))); + b.should(s5 -> s5.term(t -> t.field("spouseName.keyword").value(query))); + } + + b.should(s6 -> s6.term(t -> t.field("healthID").value(query))); + b.should(s7 -> s7.term(t -> t.field("abhaID").value(query))); + b.should(s8 -> s8.term(t -> t.field("familyID").value(query))); + b.should(s9 -> s9.term(t -> t.field("beneficiaryID").value(query))); + b.should(s10 -> s10.term(t -> t.field("benId").value(query))); + b.should(s11 -> s11.term(t -> t.field("aadharNo").value(query))); + b.should(s12 -> s12.term(t -> t.field("govtIdentityNo").value(query))); + + if (isNumeric) { + b.should(s13 -> s13.wildcard(w -> w.field("phoneNum").value("*" + query + "*"))); + b.should(s14 -> s14.wildcard(w -> w.field("healthID").value("*" + query + "*"))); + b.should(s15 -> s15.wildcard(w -> w.field("abhaID").value("*" + query + "*"))); + b.should(s16 -> s16.wildcard(w -> w.field("familyID").value("*" + query + "*"))); + b.should(s17 -> s17.wildcard(w -> w.field("beneficiaryID").value("*" + query + "*"))); + b.should(s18 -> s18.wildcard(w -> w.field("benId").value("*" + query + "*"))); + b.should(s19 -> s19.wildcard(w -> w.field("aadharNo").value("*" + query + "*"))); + b.should(s20 -> s20.wildcard(w -> w.field("govtIdentityNo").value("*" + query + "*"))); - // Exact match for phone number - b.should(s2 -> s2.term(t -> t - .field("phoneNum") - .value(query) - )); - - // NEW: Search in healthID, abhaID, familyID - b.should(s3 -> s3.term(t -> t - .field("healthID") - .value(query) - )); - - b.should(s4 -> s4.term(t -> t - .field("abhaID") - .value(query) - )); - - b.should(s5 -> s5.term(t -> t - .field("familyID") - .value(query) - )); - - // Numeric fields (only if query is numeric) - if (isNumeric) { - try { - Long numericValue = Long.parseLong(query); - b.should(s6 -> s6.term(t -> t.field("benRegId").value(numericValue))); - } catch (NumberFormatException e) { - logger.debug("Could not parse as long: {}", query); - } + b.should(s21 -> s21.prefix(p -> p.field("phoneNum").value(query))); + b.should(s22 -> s22.prefix(p -> p.field("healthID").value(query))); + b.should(s23 -> s23.prefix(p -> p.field("abhaID").value(query))); + b.should(s24 -> s24.prefix(p -> p.field("familyID").value(query))); + b.should(s25 -> s25.prefix(p -> p.field("beneficiaryID").value(query))); + b.should(s26 -> s26.prefix(p -> p.field("benId").value(query))); + + try { + Long numericValue = Long.parseLong(query); + b.should(s27 -> s27.term(t -> t.field("benRegId").value(numericValue))); + b.should(s28 -> s28.term(t -> t.field("benAccountID").value(numericValue))); - b.should(s7 -> s7.term(t -> t.field("benId").value(query))); + int intValue = numericValue.intValue(); + b.should(s29 -> s29.term(t -> t.field("genderID").value(intValue))); + b.should(s30 -> s30.term(t -> t.field("age").value(intValue))); + b.should(s31 -> s31.term(t -> t.field("stateID").value(intValue))); + b.should(s32 -> s32.term(t -> t.field("districtID").value(intValue))); + b.should(s33 -> s33.term(t -> t.field("blockID").value(intValue))); + b.should(s34 -> s34.term(t -> t.field("villageID").value(intValue))); + b.should(s35 -> s35.term(t -> t.field("servicePointID").value(intValue))); + b.should(s36 -> s36.term(t -> t.field("parkingPlaceID").value(intValue))); + + logger.info("Added numeric searches for value: {}", numericValue); + } catch (NumberFormatException e) { + logger.warn("Failed to parse numeric value: {}", query); } - - b.minimumShouldMatch("1"); - return b; - }) - ) - .size(100) // Increased to allow for location-based filtering - , BeneficiariesESDTO.class); - - logger.info("ES returned {} hits", response.hits().hits().size()); - - // Convert ES results - List> allResults = response.hits().hits().stream() - .map(hit -> mapESResultToExpectedFormat(hit.source())) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - if (allResults.isEmpty()) { - logger.info("No results found in ES, falling back to database"); - return searchInDatabaseDirectly(query); - } - - // Apply location-based ranking if user location available - if (userLocation != null) { - allResults = rankByLocation(allResults, userLocation); - } - - List> results = allResults.stream() - .limit(20) - .collect(Collectors.toList()); - - logger.info("Returning {} results from ES", results.size()); - return results; - - } catch (Exception e) { - logger.error("ES universal search failed: {}", e.getMessage(), e); - logger.info("Fallback: Searching in MySQL database"); + } + + b.minimumShouldMatch("1"); + return b; + }) + ) + .size(100) + , BeneficiariesESDTO.class); + + logger.info("ES returned {} hits for query: '{}'", response.hits().hits().size(), query); + + List> allResults = response.hits().hits().stream() + .map(hit -> { + if (hit.source() != null) { + logger.debug("Hit score: {}, benRegId: {}, benId: {}, phoneNum: {}, healthID: {}, abhaID: {}", + hit.score(), + hit.source().getBenRegId(), + hit.source().getBeneficiaryID(), + hit.source().getPhoneNum(), + hit.source().getHealthID(), + hit.source().getAbhaID()); + } + return mapESResultToExpectedFormat(hit.source()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (allResults.isEmpty()) { + logger.info("No results found in ES, falling back to database"); return searchInDatabaseDirectly(query); } + + if (userLocation != null) { + allResults = rankByLocation(allResults, userLocation); + } + + return allResults.stream().limit(20).collect(Collectors.toList()); + + } catch (Exception e) { + logger.error("ES universal search failed: {}", e.getMessage(), e); + logger.info("Fallback: Searching in MySQL database"); + return searchInDatabaseDirectly(query); } - +} /** * Overloaded method without userId (backward compatibility) */ From ff4f1bcfd71b06701a60f7bf6985046b31f1daf7 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 23 Dec 2025 09:37:08 +0530 Subject: [PATCH 13/13] fix: update the env variable --- src/main/environment/common_ci.properties | 2 +- src/main/environment/common_docker.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index f8b32d0e..98736180 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -29,7 +29,7 @@ elasticsearch.host=@env.ELASTICSEARCH_HOST@ elasticsearch.port=@env.ELASTICSEARCH_PORT@ elasticsearch.username=@env.ELASTICSEARCH_USERNAME@ elasticsearch.password=@env.ELASTICSEARCH_PASSWORD@ -elasticsearch.index.beneficiary=@env.ELASTICSEARCH_BENEFICIARY_INDEX@ +elasticsearch.index.beneficiary=@env.ELASTICSEARCH_INDEX_BENEFICIARY@ # Enable/Disable ES (for gradual rollout) elasticsearch.enabled=@env.ELASTICSEARCH_ENABLED@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index f870b4b9..8f66c5b0 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -29,7 +29,7 @@ elasticsearch.host=${ELASTICSEARCH_HOST} elasticsearch.port=${ELASTICSEARCH_PORT} elasticsearch.username=${ELASTICSEARCH_USERNAME} elasticsearch.password=${ELASTICSEARCH_PASSWORD} -elasticsearch.index.beneficiary=${ELASTICSEARCH_BENEFICIARY_INDEX} +elasticsearch.index.beneficiary=${ELASTICSEARCH_INDEX_BENEFICIARY} # Enable/Disable ES (for gradual rollout) elasticsearch.enabled=${ELASTICSEARCH_ENABLED} \ No newline at end of file