org.mongojack.JacksonDBCollection Maven / Gradle / Ivy
/*
* 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