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

com.mongodb.DBCursor Maven / Gradle / Ivy

Go to download

The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson

There is a newer version: 3.12.14
Show newest version
/*
 * Copyright 2008-present 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;

import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.internal.MongoBatchCursorAdapter;
import com.mongodb.client.internal.OperationExecutor;
import com.mongodb.client.model.Collation;
import com.mongodb.client.model.DBCollectionCountOptions;
import com.mongodb.client.model.DBCollectionFindOptions;
import com.mongodb.lang.Nullable;
import com.mongodb.operation.FindOperation;
import org.bson.codecs.Decoder;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.mongodb.DBObjects.toDBObject;
import static com.mongodb.assertions.Assertions.notNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * 

An iterator over database results. Doing a {@code find()} query on a collection returns a {@code DBCursor}.

*

An application should ensure that a cursor is closed in all circumstances, e.g. using a try-with-resources statement:

*
 *    try (DBCursor cursor = collection.find(query)) {
 *        while (cursor.hasNext()) {
 *            System.out.println(cursor.next();
 *        }
 *    }
 * 
* *

Warning: Calling {@code toArray} or {@code length} on a DBCursor will irrevocably turn it into an array. This means that, if * the cursor was iterating over ten million results (which it was lazily fetching from the database), suddenly there will be a ten-million * element array in memory. Before converting to an array, make sure that there are a reasonable number of results using {@code skip()} and * {@code limit()}. * *

For example, to get an array of the 1000-1100th elements of a cursor, use

* *
{@code
 *    List obj = collection.find(query).skip(1000).limit(100).toArray();
 * }
* * See {@link Mongo#getDB(String)} for further information about the effective deprecation of this class. * * @mongodb.driver.manual core/read-operations Read Operations */ @NotThreadSafe @SuppressWarnings("deprecation") public class DBCursor implements Cursor, Iterable { private final DBCollection collection; private final DBObject filter; private final DBCollectionFindOptions findOptions; private final OperationExecutor executor; private final boolean retryReads; private int options; private DBDecoderFactory decoderFactory; private Decoder decoder; private IteratorOrArray iteratorOrArray; private DBObject currentObject; private int numSeen; private boolean closed; private final List all = new ArrayList(); private MongoCursor cursor; // This allows us to easily enable/disable finalizer for cleaning up un-closed cursors @SuppressWarnings("UnusedDeclaration")// IDEs will say it can be converted to a local variable, resist the urge private OptionalFinalizer optionalFinalizer; /** * Initializes a new database cursor. * * @param collection collection to use * @param query the query filter to apply * @param fields keys to return from the query * @param readPreference the read preference for this query */ public DBCursor(final DBCollection collection, final DBObject query, @Nullable final DBObject fields, @Nullable final ReadPreference readPreference) { this(collection, query, fields, readPreference, true); } /** * Initializes a new database cursor. * * @param collection collection to use * @param query the query filter to apply * @param fields keys to return from the query * @param readPreference the read preference for this query * @param retryReads true if reads should be retried */ public DBCursor(final DBCollection collection, final DBObject query, @Nullable final DBObject fields, @Nullable final ReadPreference readPreference, final boolean retryReads) { this(collection, query, new DBCollectionFindOptions().projection(fields).readPreference(readPreference), retryReads); addOption(collection.getOptions()); DBObject indexKeys = lookupSuitableHints(query, collection.getHintFields()); if (indexKeys != null) { hint(indexKeys); } } DBCursor(final DBCollection collection, @Nullable final DBObject filter, final DBCollectionFindOptions findOptions) { this(collection, filter, findOptions, true); } DBCursor(final DBCollection collection, @Nullable final DBObject filter, final DBCollectionFindOptions findOptions, final boolean retryReads) { this(collection, filter, findOptions, collection.getExecutor(), collection.getDBDecoderFactory(), collection.getObjectCodec(), retryReads); } private DBCursor(final DBCollection collection, @Nullable final DBObject filter, final DBCollectionFindOptions findOptions, final OperationExecutor executor, final DBDecoderFactory decoderFactory, final Decoder decoder, final boolean retryReads) { this.collection = notNull("collection", collection); this.filter = filter; this.executor = notNull("executor", executor); this.findOptions = notNull("findOptions", findOptions.copy()); this.decoderFactory = decoderFactory; this.decoder = notNull("decoder", decoder); this.retryReads = retryReads; } /** * Creates a copy of an existing database cursor. The new cursor is an iterator, even if the original was an array. * * @return the new cursor */ public DBCursor copy() { return new DBCursor(collection, filter, findOptions, executor, decoderFactory, decoder, retryReads); } /** * Checks if there is another object available. * *

Note: Automatically adds the {@link Bytes#QUERYOPTION_AWAITDATA} option to any cursors with the * {@link Bytes#QUERYOPTION_TAILABLE} option set. For non blocking tailable cursors see {@link #tryNext }.

* * @return true if there is another object available * @mongodb.driver.manual /core/cursors/#cursor-batches Cursor Batches */ @Override public boolean hasNext() { if (closed) { throw new IllegalStateException("Cursor has been closed"); } if (cursor == null) { FindOperation operation = getQueryOperation(decoder); if (operation.getCursorType() == CursorType.Tailable) { operation.cursorType(CursorType.TailableAwait); } initializeCursor(operation); } boolean hasNext = cursor.hasNext(); setServerCursorOnFinalizer(cursor.getServerCursor()); return hasNext; } /** * Returns the object the cursor is at and moves the cursor ahead by one. * *

Note: Automatically adds the {@link Bytes#QUERYOPTION_AWAITDATA} option to any cursors with the * {@link Bytes#QUERYOPTION_TAILABLE} option set. For non blocking tailable cursors see {@link #tryNext }.

* * @return the next element * @mongodb.driver.manual /core/cursors/#cursor-batches Cursor Batches */ @Override public DBObject next() { checkIteratorOrArray(IteratorOrArray.ITERATOR); if (!hasNext()) { throw new NoSuchElementException(); } return nextInternal(); } /** * Non blocking check for tailable cursors to see if another object is available. * *

Returns the object the cursor is at and moves the cursor ahead by one or * return null if no documents is available.

* * @return the next element or null * @throws MongoException if failed * @throws IllegalArgumentException if the cursor is not tailable * @mongodb.driver.manual /core/cursors/#cursor-batches Cursor Batches */ @Nullable public DBObject tryNext() { if (cursor == null) { FindOperation operation = getQueryOperation(decoder); if (!operation.getCursorType().isTailable()) { throw new IllegalArgumentException("Can only be used with a tailable cursor"); } initializeCursor(operation); } DBObject next = cursor.tryNext(); setServerCursorOnFinalizer(cursor.getServerCursor()); return currentObject(next); } /** * Returns the element the cursor is at. * * @return the current element */ public DBObject curr() { return currentObject; } @Override public void remove() { throw new UnsupportedOperationException(); } /** * Adds a query option. See Bytes.QUERYOPTION_* for list. * * @param option the option to be added * @return {@code this} so calls can be chained * @see Bytes * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-query Query Flags * @deprecated Prefer per-option methods, e.g. {@link #cursorType(CursorType)}, {@link #noCursorTimeout(boolean)}, etc. */ @Deprecated public DBCursor addOption(final int option) { setOptions(this.options |= option); return this; } /** * Sets the query option - see Bytes.QUERYOPTION_* for list. * * @param options the bitmask of options * @return {@code this} so calls can be chained * @see Bytes * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-query Query Flags */ @Deprecated public DBCursor setOptions(final int options) { if ((options & Bytes.QUERYOPTION_EXHAUST) != 0) { throw new UnsupportedOperationException("exhaust query option is not supported"); } this.options = options; return this; } /** * Resets the query options. * * @return {@code this} so calls can be chained * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-query Query Flags */ @Deprecated public DBCursor resetOptions() { this.options = 0; return this; } /** * Gets the query options. * * @return the bitmask of options * @mongodb.driver.manual ../meta-driver/latest/legacy/mongodb-wire-protocol/#op-query Query Flags */ @Deprecated public int getOptions() { return options; } /** * Gets the query limit. * * @return the limit, or 0 if no limit is set */ public int getLimit() { return findOptions.getLimit(); } /** * Gets the batch size. * * @return the batch size */ public int getBatchSize() { return findOptions.getBatchSize(); } /** * Adds a special operator like $comment or $returnKey. For example: *
     *    addSpecial("$returnKey", 1)
     *    addSpecial("$comment", "this is a special query)
     * 
* * @param name the name of the special query operator * @param value the value of the special query operator * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator Special Operators * @deprecated Prefer per-operator methods, e.g. {@link #comment(String)}, {@link #explain()}, etc. */ @SuppressWarnings("deprecation") @Deprecated public DBCursor addSpecial(@Nullable final String name, @Nullable final Object value) { if (name == null || value == null) { return this; } if ("$comment".equals(name)) { comment(value.toString()); } else if ("$explain".equals(name)) { findOptions.getModifiers().put("$explain", true); } else if ("$hint".equals(name)) { if (value instanceof String) { hint((String) value); } else { hint((DBObject) value); } } else if ("$maxScan".equals(name)) { maxScan(((Number) value).intValue()); } else if ("$maxTimeMS".equals(name)) { maxTime(((Number) value).longValue(), MILLISECONDS); } else if ("$max".equals(name)) { max((DBObject) value); } else if ("$min".equals(name)) { min((DBObject) value); } else if ("$orderby".equals(name)) { sort((DBObject) value); } else if ("$returnKey".equals(name)) { returnKey(); } else if ("$showDiskLoc".equals(name)) { showDiskLoc(); } else if ("$snapshot".equals(name)) { snapshot(); } else if ("$natural".equals(name)) { sort(new BasicDBObject("$natural", ((Number) value).intValue())); } else { throw new IllegalArgumentException(name + "is not a supported modifier"); } return this; } /** * Adds a comment to the query to identify queries in the database profiler output. * * @param comment the comment that is to appear in the profiler output * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/comment/ $comment * @since 2.12 */ public DBCursor comment(final String comment) { findOptions.comment(comment); return this; } /** * Limits the number of documents a cursor will return for a query. * * @param max the maximum number of documents to return * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/maxScan/ $maxScan * @see #limit(int) * @since 2.12 * @deprecated Deprecated as of MongoDB 4.0 release */ @Deprecated public DBCursor maxScan(final int max) { findOptions.getModifiers().put("$maxScan", max); return this; } /** * Specifies an exclusive upper limit for the index to use in a query. * * @param max a document specifying the fields, and the upper bound values for those fields * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/max/ $max * @since 2.12 */ public DBCursor max(final DBObject max) { findOptions.max(max); return this; } /** * Specifies an inclusive lower limit for the index to use in a query. * * @param min a document specifying the fields, and the lower bound values for those fields * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/min/ $min * @since 2.12 */ public DBCursor min(final DBObject min) { findOptions.min(min); return this; } /** * Forces the cursor to only return fields included in the index. * * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/returnKey/ $returnKey * @since 2.12 */ public DBCursor returnKey() { findOptions.returnKey(true); return this; } /** * Modifies the documents returned to include references to the on-disk location of each document. The location will be returned in a * property named {@code $diskLoc} * * @return {@code this} so calls can be chained * @mongodb.driver.manual reference/operator/meta/showDiskLoc/ $showDiskLoc * @since 2.12 * @deprecated showDiskLoc has been deprecated in the MongoDB server. There is no replacement for it. */ @Deprecated public DBCursor showDiskLoc() { findOptions.getModifiers().put("$showDiskLoc", true); return this; } /** * Informs the database of indexed fields of the collection in order to improve performance. * * @param indexKeys a {@code DBObject} with fields and direction * @return same DBCursor for chaining operations * @mongodb.driver.manual reference/operator/meta/hint/ $hint */ public DBCursor hint(final DBObject indexKeys) { findOptions.hint(indexKeys); return this; } /** * Informs the database of an indexed field of the collection in order to improve performance. * * @param indexName the name of an index * @return same DBCursor for chaining operations * @mongodb.driver.manual reference/operator/meta/hint/ $hint * @deprecated Prefer {@link #hint(DBObject)} */ @Deprecated public DBCursor hint(final String indexName) { findOptions.getModifiers().put("$hint", indexName); return this; } /** * Set the maximum execution time for operations on this cursor. * * @param maxTime the maximum time that the server will allow the query to run, before killing the operation. A non-zero value requires * a server version >= 2.6 * @param timeUnit the time unit * @return same DBCursor for chaining operations * @mongodb.server.release 2.6 * @mongodb.driver.manual reference/operator/meta/maxTimeMS/ $maxTimeMS * @since 2.12.0 */ public DBCursor maxTime(final long maxTime, final TimeUnit timeUnit) { findOptions.maxTime(maxTime, timeUnit); return this; } /** * Use snapshot mode for the query. Snapshot mode prevents the cursor from returning a document more than once because an intervening * write operation results in a move of the document. Even in snapshot mode, documents inserted or deleted during the lifetime of the * cursor may or may not be returned. Currently, snapshot mode may not be used with sorting or explicit hints. * * @return {@code this} so calls can be chained * * @see com.mongodb.DBCursor#sort(DBObject) * @see com.mongodb.DBCursor#hint(DBObject) * @mongodb.driver.manual reference/operator/meta/snapshot/ $snapshot * @deprecated Deprecated in MongoDB 3.6 release and removed in MongoDB 4.0 release */ @Deprecated public DBCursor snapshot() { findOptions.getModifiers().put("$snapshot", true); return this; } /** * Returns an object containing basic information about the execution of the query that created this cursor. This creates a {@code * DBObject} with a number of fields, including but not limited to: *
    *
  • cursor: cursor type
  • *
  • nScanned: number of records examined by the database for this query
  • *
  • n: the number of records that the database returned
  • *
  • millis: how long it took the database to execute the query
  • *
* * @return a {@code DBObject} containing the explain output for this DBCursor's query * @throws MongoException if the operation failed * @mongodb.driver.manual reference/command/explain Explain Output * @deprecated Replace with direct use of the explain command using the runCommand helper method */ @Deprecated public DBObject explain() { return toDBObject(executor.execute(getQueryOperation(collection.getObjectCodec()) .asExplainableOperation(ExplainVerbosity.QUERY_PLANNER), getReadPreference(), getReadConcern())); } /** * Sets the cursor type. * * @param cursorType the cursor type, which may not be null * @return this * @since 3.9 */ public DBCursor cursorType(final CursorType cursorType) { findOptions.cursorType(cursorType); return this; } /** * Users should not set this under normal circumstances. * * @param oplogReplay if oplog replay is enabled * @return this * @since 3.9 */ public DBCursor oplogReplay(final boolean oplogReplay) { findOptions.oplogReplay(oplogReplay); return this; } /** * The server normally times out idle cursors after an inactivity period (10 minutes) * to prevent excess memory use. Set this option to prevent that. * * @param noCursorTimeout true if cursor timeout is disabled * @return this * @since 3.9 */ public DBCursor noCursorTimeout(final boolean noCursorTimeout) { findOptions.noCursorTimeout(noCursorTimeout); return this; } /** * Get partial results from a sharded cluster if one or more shards are unreachable (instead of throwing an error). * * @param partial if partial results for sharded clusters is enabled * @return this * @since 3.9 */ public DBCursor partial(final boolean partial) { findOptions.partial(partial); return this; } @SuppressWarnings("deprecation") private FindOperation getQueryOperation(final Decoder decoder) { FindOperation operation = new FindOperation(collection.getNamespace(), decoder) .filter(collection.wrapAllowNull(filter)) .batchSize(findOptions.getBatchSize()) .skip(findOptions.getSkip()) .limit(findOptions.getLimit()) .maxAwaitTime(findOptions.getMaxAwaitTime(MILLISECONDS), MILLISECONDS) .maxTime(findOptions.getMaxTime(MILLISECONDS), MILLISECONDS) .modifiers(collection.wrapAllowNull(findOptions.getModifiers())) .projection(collection.wrapAllowNull(findOptions.getProjection())) .sort(collection.wrapAllowNull(findOptions.getSort())) .collation(findOptions.getCollation()) .comment(findOptions.getComment()) .hint(collection.wrapAllowNull(findOptions.getHint())) .min(collection.wrapAllowNull(findOptions.getMin())) .max(collection.wrapAllowNull(findOptions.getMax())) .returnKey(findOptions.isReturnKey()) .showRecordId(findOptions.isShowRecordId()) .retryReads(retryReads); if ((this.options & Bytes.QUERYOPTION_TAILABLE) != 0) { if ((this.options & Bytes.QUERYOPTION_AWAITDATA) != 0) { operation.cursorType(CursorType.TailableAwait); } else { operation.cursorType(CursorType.Tailable); } } else { operation.cursorType(findOptions.getCursorType()); } if ((this.options & Bytes.QUERYOPTION_OPLOGREPLAY) != 0) { operation.oplogReplay(true); } else { operation.oplogReplay(findOptions.isOplogReplay()); } if ((this.options & Bytes.QUERYOPTION_NOTIMEOUT) != 0) { operation.noCursorTimeout(true); } else { operation.noCursorTimeout(findOptions.isNoCursorTimeout()); } if ((this.options & Bytes.QUERYOPTION_PARTIAL) != 0) { operation.partial(true); } else { operation.partial(findOptions.isPartial()); } return operation; } /** * Sorts this cursor's elements. This method must be called before getting any object from the cursor. * * @param orderBy the fields by which to sort * @return a cursor pointing to the first element of the sorted results */ public DBCursor sort(final DBObject orderBy) { findOptions.sort(orderBy); return this; } /** * Limits the number of elements returned. Note: parameter {@code limit} should be positive, although a negative value is * supported for legacy reason. Passing a negative value will call {@link DBCursor#batchSize(int)} which is the preferred method. * * @param limit the number of elements to return * @return a cursor to iterate the results * @mongodb.driver.manual reference/method/cursor.limit Limit */ public DBCursor limit(final int limit) { findOptions.limit(limit); return this; } /** *

Limits the number of elements returned in one batch. A cursor typically fetches a batch of result objects and store them * locally.

* *

If {@code batchSize} is positive, it represents the size of each batch of objects retrieved. It can be adjusted to optimize * performance and limit data transfer.

* *

If {@code batchSize} is negative, it will limit of number objects returned, that fit within the max batch size limit (usually * 4MB), and cursor will be closed. For example if {@code batchSize} is -10, then the server will return a maximum of 10 documents and * as many as can fit in 4MB, then close the cursor. Note that this feature is different from limit() in that documents must fit within * a maximum size, and it removes the need to send a request to close the cursor server-side.

* * @param numberOfElements the number of elements to return in a batch * @return {@code this} so calls can be chained */ public DBCursor batchSize(final int numberOfElements) { findOptions.batchSize(numberOfElements); return this; } /** * Discards a given number of elements at the beginning of the cursor. * * @param numberOfElements the number of elements to skip * @return a cursor pointing to the new first element of the results * @throws IllegalStateException if the cursor has started to be iterated through */ public DBCursor skip(final int numberOfElements) { findOptions.skip(numberOfElements); return this; } @Override public long getCursorId() { if (cursor != null) { ServerCursor serverCursor = cursor.getServerCursor(); if (serverCursor == null) { return 0; } return serverCursor.getId(); } else { return 0; } } /** * Returns the number of objects through which the cursor has iterated. * * @return the number of objects seen */ public int numSeen() { return numSeen; } @Override public void close() { closed = true; if (cursor != null) { cursor.close(); cursor = null; setServerCursorOnFinalizer(null); } currentObject = null; } /** * Declare that this query can run on a secondary server. * * @return a copy of the same cursor (for chaining) * @see ReadPreference#secondaryPreferred() * @deprecated Replaced with {@link com.mongodb.ReadPreference#secondaryPreferred()} */ @Deprecated public DBCursor slaveOk() { return addOption(Bytes.QUERYOPTION_SLAVEOK); } /** *

Creates a copy of this cursor object that can be iterated. Note: - you can iterate the DBCursor itself without calling this method * - no actual data is getting copied.

* *

Note that use of this method does not let you call close the underlying cursor in the case of either an exception or an early * break. The preferred method of iteration is to use DBCursor as an Iterator, so that you can call close() on it in a finally * block.

* * @return an iterator */ @Override public Iterator iterator() { return this.copy(); } /** * Converts this cursor to an array. * * @return an array of elements * @throws MongoException if failed */ public List toArray() { return toArray(Integer.MAX_VALUE); } /** * Converts this cursor to an array. * * @param max the maximum number of objects to return * @return an array of objects * @throws MongoException if failed */ public List toArray(final int max) { checkIteratorOrArray(IteratorOrArray.ARRAY); fillArray(max - 1); return all; } /** * Counts the number of objects matching the query. This does not take limit/skip into consideration, and does initiate a call to the * server. * * @return the number of objects * @throws MongoException if the operation failed * @see DBCursor#size */ public int count() { DBCollectionCountOptions countOptions = getDbCollectionCountOptions(); return (int) collection.getCount(getQuery(), countOptions); } /** * Returns the first document that matches the query. * * @return the first matching document * @since 2.12 */ @Nullable public DBObject one() { DBCursor findOneCursor = copy().limit(-1); try { return findOneCursor.hasNext() ? findOneCursor.next() : null; } finally { findOneCursor.close(); } } /** * Pulls back all items into an array and returns the number of objects. Note: this can be resource intensive. * * @return the number of elements in the array * @throws MongoException if failed * @see #count() * @see #size() */ public int length() { checkIteratorOrArray(IteratorOrArray.ARRAY); fillArray(Integer.MAX_VALUE); return all.size(); } /** * For testing only! Iterates cursor and counts objects * * @return num objects * @throws MongoException if failed * @see #count() */ public int itcount() { int n = 0; while (this.hasNext()) { this.next(); n++; } return n; } /** * Counts the number of objects matching the query this does take limit/skip into consideration * * @return the number of objects * @throws MongoException if the operation failed * @see #count() */ public int size() { DBCollectionCountOptions countOptions = getDbCollectionCountOptions().skip(findOptions.getSkip()).limit(findOptions.getLimit()); return (int) collection.getCount(getQuery(), countOptions); } /** * Gets the fields to be returned. * * @return the field selector that cursor used */ @Nullable public DBObject getKeysWanted() { return findOptions.getProjection(); } /** * Gets the query. * * @return the query that cursor used */ public DBObject getQuery() { return filter; } /** * Gets the collection. * * @return the collection that data is pulled from */ public DBCollection getCollection() { return collection; } @Override @Nullable public ServerAddress getServerAddress() { if (cursor != null) { return cursor.getServerAddress(); } else { return null; } } /** * Sets the read preference for this cursor. See the documentation for {@link ReadPreference} for more information. * * @param readPreference read preference to use * @return {@code this} so calls can be chained */ public DBCursor setReadPreference(final ReadPreference readPreference) { findOptions.readPreference(readPreference); return this; } /** * Gets the default read preference. * * @return the readPreference used by this cursor */ public ReadPreference getReadPreference() { ReadPreference readPreference = findOptions.getReadPreference(); if (readPreference != null) { return readPreference; } return collection.getReadPreference(); } /** * Sets the read concern for this collection. * * @param readConcern the read concern to use for this collection * @since 3.2 * @mongodb.server.release 3.2 * @mongodb.driver.manual reference/readConcern/ Read Concern */ DBCursor setReadConcern(@Nullable final ReadConcern readConcern) { findOptions.readConcern(readConcern); return this; } /** * Get the read concern for this collection. * * @return the {@link com.mongodb.ReadConcern} * @since 3.2 * @mongodb.server.release 3.2 * @mongodb.driver.manual reference/readConcern/ Read Concern */ ReadConcern getReadConcern() { ReadConcern readConcern = findOptions.getReadConcern(); if (readConcern != null) { return readConcern; } return collection.getReadConcern(); } /** * Returns the collation options * * @return the collation options * @since 3.4 * @mongodb.server.release 3.4 */ @Nullable public Collation getCollation() { return findOptions.getCollation(); } /** * Sets the collation options * *

A null value represents the server default.

* @param collation the collation options to use * @return this * @since 3.4 * @mongodb.server.release 3.4 */ public DBCursor setCollation(@Nullable final Collation collation) { findOptions.collation(collation); return this; } /** * Sets the factory that will be used create a {@code DBDecoder} that will be used to decode BSON documents into DBObject instances. * * @param factory the DBDecoderFactory * @return {@code this} so calls can be chained */ public DBCursor setDecoderFactory(final DBDecoderFactory factory) { this.decoderFactory = factory; //Not creating new CompoundDBObjectCodec because we don't care about encoder. this.decoder = new DBDecoderAdapter(factory.create(), collection, getCollection().getBufferPool()); return this; } /** * Gets the decoder factory that creates the decoder this cursor will use to decode objects from MongoDB. * * @return the decoder factory. */ public DBDecoderFactory getDecoderFactory() { return decoderFactory; } @Override public String toString() { return "DBCursor{" + "collection=" + collection + ", find=" + findOptions + (cursor != null ? (", cursor=" + cursor.getServerCursor()) : "") + '}'; } private void initializeCursor(final FindOperation operation) { cursor = new MongoBatchCursorAdapter(executor.execute(operation, getReadPreferenceForCursor(), getReadConcern())); if (isCursorFinalizerEnabled() && cursor.getServerCursor() != null) { optionalFinalizer = new OptionalFinalizer(collection.getDB().getMongo(), collection.getNamespace()); } } private boolean isCursorFinalizerEnabled() { return collection.getDB().getMongo().getMongoClientOptions().isCursorFinalizerEnabled(); } private void setServerCursorOnFinalizer(@Nullable final ServerCursor serverCursor) { if (optionalFinalizer != null) { optionalFinalizer.setServerCursor(serverCursor); } } private void checkIteratorOrArray(final IteratorOrArray expected) { if (iteratorOrArray == null) { iteratorOrArray = expected; return; } if (expected == iteratorOrArray) { return; } throw new IllegalArgumentException("Can't switch cursor access methods"); } private ReadPreference getReadPreferenceForCursor() { ReadPreference readPreference = getReadPreference(); if ((options & Bytes.QUERYOPTION_SLAVEOK) != 0 && !readPreference.isSlaveOk()) { readPreference = ReadPreference.secondaryPreferred(); } return readPreference; } private void fillArray(final int n) { checkIteratorOrArray(IteratorOrArray.ARRAY); while (n >= all.size() && hasNext()) { all.add(nextInternal()); } } private DBObject nextInternal() { if (iteratorOrArray == null) { checkIteratorOrArray(IteratorOrArray.ITERATOR); } DBObject next = cursor.next(); setServerCursorOnFinalizer(cursor.getServerCursor()); return currentObjectNonNull(next); } @Nullable private DBObject currentObject(@Nullable final DBObject newCurrentObject){ if (newCurrentObject != null) { currentObject = newCurrentObject; numSeen++; DBObject projection = findOptions.getProjection(); if (projection != null && !(projection.keySet().isEmpty())) { currentObject.markAsPartialObject(); } } return newCurrentObject; } private DBObject currentObjectNonNull(final DBObject newCurrentObject){ currentObject = newCurrentObject; numSeen++; DBObject projection = findOptions.getProjection(); if (projection != null && !(projection.keySet().isEmpty())) { currentObject.markAsPartialObject(); } return newCurrentObject; } @Nullable private static DBObject lookupSuitableHints(final DBObject query, @Nullable final List hints) { if (hints == null) { return null; } Set keys = query.keySet(); for (final DBObject hint : hints) { if (keys.containsAll(hint.keySet())) { return hint; } } return null; } private enum IteratorOrArray { ITERATOR, ARRAY } private static class OptionalFinalizer { private final Mongo mongo; private final MongoNamespace namespace; private volatile ServerCursor serverCursor; private OptionalFinalizer(final Mongo mongo, final MongoNamespace namespace) { this.namespace = notNull("namespace", namespace); this.mongo = notNull("mongo", mongo); } private void setServerCursor(@Nullable final ServerCursor serverCursor) { this.serverCursor = serverCursor; } @Override @SuppressWarnings("deprecation") protected void finalize() { if (serverCursor != null) { mongo.addOrphanedCursor(serverCursor, namespace); } } } private DBCollectionCountOptions getDbCollectionCountOptions() { DBCollectionCountOptions countOptions = new DBCollectionCountOptions() .readPreference(getReadPreferenceForCursor()) .readConcern(getReadConcern()) .collation(getCollation()) .maxTime(findOptions.getMaxTime(MILLISECONDS), MILLISECONDS); Object hint = findOptions.getHint() != null ? findOptions.getHint() : findOptions.getModifiers().get("$hint"); if (hint != null) { if (hint instanceof String) { countOptions.hintString((String) hint); } else { countOptions.hint((DBObject) hint); } } return countOptions; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy