All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.smallmind.mongodb.throng.ThrongClient Maven / Gradle / Ivy

/*
 * Copyright (c) 2007 through 2024 David Berkman
 *
 * This file is part of the SmallMind Code Project.
 *
 * The SmallMind Code Project is free software, you can redistribute
 * it and/or modify it under either, at your discretion...
 *
 * 1) The terms of GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * ...or...
 *
 * 2) The terms of the Apache License, Version 2.0.
 *
 * The SmallMind Code Project is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License or Apache License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * and the Apache License along with the SmallMind Code Project. If not, see
 *  or .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7
 * ------------------------------------------------------------------
 * If you modify this Program, or any covered work, by linking or
 * combining it with other code, such other code is not for that reason
 * alone subject to any of the requirements of the GNU Affero GPL
 * version 3.
 */
package org.smallmind.mongodb.throng;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.InsertOneOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertOneResult;
import org.bson.BsonDocument;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.smallmind.mongodb.throng.codec.ArrayCodecProvider;
import org.smallmind.mongodb.throng.index.IndexUtility;
import org.smallmind.mongodb.throng.mapping.EmbeddedReferences;
import org.smallmind.mongodb.throng.mapping.ThrongEmbeddedUtility;
import org.smallmind.mongodb.throng.mapping.ThrongEntity;
import org.smallmind.mongodb.throng.mapping.ThrongEntityCodec;
import org.smallmind.mongodb.throng.mapping.annotation.Embedded;
import org.smallmind.mongodb.throng.mapping.annotation.Entity;
import org.smallmind.mongodb.throng.query.Filter;
import org.smallmind.mongodb.throng.query.Query;
import org.smallmind.mongodb.throng.query.Updates;

/*
  Weaknesses...
    1) Entity classes can not be marked as polymorphic.
    2) Embedded classes can not have lifecycle methods.
    3) There's no fully correct automated handling for containers with generics of either @Embedded or @Codec types, i.e. List, Map, Bag, etc.
       Containers of @Embedded types will miss automated index processing, and containers of @Codec types will throw an exception due to mismatch
       of the field and codec. Creating container subclasses, with codecs parameterized for those subclasses, and, in the case of @Embedded types,
       adding the appropriate indexes to the parent class, is the correct route.
*/
public class ThrongClient {

  private final MongoDatabase mongoDatabase;
  private final CodecRegistry codecRegistry;
  private final HashMap, ThrongEntityCodec> entityCodecMap = new HashMap<>();

  public ThrongClient (MongoClient mongoClient, String database, ThrongOptions options, Class... entityClasses)
    throws ThrongMappingException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

    CodecRegistry driverCodecRegistry;

    EmbeddedReferences embeddedReferences = new EmbeddedReferences();

    mongoDatabase = mongoClient.getDatabase(database);
    driverCodecRegistry = CodecRegistries.fromRegistries(mongoDatabase.getCodecRegistry(), CodecRegistries.fromProviders(new ArrayCodecProvider(options.isStoreNulls())));

    if (entityClasses != null) {
      for (Class entityClass : entityClasses) {

        Embedded embedded;

        if ((embedded = entityClass.getAnnotation(Embedded.class)) != null) {
          ThrongEmbeddedUtility.generateEmbeddedCodec(entityClass, embedded, driverCodecRegistry, embeddedReferences, options.isStoreNulls());
        }
      }

      for (Class entityClass : entityClasses) {

        Entity entity;

        if ((entity = entityClass.getAnnotation(Entity.class)) != null) {

          ThrongEntityCodec entityCodec;

          entityCodecMap.put(entityClass, entityCodec = new ThrongEntityCodec<>(new ThrongEntity<>(entityClass, entity, CodecRegistries.fromRegistries(CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new LinkedList<>(embeddedReferences.values()))), driverCodecRegistry), embeddedReferences, options.isStoreNulls())));
          if (options.isCreateIndexes()) {
            IndexUtility.createIndex(mongoDatabase.getCollection(entityCodec.getCollection()), entityCodec.provideIndexes(), options.isIncludeCollation());
          }
        }
      }
    }

    codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new ThrongDocumentCodec()), CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new LinkedList<>(embeddedReferences.values()))), driverCodecRegistry);
  }

  private  ThrongEntityCodec getCodec (Class entityClass) {

    ThrongEntityCodec entityCodec;

    if ((entityCodec = (ThrongEntityCodec)entityCodecMap.get(entityClass)) == null) {
      throw new ThrongRuntimeException("Unmapped entity type(%s)", entityClass);
    } else {

      return entityCodec;
    }
  }

  public  long count (Class entityClass, Filter filter) {

    ThrongEntityCodec entityCodec = getCodec(entityClass);

    return mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).countDocuments(filter.toBsonDocument(BsonDocument.class, codecRegistry));
  }

  public  ThrongIterable find (Class entityClass, Query query) {

    ThrongEntityCodec entityCodec = getCodec(entityClass);

    return new ThrongIterable<>(query.apply(mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).find(), BsonDocument.class, codecRegistry), entityCodec);
  }

  public  T findOne (Class entityClass, Query query) {

    ThrongEntityCodec entityCodec = getCodec(entityClass);
    FindIterable findIterable = query.apply(mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).find(), BsonDocument.class, codecRegistry).limit(1);
    ThrongDocument throngDocument;

    if ((throngDocument = findIterable.first()) == null) {

      return null;
    } else {

      return TranslationUtility.fromBson(entityCodec, throngDocument.getBsonDocument());
    }
  }

  public  InsertOneResult insert (T value, InsertOneOptions options) {

    ThrongEntityCodec entityCodec = getCodec((Class)value.getClass());

    return mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).insertOne(new ThrongDocument(TranslationUtility.toBson(value, entityCodec)));
  }

  public  UpdateResult update (Class entityClass, Filter filter, Updates updates, UpdateOptions updateOptions) {

    ThrongEntityCodec entityCodec = getCodec(entityClass);

    return new UpdateResult(mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).updateMany(filter.toBsonDocument(BsonDocument.class, codecRegistry), updates.toBsonDocument(BsonDocument.class, codecRegistry), updateOptions));
  }

  public  DeleteResult delete (Class entityClass, Filter filter, DeleteOptions deleteOptions) {

    ThrongEntityCodec entityCodec = getCodec(entityClass);

    return mongoDatabase.getCollection(entityCodec.getCollection()).withCodecRegistry(codecRegistry).withDocumentClass(ThrongDocument.class).deleteMany(filter.toBsonDocument(BsonDocument.class, codecRegistry), deleteOptions);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy