From f8c7fd7ef6b60f238dec112fcba71c188a184132 Mon Sep 17 00:00:00 2001 From: tvd12 Date: Thu, 10 Sep 2026 16:49:47 +0700 Subject: [PATCH] delete vector --- .../ezyvector/service/EzyVectorService.java | 50 +++++++++++++++++++ .../storage/EzyVectorFileStorage.java | 31 ++++++++++++ .../WebApiEzyVectorCollectionController.java | 15 ++++++ .../WebEzyVectorPointsControllerService.java | 13 +++++ .../request/WebDeleteVectorPointsRequest.java | 28 +++++++++++ .../WebEzyVectorPointsValidator.java | 21 ++++++++ 6 files changed, 158 insertions(+) create mode 100644 ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/request/WebDeleteVectorPointsRequest.java diff --git a/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/service/EzyVectorService.java b/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/service/EzyVectorService.java index 61a876a..c0d320e 100644 --- a/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/service/EzyVectorService.java +++ b/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/service/EzyVectorService.java @@ -138,6 +138,38 @@ public void upsert( startHnswBuildIfNecessary(collectionId, vectorSize); } + public void deletePoints( + String collectionName, + List pointIds + ) throws Exception { + EzyVectorCollectionVectorSizeResult collection = + getVectorSizeResultEntityByNameOrThrow( + collectionName + ); + long collectionId = collection.getId(); + synchronized (writeLock) { + List points = + new ArrayList<>(pointIds.size()); + List slotIds = new ArrayList<>(pointIds.size()); + for (Long pointId : pointIds) { + EzyVectorCollectionPoint point = collectionPointRepository + .findByCollectionIdAndPointId( + collectionId, + pointId + ); + if (point != null) { + points.add(point); + slotIds.add(point.getId()); + } + } + newVectorFileStorage().deleteAll(collectionId, slotIds); + deleteFromHnswIndex(collectionId, points); + for (EzyVectorCollectionPoint point : points) { + collectionPointRepository.delete(point.getId()); + } + } + } + public List search( String collectionName, float[] vector, @@ -466,6 +498,24 @@ private void updateHnswIndex( } } + private void deleteFromHnswIndex( + long collectionId, + List points + ) throws Exception { + HnswIndex index = hnswIndexByCollectionId.get(collectionId); + if (index == null) { + return; + } + for (EzyVectorCollectionPoint point : points) { + index.remove(point.getPointId()); + } + if (readyHnswCollectionIds.contains(collectionId)) { + index.save( + newVectorFileStorage().getHnswPath(collectionId) + ); + } + } + private void buildHnswIndex(long collectionId) { try { HnswIndex index = hnswIndexByCollectionId diff --git a/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/storage/EzyVectorFileStorage.java b/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/storage/EzyVectorFileStorage.java index 6d37509..db1cb35 100644 --- a/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/storage/EzyVectorFileStorage.java +++ b/ezyvector-sdk/src/main/java/org/youngmonkeys/ezyvector/storage/EzyVectorFileStorage.java @@ -99,6 +99,37 @@ public void upsertAll( } } + public void deleteAll( + long collectionId, + List slotIds + ) throws IOException { + if (slotIds.isEmpty()) { + return; + } + Path pointIdsFile = getSegmentDir(collectionId) + .resolve(POINT_IDS_FILE); + if (!Files.isRegularFile(pointIdsFile)) { + return; + } + try ( + FileChannel pointIdChannel = FileChannel.open( + pointIdsFile, + StandardOpenOption.WRITE + ) + ) { + for (Long slotId : slotIds) { + validateSlotId(slotId); + long pointIdOffset = (slotId - 1L) * Long.BYTES; + writeFully( + pointIdChannel, + toLongBuffer(0L), + pointIdOffset + ); + } + pointIdChannel.force(true); + } + } + public List search( long collectionId, long vectorSize, diff --git a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/api/WebApiEzyVectorCollectionController.java b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/api/WebApiEzyVectorCollectionController.java index c8aacd4..0e82958 100644 --- a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/api/WebApiEzyVectorCollectionController.java +++ b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/api/WebApiEzyVectorCollectionController.java @@ -38,6 +38,7 @@ import org.youngmonkeys.ezyvector.web.controller.service.WebEzyVectorSearchControllerService; import org.youngmonkeys.ezyvector.web.converter.WebEzyVectorRequestToModelConverter; import org.youngmonkeys.ezyvector.web.request.WebCreateVectorCollectionRequest; +import org.youngmonkeys.ezyvector.web.request.WebDeleteVectorPointsRequest; import org.youngmonkeys.ezyvector.web.request.WebEzyVectorSearchRequest; import org.youngmonkeys.ezyvector.web.request.WebUpsertVectorPointsRequest; import org.youngmonkeys.ezyvector.web.response.WebCreateVectorCollectionResponse; @@ -105,6 +106,20 @@ public WebUpsertVectorPointsResponse collectionNamePointsPut( ); } + @DoPost("/{collectionName}/points/delete") + public WebUpsertVectorPointsResponse collectionNamePointsDeletePost( + RequestArguments arguments, + @PathVariable String collectionName, + @RequestBody WebDeleteVectorPointsRequest request + ) throws Exception { + vectorCollectionValidator.validateAuthentication(arguments); + vectorPointsValidator.validate(request); + return vectorPointsControllerService.deletePoints( + collectionName, + request + ); + } + @DoPost("/{collectionName}/points/search") public WebEzyVectorSearchResponse collectionNamePointsSearchPost( RequestArguments arguments, diff --git a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/service/WebEzyVectorPointsControllerService.java b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/service/WebEzyVectorPointsControllerService.java index 93eea6c..c85d3f5 100644 --- a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/service/WebEzyVectorPointsControllerService.java +++ b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/controller/service/WebEzyVectorPointsControllerService.java @@ -20,6 +20,7 @@ import lombok.AllArgsConstructor; import org.youngmonkeys.ezyvector.web.controller.decorator.WebEzyVectorPointsModelDecorator; import org.youngmonkeys.ezyvector.web.converter.WebEzyVectorRequestToModelConverter; +import org.youngmonkeys.ezyvector.web.request.WebDeleteVectorPointsRequest; import org.youngmonkeys.ezyvector.web.request.WebUpsertVectorPointsRequest; import org.youngmonkeys.ezyvector.web.response.WebUpsertVectorPointsResponse; import org.youngmonkeys.ezyvector.web.service.WebEzyVectorService; @@ -43,4 +44,16 @@ public WebUpsertVectorPointsResponse upsertPoints( return vectorPointsModelDecorator .decorateToUpsertVectorPointsResponse(); } + + public WebUpsertVectorPointsResponse deletePoints( + String collectionName, + WebDeleteVectorPointsRequest request + ) throws Exception { + vectorService.deletePoints( + collectionName, + request.getPoints() + ); + return vectorPointsModelDecorator + .decorateToUpsertVectorPointsResponse(); + } } diff --git a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/request/WebDeleteVectorPointsRequest.java b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/request/WebDeleteVectorPointsRequest.java new file mode 100644 index 0000000..166ed05 --- /dev/null +++ b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/request/WebDeleteVectorPointsRequest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2026 youngmonkeys.org + * + * Licensed under the ezyplatform, Version 1.0.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://youngmonkeys.org/licenses/ezyplatform-1.0.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.youngmonkeys.ezyvector.web.request; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class WebDeleteVectorPointsRequest { + private List points; +} diff --git a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/validator/WebEzyVectorPointsValidator.java b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/validator/WebEzyVectorPointsValidator.java index 606c57e..885edde 100644 --- a/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/validator/WebEzyVectorPointsValidator.java +++ b/ezyvector-web-plugin/src/main/java/org/youngmonkeys/ezyvector/web/validator/WebEzyVectorPointsValidator.java @@ -18,6 +18,7 @@ import com.tvd12.ezyfox.bean.annotation.EzySingleton; import com.tvd12.ezyhttp.core.exception.HttpBadRequestException; +import org.youngmonkeys.ezyvector.web.request.WebDeleteVectorPointsRequest; import org.youngmonkeys.ezyvector.web.request.WebUpsertVectorPointsRequest; import java.util.HashMap; @@ -27,6 +28,26 @@ @EzySingleton public class WebEzyVectorPointsValidator { + public void validate( + WebDeleteVectorPointsRequest request + ) { + Map errors = new HashMap<>(); + List points = request.getPoints(); + if (points == null || points.isEmpty()) { + errors.put("points", "required"); + } else { + for (int i = 0; i < points.size(); ++i) { + Long pointId = points.get(i); + if (pointId == null || pointId <= 0L) { + errors.put("points[" + i + "]", "invalid"); + } + } + } + if (!errors.isEmpty()) { + throw new HttpBadRequestException(errors); + } + } + public void validate( WebUpsertVectorPointsRequest request ) {