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

org.mongojack.JacksonDBCollection Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program 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
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */
package org.mongojack;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.DuplicateKeyException;
import com.mongodb.ErrorCategory;
import com.mongodb.MongoException;
import com.mongodb.MongoServerException;
import com.mongodb.WriteConcern;
import com.mongodb.WriteConcernResult;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Collation;
import com.mongodb.client.model.CollationStrength;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateOptions;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.UuidRepresentation;
import org.bson.codecs.CollectibleCodec;
import org.bson.conversions.Bson;
import org.graylog2.database.jackson.CustomJacksonCodecRegistry;
import org.graylog2.database.jackson.legacy.LegacyDeleteResult;
import org.graylog2.database.jackson.legacy.LegacyInsertManyResult;
import org.graylog2.database.jackson.legacy.LegacyInsertOneResult;
import org.graylog2.database.jackson.legacy.LegacyUpdateOneResult;
import org.graylog2.database.jackson.legacy.LegacyUpdateResult;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * Compatibility layer to support existing code interacting with the Mongojack 2.x API.
 *
 * @deprecated use {@link org.graylog2.database.MongoCollections} as an entrypoint for interacting with MongoDB.
 */
@Deprecated
public class JacksonDBCollection {

    private final JacksonMongoCollection delegate;
    private final Class valueType;
    private final Class idType;
    private final ObjectMapper objectMapper;
    private final DBCollection dbCollection;

    public static  JacksonDBCollection wrap(
            DBCollection dbCollection, Class type, Class keyType,
            ObjectMapper objectMapper) {

        return new JacksonDBCollection<>(dbCollection, type, keyType, objectMapper);
    }

    private JacksonDBCollection(DBCollection dbCollection, Class valueType, Class idType, ObjectMapper objectMapper) {

        final MongoDatabase db = dbCollection.getDB().getMongoClient().getDatabase(dbCollection.getDB().getName());

        this.dbCollection = dbCollection;
        final JacksonMongoCollection jacksonMongoCollection = JacksonMongoCollection.builder()
                .withObjectMapper(objectMapper)
                .build(db, dbCollection.getName(), valueType, UuidRepresentation.UNSPECIFIED);
        final CustomJacksonCodecRegistry jacksonCodecRegistry = new CustomJacksonCodecRegistry(objectMapper,
                jacksonMongoCollection.getCodecRegistry());
        jacksonCodecRegistry.addCodecForClass(valueType);
        this.delegate = (JacksonMongoCollection) jacksonMongoCollection.withCodecRegistry(jacksonCodecRegistry);
        this.valueType = valueType;
        this.idType = idType;
        this.objectMapper = objectMapper;
    }

    public void createIndex(DBObject keys, DBObject options) {
        delegate.createIndex(new BasicDBObject(keys.toMap()), toIndexOptions(options));
    }

    public void createIndex(DBObject keys) {
        delegate.createIndex(new BasicDBObject(keys.toMap()));
    }

    public DBCursor find() {
        return new DBCursor<>(delegate, null, delegate::find);
    }

    public DBCursor find(Bson filter) {
        return new DBCursor<>(delegate, filter, () -> delegate.find(filter));
    }

    public DBCursor find(DBObject dbObject) {
        final BasicDBObject filter = new BasicDBObject(dbObject.toMap());
        return new DBCursor<>(delegate, filter, () -> delegate.find(filter));
    }

    public T findOneById(K objectId) {
        return delegate.findOneById(objectId);
    }

    public T findOne(Bson filter) throws MongoException {
        return delegate.findOne(filter);
    }

    public T findOne(DBObject filter) throws MongoException {
        return delegate.findOne(new BasicDBObject(filter.toMap()));
    }

    public T findOne() {
        return delegate.findOne();
    }

    public  Iterable distinct(String fieldName, Class tResultClass) {
        return delegate.distinct(fieldName, tResultClass);
    }

    public long count() {
        return delegate.estimatedDocumentCount();
    }

    public long count(Bson filter) {
        return delegate.countDocuments(filter);
    }

    public long count(DBObject dbObject) {
        return delegate.countDocuments(new BasicDBObject(dbObject.toMap()));
    }

    public WriteResult save(T object) {
        return save(object, null);
    }

    public WriteResult save(T object, WriteConcern concern) {
        return doSave(object, concern);
    }

    private WriteResult doSave(T object, WriteConcern concern) {
        final var collection = concern == null ? delegate : delegate.withWriteConcern(concern);

        final CollectibleCodec codec = (CollectibleCodec) delegate.getCodecRegistry().get(valueType);
        final BsonValue id = codec.getDocumentId(object);

        try {
            if (id == null || id.isNull()) {
                return new LegacyInsertOneResult<>(collection, collection.insertOne(object), idType);
            } else {
                final var idQuery = Filters.eq("_id", id);
                return new LegacyUpdateOneResult<>(collection, object,
                        collection.replaceOne(idQuery, object, new ReplaceOptions().upsert(true)), valueType, idType);
            }
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public LegacyDeleteResult remove(DBObject query) {
        return new LegacyDeleteResult<>(delegate.deleteMany(new BasicDBObject(query.toMap())));
    }

    public LegacyDeleteResult remove(Bson filter) {
        return new LegacyDeleteResult<>(delegate.deleteMany(filter));
    }

    public WriteResult remove(Bson filter, WriteConcern concern) {
        var coll = delegate.withWriteConcern(concern);
        return new LegacyDeleteResult<>(coll.deleteMany(filter));
    }

    public LegacyDeleteResult removeById(K objectId) {
        return new LegacyDeleteResult<>(delegate.removeById(objectId));
    }

    public WriteResult update(Bson filter, T object, boolean upsert, boolean multi) {
        return update(filter, object, upsert, multi, delegate.getWriteConcern());
    }

    public WriteResult update(Bson filter, T object, boolean upsert, boolean multi,
                                    @Nullable WriteConcern concern) {
        if (multi) {
            throw new IllegalArgumentException(("Multi-update is not supported for object-based updates."));
        }
        final var coll = concern == null ? delegate : delegate.withWriteConcern(concern);
        final var options = new ReplaceOptions().upsert(upsert);
        try {
            return new LegacyUpdateResult<>(coll.replaceOne(filter, object, options));
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public WriteResult update(Bson filter, Bson update, boolean upsert, boolean multi) {
        try {
            if (multi) {
                return new LegacyUpdateResult<>(delegate.updateMany(filter, update, new UpdateOptions().upsert(upsert)));
            } else {
                return new LegacyUpdateResult<>(delegate.updateOne(filter, update, new UpdateOptions().upsert(upsert)));
            }
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public WriteResult update(Bson query, Bson update) {
        return update(query, update, false, false);
    }

    public WriteResult update(DBObject query, Bson update) {
        return update((Bson) new BasicDBObject(query.toMap()), update);
    }

    public WriteResult update(Bson query, T object) {
        return update(query, object, false, false);
    }

    public void updateById(K id, Bson update) {
        try {
            delegate.updateById(id, update);
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public WriteResult updateById(K id, T update) {
        try {
            return new LegacyUpdateResult<>(delegate.replaceOneById(id, update));
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public WriteResult updateMulti(Bson query, Bson update) {
        return update(query, update, false, true);
    }

    public WriteResult insert(T object) {
        try {
            return new LegacyInsertOneResult<>(delegate, delegate.insertOne(object), idType);
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public WriteResult insert(List list) {
        try {
            return new LegacyInsertManyResult<>(delegate, delegate.insertMany(list), idType);
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public long getCount() {
        return delegate.countDocuments();
    }

    public long getCount(Bson filter) {
        return delegate.countDocuments(filter);
    }

    public T findAndModify(Bson filter, Bson fields, Bson sort, boolean remove, Bson update, boolean returnNew,
                           boolean upsert) {
        if (remove) {
            throw new IllegalArgumentException("Removing objects is not supported!");
        }

        var options = new FindOneAndUpdateOptions()
                .projection(fields)
                .sort(sort)
                .returnDocument(returnNew ? ReturnDocument.AFTER : ReturnDocument.BEFORE)
                .upsert(upsert);

        try {
            return delegate.findOneAndUpdate(filter, update, options);
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public T findAndModify(Bson query, Bson update) {
        return findAndModify(query, null, null, false, update, false, false);
    }

    public T findAndModify(Bson filter, Bson fields, Bson sort, boolean remove, T object, boolean returnNew, boolean upsert) {
        if (remove) {
            throw new IllegalArgumentException("Removing objects is not supported!");
        }
        var options = new FindOneAndReplaceOptions()
                .projection(fields)
                .sort(sort)
                .returnDocument(returnNew ? ReturnDocument.AFTER : ReturnDocument.BEFORE)
                .upsert(upsert);

        try {
            return delegate.findOneAndReplace(filter, object, options);
        } catch (MongoServerException e) {
            throw possiblyAsDuplicateKeyError(e);
        }
    }

    public T findAndRemove(Bson filter) {
        return delegate.findOneAndDelete(filter);
    }

    public void dropIndexes() {
        delegate.dropIndexes();
    }

    public void dropIndex(Bson keys) {
        delegate.dropIndex(keys);
    }

    public void dropIndex(String name) {
        delegate.dropIndex(name);
    }

    public void drop() {
        delegate.drop();
    }

    public List getIndexInfo() {
        return delegate.listIndexes(DBObject.class).into(new ArrayList<>());
    }

    public DBCollection getDbCollection() {
        return dbCollection;
    }

    record IndexOptionDto(
            @JsonProperty("unique") Optional unique,
            @JsonProperty("name") Optional name,
            @JsonProperty("expireAfterSeconds") Optional expireAfterSeconds,
            @JsonProperty("sparse") Optional sparse,
            @JsonProperty("collation") Optional collationDto) {

        public IndexOptions toIndexOptions() {
            final var io = new IndexOptions();
            unique.ifPresent(io::unique);
            name.ifPresent(io::name);
            expireAfterSeconds.ifPresent(seconds -> io.expireAfter(seconds, TimeUnit.SECONDS));
            sparse.ifPresent(io::sparse);
            collationDto.ifPresent(collation -> io.collation(collation.toCollation()));
            return io;
        }
    }

    record CollationDto(
            @JsonProperty("locale") Optional locale,
            @JsonProperty("strength") Optional strength
    ) {
        public Collation toCollation() {
            final var builder = Collation.builder();
            locale.ifPresent(builder::locale);
            strength.ifPresent(s -> builder.collationStrength(CollationStrength.fromInt(s)));
            return builder.build();
        }
    }

    private IndexOptions toIndexOptions(DBObject options) {
        return objectMapper.convertValue(options.toMap(), IndexOptionDto.class).toIndexOptions();
    }

    private MongoException possiblyAsDuplicateKeyError(final MongoServerException e) {
        if (ErrorCategory.fromErrorCode(e.getCode()) == ErrorCategory.DUPLICATE_KEY) {
            return new DuplicateKeyException(new BsonDocument("err", new BsonString(e.getMessage())),
                    e.getServerAddress(),
                    WriteConcernResult.acknowledged(0, false, null));
        } else {
            return e;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy