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

com.mongodb.async.client.MongoCollectionImpl Maven / Gradle / Ivy

There is a newer version: 3.12.14
Show newest version
/*
 * Copyright (c) 2008-2015 MongoDB, Inc.
 *
 * 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
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.mongodb.async.client;

import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoNamespace;
import com.mongodb.MongoWriteConcernException;
import com.mongodb.MongoWriteException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.WriteError;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.bulk.DeleteRequest;
import com.mongodb.bulk.IndexRequest;
import com.mongodb.bulk.InsertRequest;
import com.mongodb.bulk.UpdateRequest;
import com.mongodb.bulk.WriteRequest;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.DeleteManyModel;
import com.mongodb.client.model.DeleteOneModel;
import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.FindOptions;
import com.mongodb.client.model.IndexModel;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.RenameCollectionOptions;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateManyModel;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.operation.AsyncOperationExecutor;
import com.mongodb.operation.CountOperation;
import com.mongodb.operation.CreateIndexesOperation;
import com.mongodb.operation.DropCollectionOperation;
import com.mongodb.operation.DropIndexOperation;
import com.mongodb.operation.FindAndDeleteOperation;
import com.mongodb.operation.FindAndReplaceOperation;
import com.mongodb.operation.FindAndUpdateOperation;
import com.mongodb.operation.MixedBulkWriteOperation;
import com.mongodb.operation.RenameCollectionOperation;
import org.bson.BsonDocument;
import org.bson.BsonDocumentWrapper;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

class MongoCollectionImpl implements MongoCollection {
    private final MongoNamespace namespace;
    private final Class documentClass;
    private final ReadPreference readPreference;
    private final CodecRegistry codecRegistry;
    private final WriteConcern writeConcern;
    private final AsyncOperationExecutor executor;

    MongoCollectionImpl(final MongoNamespace namespace, final Class documentClass, final CodecRegistry codecRegistry,
                        final ReadPreference readPreference, final WriteConcern writeConcern, final AsyncOperationExecutor executor) {
        this.namespace = notNull("namespace", namespace);
        this.documentClass = notNull("documentClass", documentClass);
        this.codecRegistry = notNull("codecRegistry", codecRegistry);
        this.readPreference = notNull("readPreference", readPreference);
        this.writeConcern = notNull("writeConcern", writeConcern);
        this.executor = notNull("executor", executor);
    }

    @Override
    public MongoNamespace getNamespace() {
        return namespace;
    }

    @Override
    public Class getDocumentClass() {
        return documentClass;
    }

    @Override
    public CodecRegistry getCodecRegistry() {
        return codecRegistry;
    }

    @Override
    public ReadPreference getReadPreference() {
        return readPreference;
    }

    @Override
    public WriteConcern getWriteConcern() {
        return writeConcern;
    }

    @Override
    public  MongoCollection withDocumentClass(final Class newDocumentClass) {
        return new MongoCollectionImpl(namespace, newDocumentClass, codecRegistry, readPreference, writeConcern, executor);
    }

    @Override
    public MongoCollection withCodecRegistry(final CodecRegistry codecRegistry) {
        return new MongoCollectionImpl(namespace, documentClass, codecRegistry, readPreference, writeConcern, executor);
    }

    @Override
    public MongoCollection withReadPreference(final ReadPreference readPreference) {
        return new MongoCollectionImpl(namespace, documentClass, codecRegistry, readPreference, writeConcern, executor);
    }

    @Override
    public MongoCollection withWriteConcern(final WriteConcern writeConcern) {
        return new MongoCollectionImpl(namespace, documentClass, codecRegistry, readPreference, writeConcern, executor);
    }

    @Override
    public void count(final SingleResultCallback callback) {
        count(new BsonDocument(), new CountOptions(), callback);
    }

    @Override
    public void count(final Bson filter, final SingleResultCallback callback) {
        count(filter, new CountOptions(), callback);
    }

    @Override
    public void count(final Bson filter, final CountOptions options, final SingleResultCallback callback) {
        CountOperation operation = new CountOperation(namespace)
                                   .filter(toBsonDocument(filter))
                                   .skip(options.getSkip())
                                   .limit(options.getLimit())
                                   .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS);
        if (options.getHint() != null) {
            operation.hint(toBsonDocument(options.getHint()));
        } else if (options.getHintString() != null) {
            operation.hint(new BsonString(options.getHintString()));
        }
        executor.execute(operation, readPreference, callback);
    }

    @Override
    public  DistinctIterable distinct(final String fieldName, final Class resultClass) {
        return new DistinctIterableImpl(namespace, documentClass, resultClass, codecRegistry, readPreference, executor,
                                                            fieldName);
    }

    @Override
    public FindIterable find() {
        return find(new BsonDocument(), documentClass);
    }

    @Override
    public  FindIterable find(final Class resultClass) {
        return find(new BsonDocument(), resultClass);
    }

    @Override
    public FindIterable find(final Bson filter) {
        return find(filter, documentClass);
    }

    @Override
    public  FindIterable find(final Bson filter, final Class resultClass) {
        return new FindIterableImpl(namespace, documentClass, resultClass, codecRegistry, readPreference, executor,
                                                        filter, new FindOptions());
    }

    @Override
    public AggregateIterable aggregate(final List pipeline) {
        return aggregate(pipeline, documentClass);
    }

    @Override
    public  AggregateIterable aggregate(final List pipeline, final Class resultClass) {
        return new AggregateIterableImpl(namespace, documentClass, resultClass, codecRegistry, readPreference, executor,
                                                             pipeline);
    }

    @Override
    public MapReduceIterable mapReduce(final String mapFunction, final String reduceFunction) {
        return mapReduce(mapFunction, reduceFunction, documentClass);
    }

    @Override
    public  MapReduceIterable mapReduce(final String mapFunction, final String reduceFunction,
                                                          final Class resultClass) {
        return new MapReduceIterableImpl(namespace, documentClass, resultClass, codecRegistry, readPreference, executor,
                                                             mapFunction, reduceFunction);
    }

    @Override
    public void bulkWrite(final List> requests,
                          final SingleResultCallback callback) {
        bulkWrite(requests, new BulkWriteOptions(), callback);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void bulkWrite(final List> requests, final BulkWriteOptions options,
                          final SingleResultCallback callback) {
        List writeRequests = new ArrayList(requests.size());
        for (WriteModel writeModel : requests) {
            WriteRequest writeRequest;
            if (writeModel instanceof InsertOneModel) {
                TDocument document = ((InsertOneModel) writeModel).getDocument();
                if (getCodec() instanceof CollectibleCodec) {
                    ((CollectibleCodec) getCodec()).generateIdIfAbsentFromDocument(document);
                }
                writeRequest = new InsertRequest(documentToBsonDocument(document));
            } else if (writeModel instanceof ReplaceOneModel) {
                ReplaceOneModel replaceOneModel = (ReplaceOneModel) writeModel;
                writeRequest = new UpdateRequest(toBsonDocument(replaceOneModel.getFilter()), documentToBsonDocument(replaceOneModel
                                                                                                                     .getReplacement()),
                                                 WriteRequest.Type.REPLACE)
                               .upsert(replaceOneModel.getOptions().isUpsert());
            } else if (writeModel instanceof UpdateOneModel) {
                UpdateOneModel updateOneModel = (UpdateOneModel) writeModel;
                writeRequest = new UpdateRequest(toBsonDocument(updateOneModel.getFilter()), toBsonDocument(updateOneModel.getUpdate()),
                                                 WriteRequest.Type.UPDATE)
                               .multi(false)
                               .upsert(updateOneModel.getOptions().isUpsert());
            } else if (writeModel instanceof UpdateManyModel) {
                UpdateManyModel updateManyModel = (UpdateManyModel) writeModel;
                writeRequest = new UpdateRequest(toBsonDocument(updateManyModel.getFilter()), toBsonDocument(updateManyModel.getUpdate()),
                                                 WriteRequest.Type.UPDATE)
                               .multi(true)
                               .upsert(updateManyModel.getOptions().isUpsert());
            } else if (writeModel instanceof DeleteOneModel) {
                DeleteOneModel deleteOneModel = (DeleteOneModel) writeModel;
                writeRequest = new DeleteRequest(toBsonDocument(deleteOneModel.getFilter())).multi(false);
            } else if (writeModel instanceof DeleteManyModel) {
                DeleteManyModel deleteManyModel = (DeleteManyModel) writeModel;
                writeRequest = new DeleteRequest(toBsonDocument(deleteManyModel.getFilter())).multi(true);
            } else {
                throw new UnsupportedOperationException(format("WriteModel of type %s is not supported", writeModel.getClass()));
            }

            writeRequests.add(writeRequest);
        }

        executor.execute(new MixedBulkWriteOperation(namespace, writeRequests, options.isOrdered(), writeConcern), callback);
    }

    @Override
    public void insertOne(final TDocument document, final SingleResultCallback callback) {
        TDocument insertDocument = document;
        if (getCodec() instanceof CollectibleCodec) {
            ((CollectibleCodec) getCodec()).generateIdIfAbsentFromDocument(insertDocument);
        }
        executeSingleWriteRequest(new InsertRequest(documentToBsonDocument(insertDocument)), new SingleResultCallback() {
            @Override
            public void onResult(final BulkWriteResult result, final Throwable t) {
                callback.onResult(null, t);
            }
        });
    }

    @Override
    public void insertMany(final List documents, final SingleResultCallback callback) {
        insertMany(documents, new InsertManyOptions(), callback);
    }

    @Override
    public void insertMany(final List documents, final InsertManyOptions options,
                           final SingleResultCallback callback) {
        List requests = new ArrayList(documents.size());
        for (TDocument document : documents) {
            if (getCodec() instanceof CollectibleCodec) {
                document = ((CollectibleCodec) getCodec()).generateIdIfAbsentFromDocument(document);
            }
            requests.add(new InsertRequest(documentToBsonDocument(document)));
        }
        executor.execute(new MixedBulkWriteOperation(namespace, requests, options.isOrdered(), writeConcern),
                         errorHandlingCallback(new SingleResultCallback() {
                             @Override
                             public void onResult(final BulkWriteResult result, final Throwable t) {
                                 callback.onResult(null, t);
                             }
                         }));
    }

    @Override
    public void deleteOne(final Bson filter, final SingleResultCallback callback) {
        delete(filter, false, callback);
    }

    @Override
    public void deleteMany(final Bson filter, final SingleResultCallback callback) {
        delete(filter, true, callback);
    }

    @Override
    public void replaceOne(final Bson filter, final TDocument replacement, final SingleResultCallback callback) {
        replaceOne(filter, replacement, new UpdateOptions(), callback);
    }

    @Override
    public void replaceOne(final Bson filter, final TDocument replacement, final UpdateOptions options,
                           final SingleResultCallback callback) {
        executeSingleWriteRequest(new UpdateRequest(toBsonDocument(filter), documentToBsonDocument(replacement), WriteRequest.Type.REPLACE)
                                  .upsert(options.isUpsert()),
                                  new SingleResultCallback() {
                                      @Override
                                      public void onResult(final BulkWriteResult result, final Throwable t) {
                                          if (t != null) {
                                              callback.onResult(null, t);
                                          } else {
                                              callback.onResult(toUpdateResult(result), null);
                                          }
                                      }
                                  });
    }

    @Override
    public void updateOne(final Bson filter, final Bson update, final SingleResultCallback callback) {
        updateOne(filter, update, new UpdateOptions(), callback);
    }

    @Override
    public void updateOne(final Bson filter, final Bson update, final UpdateOptions options,
                          final SingleResultCallback callback) {
        update(filter, update, options, false, callback);
    }

    @Override
    public void updateMany(final Bson filter, final Bson update, final SingleResultCallback callback) {
        updateMany(filter, update, new UpdateOptions(), callback);
    }

    @Override
    public void updateMany(final Bson filter, final Bson update, final UpdateOptions options,
                           final SingleResultCallback callback) {
        update(filter, update, options, true, callback);
    }

    @Override
    public void findOneAndDelete(final Bson filter, final SingleResultCallback callback) {
        findOneAndDelete(filter, new FindOneAndDeleteOptions(), callback);
    }

    @Override
    public void findOneAndDelete(final Bson filter, final FindOneAndDeleteOptions options, final SingleResultCallback callback) {
        executor.execute(new FindAndDeleteOperation(namespace, getCodec())
                         .filter(toBsonDocument(filter))
                         .projection(toBsonDocument(options.getProjection()))
                         .sort(toBsonDocument(options.getSort()))
                         .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS), callback);
    }

    @Override
    public void findOneAndReplace(final Bson filter, final TDocument replacement, final SingleResultCallback callback) {
        findOneAndReplace(filter, replacement, new FindOneAndReplaceOptions(), callback);
    }

    @Override
    public void findOneAndReplace(final Bson filter, final TDocument replacement, final FindOneAndReplaceOptions options,
                                  final SingleResultCallback callback) {
        executor.execute(new FindAndReplaceOperation(namespace, getCodec(), documentToBsonDocument(replacement))
                         .filter(toBsonDocument(filter))
                         .projection(toBsonDocument(options.getProjection()))
                         .sort(toBsonDocument(options.getSort()))
                         .returnOriginal(options.getReturnDocument() == ReturnDocument.BEFORE)
                         .upsert(options.isUpsert())
                         .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS), callback);
    }

    @Override
    public void findOneAndUpdate(final Bson filter, final Bson update, final SingleResultCallback callback) {
        findOneAndUpdate(filter, update, new FindOneAndUpdateOptions(), callback);
    }

    @Override
    public void findOneAndUpdate(final Bson filter, final Bson update, final FindOneAndUpdateOptions options,
                                 final SingleResultCallback callback) {
        executor.execute(new FindAndUpdateOperation(namespace, getCodec(), toBsonDocument(update))
                         .filter(toBsonDocument(filter))
                         .projection(toBsonDocument(options.getProjection()))
                         .sort(toBsonDocument(options.getSort()))
                         .returnOriginal(options.getReturnDocument() == ReturnDocument.BEFORE)
                         .upsert(options.isUpsert())
                         .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS), callback);
    }

    @Override
    public void drop(final SingleResultCallback callback) {
        executor.execute(new DropCollectionOperation(namespace), callback);
    }

    @Override
    public void createIndex(final Bson key, final SingleResultCallback callback) {
        createIndex(key, new IndexOptions(), callback);
    }

    @Override
    public void createIndex(final Bson key, final IndexOptions indexOptions, final SingleResultCallback callback) {
        createIndexes(singletonList(new IndexModel(key, indexOptions)), new SingleResultCallback>() {
            @Override
            public void onResult(final List result, final Throwable t) {
                if (t != null) {
                    callback.onResult(null, t);
                } else {
                    callback.onResult(result.get(0), null);
                }
            }
        });
    }

    @Override
    public void createIndexes(final List indexes, final SingleResultCallback> callback) {
        notNull("indexes", indexes);

        List indexRequests = new ArrayList(indexes.size());
        for (IndexModel model : indexes) {
            indexRequests.add(new IndexRequest(toBsonDocument(model.getKeys()))
                              .name(model.getOptions().getName())
                              .background(model.getOptions().isBackground())
                              .unique(model.getOptions().isUnique())
                              .sparse(model.getOptions().isSparse())
                              .expireAfter(model.getOptions().getExpireAfter(TimeUnit.SECONDS), TimeUnit.SECONDS)
                              .version(model.getOptions().getVersion())
                              .weights(toBsonDocument(model.getOptions().getWeights()))
                              .defaultLanguage(model.getOptions().getDefaultLanguage())
                              .languageOverride(model.getOptions().getLanguageOverride())
                              .textVersion(model.getOptions().getTextVersion())
                              .sphereVersion(model.getOptions().getSphereVersion())
                              .bits(model.getOptions().getBits())
                              .min(model.getOptions().getMin())
                              .max(model.getOptions().getMax())
                              .bucketSize(model.getOptions().getBucketSize())
                              .storageEngine(toBsonDocument(model.getOptions().getStorageEngine())));
        }
        final CreateIndexesOperation createIndexesOperation = new CreateIndexesOperation(getNamespace(), indexRequests);
        executor.execute(createIndexesOperation, new SingleResultCallback() {
            @Override
            public void onResult(final Void result, final Throwable t) {
                if (t != null) {
                    callback.onResult(null, t);
                } else {
                    callback.onResult(createIndexesOperation.getIndexNames(), null);
                }
            }
        });
    }

    @Override
    public ListIndexesIterable listIndexes() {
        return listIndexes(Document.class);
    }

    @Override
    public  ListIndexesIterable listIndexes(final Class resultClass) {
        return new ListIndexesIterableImpl(namespace, resultClass, codecRegistry, readPreference, executor);
    }

    @Override
    public void dropIndex(final String indexName, final SingleResultCallback callback) {
        executor.execute(new DropIndexOperation(namespace, indexName), callback);
    }

    @Override
    public void dropIndex(final Bson keys, final SingleResultCallback callback) {
        executor.execute(new DropIndexOperation(namespace, keys.toBsonDocument(BsonDocument.class, codecRegistry)), callback);
    }

    @Override
    public void dropIndexes(final SingleResultCallback callback) {
        dropIndex("*", callback);
    }

    @Override
    public void renameCollection(final MongoNamespace newCollectionNamespace, final SingleResultCallback callback) {
        renameCollection(newCollectionNamespace, new RenameCollectionOptions(), callback);
    }

    @Override
    public void renameCollection(final MongoNamespace newCollectionNamespace, final RenameCollectionOptions options,
                                 final SingleResultCallback callback) {
        executor.execute(new RenameCollectionOperation(getNamespace(), newCollectionNamespace)
                         .dropTarget(options.isDropTarget()), callback);
    }

    private void delete(final Bson filter, final boolean multi, final SingleResultCallback callback) {
        executeSingleWriteRequest(new DeleteRequest(toBsonDocument(filter)).multi(multi), new SingleResultCallback() {
            @Override
            public void onResult(final BulkWriteResult result, final Throwable t) {
                if (t != null) {
                    callback.onResult(null, t);
                } else {
                    if (result.wasAcknowledged()) {
                        callback.onResult(DeleteResult.acknowledged(result.getDeletedCount()), null);
                    } else {
                        callback.onResult(DeleteResult.unacknowledged(), null);
                    }

                }
            }
        });
    }

    private void update(final Bson filter, final Bson update, final UpdateOptions updateOptions, final boolean multi,
                        final SingleResultCallback callback) {
        executeSingleWriteRequest(new UpdateRequest(toBsonDocument(filter), toBsonDocument(update), WriteRequest.Type.UPDATE)
                                  .upsert(updateOptions.isUpsert()).multi(multi), new SingleResultCallback() {
            @Override
            public void onResult(final BulkWriteResult result, final Throwable t) {
                if (t != null) {
                    callback.onResult(null, t);
                } else {
                    callback.onResult(toUpdateResult(result), null);
                }
            }
        });
    }

    private void executeSingleWriteRequest(final WriteRequest request, final SingleResultCallback callback) {
        executor.execute(new MixedBulkWriteOperation(namespace, asList(request), true, writeConcern),
                         new SingleResultCallback() {
                             @Override
                             public void onResult(final BulkWriteResult result, final Throwable t) {
                                 if (t instanceof MongoBulkWriteException) {
                                     MongoBulkWriteException e = (MongoBulkWriteException) t;
                                     if (e.getWriteErrors().isEmpty()) {
                                         callback.onResult(null, new MongoWriteConcernException(e.getWriteConcernError(),
                                                                                                e.getServerAddress()));
                                     } else {
                                         callback.onResult(null, new MongoWriteException(new WriteError(e.getWriteErrors().get(0)),
                                                                                         e.getServerAddress()));
                                     }
                                 } else {
                                     callback.onResult(result, t);
                                 }
                             }
                         });
    }

    private UpdateResult toUpdateResult(final com.mongodb.bulk.BulkWriteResult result) {
        if (result.wasAcknowledged()) {
            Long modifiedCount = result.isModifiedCountAvailable() ? (long) result.getModifiedCount() : null;
            BsonValue upsertedId = result.getUpserts().isEmpty() ? null : result.getUpserts().get(0).getId();
            return UpdateResult.acknowledged(result.getMatchedCount(), modifiedCount, upsertedId);
        } else {
            return UpdateResult.unacknowledged();
        }
    }

    private Codec getCodec() {
        return getCodec(documentClass);
    }

    private  Codec getCodec(final Class resultClass) {
        return codecRegistry.get(resultClass);
    }

    private BsonDocument documentToBsonDocument(final TDocument document) {
        return BsonDocumentWrapper.asBsonDocument(document, codecRegistry);
    }

    private BsonDocument toBsonDocument(final Bson document) {
        return document == null ? null : document.toBsonDocument(documentClass, codecRegistry);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy