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

com.breuninger.boot.mongo.AbstractMongoRepository Maven / Gradle / Ivy

There is a newer version: 2.0.5.RELEASE
Show newest version
package com.breuninger.boot.mongo;

import static java.util.Objects.isNull;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;

import static com.breuninger.boot.mongo.UpdateIfMatchResult.CONCURRENTLY_MODIFIED;
import static com.breuninger.boot.mongo.UpdateIfMatchResult.NOT_FOUND;
import static com.breuninger.boot.mongo.UpdateIfMatchResult.OK;
import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.ReturnDocument.AFTER;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.annotation.PostConstruct;

import org.bson.BsonDocument;
import org.bson.Document;

import com.breuninger.boot.mongo.configuration.MongoProperties;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.UpdateOptions;

public abstract class AbstractMongoRepository {

  public static final String ID = "_id";
  public static final String ETAG = "etag";
  private static final boolean DISABLE_PARALLEL_STREAM_PROCESSING = false;
  private static final UpdateOptions BULK_UPSERT_OPERATION = new UpdateOptions().upsert(true);
  private static final BulkWriteOptions BULK_WRITE_OPTIONS = new BulkWriteOptions().ordered(false);
  protected final MongoProperties mongoProperties;

  public AbstractMongoRepository(final MongoProperties mongoProperties) {
    this.mongoProperties = mongoProperties;
  }

  /**
   * @deprecated Use {@link #AbstractMongoRepository(MongoProperties)} instead.
   */
  @Deprecated
  public AbstractMongoRepository() {
    mongoProperties = new MongoProperties();
    mongoProperties.setDefaultReadTimeout(2000);
    mongoProperties.setDefaultWriteTimeout(2000);
  }

  protected static  Stream toStream(final Iterable iterable) {
    return StreamSupport.stream(iterable.spliterator(), DISABLE_PARALLEL_STREAM_PROCESSING);
  }

  @PostConstruct
  public void postConstruct() {
    ensureIndexes();
  }

  public Optional findOne(final K key) {
    return findOne(key, mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public Optional findOne(final K key, final long maxTime, final TimeUnit timeUnit) {
    return ofNullable(collection().find(byId(key)).maxTime(maxTime, timeUnit).map(this::decode).first());
  }

  public Stream findAllAsStream() {
    return findAllAsStream(mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public Stream findAllAsStream(final long maxTime, final TimeUnit timeUnit) {
    return toStream(collection().find().maxTime(maxTime, timeUnit)).map(this::decode);
  }

  public List findAll() {
    return findAll(mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public List findAll(final long maxTime, final TimeUnit timeUnit) {
    return findAllAsStream(maxTime, timeUnit).collect(toList());
  }

  public Stream findAllAsStream(final int skip, final int limit) {
    return findAllAsStream(skip, limit, mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public Stream findAllAsStream(final int skip, final int limit, final long maxTime, final TimeUnit timeUnit) {
    return toStream(getFindIterable(skip, limit).maxTime(maxTime, timeUnit)).map(this::decode);
  }

  private FindIterable getFindIterable(final int skip, final int limit) {
    return collection().find().skip(skip).limit(limit);
  }

  public List findAll(final int skip, final int limit) {
    return findAll(skip, limit, mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public List findAll(final int skip, final int limit, final long maxTime, final TimeUnit timeUnit) {
    return findAllAsStream(skip, limit, maxTime, timeUnit).collect(toList());
  }

  public V createOrUpdate(final V value) {
    return createOrUpdate(value, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public V createOrUpdate(final V value, final long maxTime, final TimeUnit timeUnit) {
    final var doc = encode(value);
    collectionWithWriteTimeout(maxTime, timeUnit).replaceOne(byId(keyOf(value)), doc, new UpdateOptions().upsert(true));
    return decode(doc);
  }

  public void createOrUpdateBulk(final Collection values) {
    createOrUpdateBulk(values, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public void createOrUpdateBulk(final Collection values, final long maxTime, final TimeUnit timeUnit) {
    if (values.isEmpty()) {
      return;
    }
    final var bulkOperations = values.stream()
      .map(value -> new ReplaceOneModel<>(eq(ID, keyOf(value)), encode(value), BULK_UPSERT_OPERATION))
      .collect(toList());
    collectionWithWriteTimeout(maxTime, timeUnit).bulkWrite(bulkOperations, BULK_WRITE_OPTIONS);
  }

  protected MongoCollection collectionWithWriteTimeout(final long maxTime, final TimeUnit timeUnit) {
    return collection().withWriteConcern(collection().getWriteConcern().withWTimeout(maxTime, timeUnit));
  }

  public V create(final V value) {
    return create(value, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public V create(final V value, final long maxTime, final TimeUnit timeUnit) {
    final var key = keyOf(value);
    if (key != null) {
      final var doc = encode(value);
      collectionWithWriteTimeout(maxTime, timeUnit).insertOne(doc);
      return decode(doc);
    } else {
      throw new NullPointerException("Key must not be null");
    }
  }

  public boolean update(final V value) {
    return update(value, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public boolean update(final V value, final long maxTime, final TimeUnit timeUnit) {
    final var key = keyOf(value);
    if (key != null) {
      return collectionWithWriteTimeout(maxTime, timeUnit).replaceOne(byId(key), encode(value)).getModifiedCount() == 1;
    } else {
      throw new IllegalArgumentException("Key must not be null");
    }
  }

  public UpdateIfMatchResult updateIfMatch(final V value, final String eTag) {
    return updateIfMatch(value, eTag, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public UpdateIfMatchResult updateIfMatch(final V value, final String eTag, final long maxTime, final TimeUnit timeUnit) {
    final var key = keyOf(value);
    if (key != null) {
      final var query = and(eq(AbstractMongoRepository.ID, key), eq(ETAG, eTag));

      final var updatedETaggable = collectionWithWriteTimeout(maxTime, timeUnit).findOneAndReplace(query, encode(value),
        new FindOneAndReplaceOptions().returnDocument(AFTER));
      if (isNull(updatedETaggable)) {
        final var documentExists =
          collection().count(eq(AbstractMongoRepository.ID, key), new CountOptions().maxTime(maxTime, timeUnit)) != 0;
        if (documentExists) {
          return CONCURRENTLY_MODIFIED;
        }

        return NOT_FOUND;
      }

      return OK;
    } else {
      throw new IllegalArgumentException("Key must not be null");
    }
  }

  public long size() {
    return size(mongoProperties.getDefaultReadTimeout(), TimeUnit.MILLISECONDS);
  }

  public long size(final long maxTime, final TimeUnit timeUnit) {
    return collection().count(new BsonDocument(), new CountOptions().maxTime(maxTime, timeUnit));
  }

  public void delete(final K key) {
    delete(key, mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public void delete(final K key, final long maxTime, final TimeUnit timeUnit) {
    collectionWithWriteTimeout(maxTime, timeUnit).deleteOne(byId(key));
  }

  public void deleteAll() {
    deleteAll(mongoProperties.getDefaultWriteTimeout(), TimeUnit.MILLISECONDS);
  }

  public void deleteAll(final long maxTime, final TimeUnit timeUnit) {
    collectionWithWriteTimeout(maxTime, timeUnit).deleteMany(matchAll());
  }

  protected Document byId(final K key) {
    if (key != null) {
      return new Document(ID, key.toString());
    } else {
      throw new NullPointerException("Key must not be null");
    }
  }

  protected Document matchAll() {
    return new Document();
  }

  protected abstract MongoCollection collection();

  protected abstract K keyOf(final V value);

  protected abstract Document encode(final V value);

  protected abstract V decode(final Document document);

  protected abstract void ensureIndexes();
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy