content: /*
   *  Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com).
   *  <p>
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *  <p>
   *  http://www.apache.org/licenses/LICENSE-2.0
   *  <p>
   *  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 com.agentsflex.store.vectorex;
  
  import com.agentsflex.core.document.Document;
  import com.agentsflex.core.store.DocumentStore;
  import com.agentsflex.core.store.SearchWrapper;
  import com.agentsflex.core.store.StoreOptions;
  import com.agentsflex.core.store.StoreResult;
  import com.agentsflex.core.util.CollectionUtil;
  import com.agentsflex.core.util.VectorUtil;
  import com.google.common.collect.Lists;
  import io.github.javpower.vectorex.keynote.core.DbData;
  import io.github.javpower.vectorex.keynote.core.VectorData;
  import io.github.javpower.vectorex.keynote.core.VectorSearchResult;
  import io.github.javpower.vectorex.keynote.model.MetricType;
  import io.github.javpower.vectorex.keynote.model.VectorFiled;
  import io.github.javpower.vectorexcore.VectoRexClient;
  import io.github.javpower.vectorexcore.entity.KeyValue;
  import io.github.javpower.vectorexcore.entity.ScalarField;
  import io.github.javpower.vectorexcore.entity.VectoRexEntity;
  
  import java.util.*;
  
  public class VectoRexStore extends DocumentStore {
  
      private final VectoRexStoreConfig config;
      private final VectoRexClient client;
      private final String defaultCollectionName;
      private boolean isCreateCollection=false;
  
      public VectoRexStore(VectoRexStoreConfig config) {
          this.config = config;
          this.defaultCollectionName=config.getDefaultCollectionName();
          this.client = new VectoRexClient(config.getUri());
      }
      @Override
      public StoreResult storeInternal(List<Document> documents, StoreOptions options) {
          List<DbData> data=new ArrayList<>();
          for (Document doc : documents) {
              Map<String, Object> dict=new HashMap<>();
              dict.put("id",String.valueOf(doc.getId()));
              dict.put("content", doc.getContent());
              dict.put("vector", VectorUtil.toFloatList(doc.getVector()));
              DbData dbData=new DbData();
              dbData.setId(String.valueOf(doc.getId()));
              dbData.setMetadata(dict);
              VectorData vd=new VectorData(dbData.getId(),VectorUtil.toFloatArray(doc.getVector()));
              vd.setName("vector");
              dbData.setVectorFiled(Lists.newArrayList(vd));
              data.add(dbData);
          }
          String collectionName = options.getCollectionNameOrDefault(defaultCollectionName);
          if(config.isAutoCreateCollection()&&!isCreateCollection){
              List<VectoRexEntity> collections = client.getCollections();
              if(CollectionUtil.noItems(collections)||collections.stream().noneMatch(e -> e.getCollectionName().equals(collectionName))){
                  createCollection(collectionName);
              }else {
                  isCreateCollection=true;
              }
          }
          if(CollectionUtil.hasItems(data)){
              client.getStore(collectionName).saveAll(data);
          }
          return StoreResult.successWithIds(documents);
      }
  
  
      private void createCollection(String collectionName) {
          VectorFiled vectorFiled = new VectorFiled();
          vectorFiled.setDimensions(this.getEmbeddingModel().dimensions());
          vectorFiled.setName("vector");
          vectorFiled.setMetricType(MetricType.FLOAT_COSINE_DISTANCE);
          VectoRexEntity entity=new VectoRexEntity();
          entity.setCollectionName(collectionName);
          List<KeyValue<String, VectorFiled>> vectorFiles=new ArrayList<>();
          vectorFiles.add(new KeyValue<>("vector",vectorFiled));
          List<KeyValue<String, ScalarField>> scalarFields=new ArrayList<>();
          ScalarField id = new ScalarField();
          id.setName("id");
          id.setIsPrimaryKey(true);
          scalarFields.add(new KeyValue<>("id",id));
          ScalarField content = new ScalarField();
          content.setName("content");
          content.setIsPrimaryKey(false);
          scalarFields.add(new KeyValue<>("content",content));
          entity.setVectorFileds(vectorFiles);
          entity.setScalarFields(scalarFields);
          client.createCollection(entity);
      }
  
      @Override
      public StoreResult deleteInternal(Collection<?> ids, StoreOptions options) {
          client.getStore(options.getCollectionNameOrDefault(defaultCollectionName)).deleteAll((List<String>) ids);
          return StoreResult.success();
  
      }
  
      @Override
      public List<Document> searchInternal(SearchWrapper searchWrapper, StoreOptions options) {
          List<VectorSearchResult> data = client.getStore(options.getCollectionNameOrDefault(defaultCollectionName)).search("vector", VectorUtil.toFloatList(searchWrapper.getVector()), searchWrapper.getMaxResults(), null);
          List<Document> documents = new ArrayList<>();
          for (VectorSearchResult result : data) {
              DbData dd = result.getData();
              Map<String, Object> metadata = dd.getMetadata();
              Document doc=new Document();
              doc.setId(result.getId());
              doc.setContent((String) metadata.get("content"));
              Object vectorObj = metadata.get("vector");
              if (vectorObj instanceof List) {
                  //noinspection unchecked
                  doc.setVector(VectorUtil.convertToVector((List<Float>) vectorObj));
              }
              documents.add(doc);
          }
          return documents;
      }
  
      @Override
      public StoreResult updateInternal(List<Document> documents, StoreOptions options) {
          if (documents == null || documents.isEmpty()) {
              return StoreResult.success();
          }
          for (Document doc : documents) {
              Map<String, Object> dict=new HashMap<>();
              dict.put("id",String.valueOf(doc.getId()));
              dict.put("content", doc.getContent());
              dict.put("vector", VectorUtil.toFloatList(doc.getVector()));
              DbData dbData=new DbData();
              dbData.setId(String.valueOf(doc.getId()));
              dbData.setMetadata(dict);
              VectorData vd=new VectorData(dbData.getId(),VectorUtil.toFloatArray(doc.getVector()));
              vd.setName("vector");
              dbData.setVectorFiled(Lists.newArrayList(vd));
              client.getStore(options.getCollectionNameOrDefault(defaultCollectionName)).update(dbData);
          }
          return StoreResult.successWithIds(documents);
      }
  
      public VectoRexClient getClient() {
          return client;
      }
  
  }
  