diff --git a/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/FaceVectorClientRepository.java b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/FaceVectorClientRepository.java new file mode 100644 index 00000000..bd10ec73 --- /dev/null +++ b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/FaceVectorClientRepository.java @@ -0,0 +1,37 @@ +package com.umc.naoman.domain.photo.elasticsearch.repository; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldValue; +import com.umc.naoman.global.error.BusinessException; +import com.umc.naoman.global.error.code.ElasticsearchErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.io.IOException; +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class FaceVectorClientRepository { + private final ElasticsearchClient elasticsearchClient; + + //photoName으로 face_vectors 삭제 + public void deleteFaceVectorsByPhotoName(List photoNameList){ + List fieldValuePhotoNameList = photoNameList.stream() + .map(FieldValue::of) + .toList(); + try { + elasticsearchClient.deleteByQuery(delete -> delete + .index("face_vectors") + .query(q -> q + .terms(t -> t + .field("name") + .terms(te -> te.value(fieldValuePhotoNameList)) + ) + ) + ); + } catch (IOException e){ + throw new BusinessException(ElasticsearchErrorCode.ELASTICSEARCH_IOEXCEPTION, e); + } + } +} diff --git a/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepository.java b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepository.java index f1620568..e81fc2c2 100644 --- a/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepository.java +++ b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepository.java @@ -1,6 +1,7 @@ package com.umc.naoman.domain.photo.elasticsearch.repository; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldValue; import co.elastic.clients.elasticsearch.core.BulkRequest; import co.elastic.clients.elasticsearch.core.BulkResponse; import co.elastic.clients.elasticsearch.core.SearchResponse; @@ -26,6 +27,7 @@ @RequiredArgsConstructor public class PhotoEsClientRepository { private final ElasticsearchClient elasticsearchClient; + private final FaceVectorClientRepository faceVectorClientRepository; //사진 업로드 시 ES에 벌크로 업로드 public void savePhotoBulk(List photoList) { @@ -158,6 +160,110 @@ public Page findPhotoEsByShareGroupIdAndNotFaceTag(Long shareGroupId, P return toPagePhotoEs(response.hits().hits(), pageable); } + // rdsId로 ES에서 사진 삭제 + public void deletePhotoEsByRdsId(List rdsIdList, Long shareGroupId) { + List fieldValueList = rdsIdList.stream() + .map(FieldValue::of) + .toList(); + try { + elasticsearchClient.deleteByQuery(d -> d + .index("photos_es") + .routing(shareGroupId.toString()) + .query(q -> q + .terms(t -> t + .field("rdsId") + .terms(te -> te.value(fieldValueList)) + ) + ) + ); + } catch (IOException e) { + throw new BusinessException(ElasticsearchErrorCode.ELASTICSEARCH_IOEXCEPTION, e); + } + } + + //특정 회원의 얼굴이 태그된 사진 삭제 -> 해당 사진에서 감지된 얼굴벡터도 함께 삭제 return : 삭제된 사진의 rdsId + public List deletePhotoEsByFaceTag(Long memberId){ + SearchResponse response = null; + List rdsIdList = new ArrayList<>(); + List photoNameList = new ArrayList<>(); + try{ + response = elasticsearchClient.search(s -> s + .index("photos_es") + .from(0) + .size(5000) + .query(q -> q + .term(t -> t + .field("faceTag") + .value(FieldValue.of(memberId)) + ) + ), + PhotoEs.class); + elasticsearchClient.deleteByQuery(d -> d + .index("photos_es") + .query(q -> q + .term(t -> t + .field("faceTag") + .value(FieldValue.of(memberId)) + ) + ) + ); + rdsIdList = response.hits().hits().stream() + .map(hit -> hit.source().getRdsId()) + .toList(); + photoNameList = response.hits().hits().stream() + .map(hit -> hit.source().getName()) + .toList(); + } catch (IOException e) { + throw new BusinessException(ElasticsearchErrorCode.ELASTICSEARCH_IOEXCEPTION, e); + } + //삭제된 사진에서 감지된 얼굴 벡터 삭제 + faceVectorClientRepository.deleteFaceVectorsByPhotoName(photoNameList); + return rdsIdList; + } + + //특정 공유 그룹의 사진 삭제 -> 해당 사진에서 감지된 얼굴벡터도 함께 샥제 return : 삭제된 사진의 rdsId + public List deletePhotoEsByShareGroupIdList(List shareGroupIdList) { + List fieldValueShareGroupList = shareGroupIdList.stream() + .map(FieldValue::of) + .toList(); + SearchResponse response = null; + List rdsIdList = new ArrayList<>(); + List photoNameList = new ArrayList<>(); + try { + response = elasticsearchClient.search(s -> s + .index("photos_es") + .from(0) + .size(5000) + .query(q -> q + .terms(t -> t + .field("shareGroupId") + .terms(te -> te.value(fieldValueShareGroupList)) + ) + ), + PhotoEs.class); + elasticsearchClient.deleteByQuery(d -> d + .index("photos_es") + .query(q -> q + .terms(t -> t + .field("shareGroupId") + .terms(te -> te.value(fieldValueShareGroupList)) + ) + ) + ); + rdsIdList = response.hits().hits().stream() + .map(hit -> hit.source().getRdsId()) + .toList(); + photoNameList = response.hits().hits().stream() + .map(hit -> hit.source().getName()) + .toList(); + } catch (IOException e) { + throw new BusinessException(ElasticsearchErrorCode.ELASTICSEARCH_IOEXCEPTION, e); + } + //삭제된 사진에서 감지된 얼굴 벡터 삭제 + faceVectorClientRepository.deleteFaceVectorsByPhotoName(photoNameList); + return rdsIdList; + } + String esTimeFormat(LocalDateTime localDateTime) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return localDateTime.format(dateTimeFormatter); diff --git a/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/SampleFaceVectorClientRepository.java b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/SampleFaceVectorClientRepository.java new file mode 100644 index 00000000..3658dfd4 --- /dev/null +++ b/src/main/java/com/umc/naoman/domain/photo/elasticsearch/repository/SampleFaceVectorClientRepository.java @@ -0,0 +1,32 @@ +package com.umc.naoman.domain.photo.elasticsearch.repository; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldValue; +import com.umc.naoman.global.error.BusinessException; +import com.umc.naoman.global.error.code.ElasticsearchErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.io.IOException; + +@Repository +@RequiredArgsConstructor +public class SampleFaceVectorClientRepository { + private final ElasticsearchClient elasticsearchClient; + + public void deleteSampleFaceVectorByMemberId(Long memberId) { + try { + elasticsearchClient.deleteByQuery(d -> d + .index("sample_face_vectors") + .query(q -> q + .term(t -> t + .field("memberId") + .value(FieldValue.of(memberId)) + ) + ) + ); + } catch (IOException e){ + throw new BusinessException(ElasticsearchErrorCode.ELASTICSEARCH_IOEXCEPTION, e); + } + } +} diff --git a/src/test/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepositoryTest.java b/src/test/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepositoryTest.java new file mode 100644 index 00000000..9e80cae8 --- /dev/null +++ b/src/test/java/com/umc/naoman/domain/photo/elasticsearch/repository/PhotoEsClientRepositoryTest.java @@ -0,0 +1,20 @@ +package com.umc.naoman.domain.photo.elasticsearch.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class PhotoEsClientRepositoryTest { + @Autowired + private PhotoEsClientRepository photoEsClientRepository; + @Test + public void test() { + List val = photoEsClientRepository.deletePhotoEsByFaceTag(1001L); + + } +} \ No newline at end of file