com.mongodb.DBCursor Maven / Gradle / Ivy
Show all versions of mongo-java-driver Show documentation
/*
* 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;
import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.FindOptions;
import com.mongodb.operation.FindOperation;
import com.mongodb.operation.OperationExecutor;
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} thus
*
* DBCursor cursor = collection.find(query);
* if(cursor.hasNext()) {
* DBObject obj = 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
public class DBCursor implements Cursor, Iterable {
private final DBCollection collection;
private final OperationExecutor executor;
private final DBObject filter;
private final DBObject modifiers;
private DBObject projection;
private DBObject sort;
private final FindOptions findOptions;
private int options;
private ReadPreference readPreference;
private Decoder resultDecoder;
private DBDecoderFactory decoderFactory;
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, final DBObject fields, final ReadPreference readPreference) {
this(collection, collection.getExecutor(), query, new BasicDBObject(), fields, null, new FindOptions(), readPreference);
addOption(collection.getOptions());
DBObject indexKeys = lookupSuitableHints(query, collection.getHintFields());
if (indexKeys != null) {
hint(indexKeys);
}
}
private DBCursor(final DBCollection collection, final OperationExecutor executor, final DBObject filter,
final DBObject modifiers, final DBObject fields, final DBObject sort,
final FindOptions findOptions,
final ReadPreference readPreference) {
if (collection == null) {
throw new IllegalArgumentException("Collection can't be null");
}
this.collection = collection;
this.executor = executor;
this.filter = filter;
this.modifiers = modifiers;
this.projection = fields;
this.sort = sort;
this.findOptions = findOptions;
this.readPreference = readPreference;
this.resultDecoder = collection.getObjectCodec();
this.decoderFactory = collection.getDBDecoderFactory();
}
/**
* 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, executor, filter, modifiers, projection, sort, new FindOptions(findOptions), readPreference);
}
/**
* 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(findOptions, resultDecoder);
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
*/
public DBObject tryNext() {
if (cursor == null) {
FindOperation operation = getQueryOperation(findOptions, resultDecoder);
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
*/
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
*/
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
*/
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
*/
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 $maxScan or $returnKey. For example:
*
* addSpecial("$returnKey", 1)
* addSpecial("$maxScan", 100)
*
*
* @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
*/
public DBCursor addSpecial(final String name, final Object value) {
if (name == null || value == null) {
return this;
}
if ("$comment".equals(name)) {
comment(value.toString());
} else if ("$explain".equals(name)) {
modifiers.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) {
modifiers.put("$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
*/
public DBCursor maxScan(final int max) {
modifiers.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) {
modifiers.put("$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) {
modifiers.put("$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() {
modifiers.put("$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
*/
public DBCursor showDiskLoc() {
modifiers.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) {
modifiers.put("$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
*/
public DBCursor hint(final String indexName) {
modifiers.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
*/
public DBCursor snapshot() {
modifiers.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/explain Explain Output
*/
public DBObject explain() {
return toDBObject(executor.execute(getQueryOperation(findOptions, collection.getObjectCodec())
.asExplainableOperation(ExplainVerbosity.QUERY_PLANNER),
getReadPreference()));
}
private FindOperation getQueryOperation(final FindOptions options, final Decoder decoder) {
FindOperation operation = new FindOperation(collection.getNamespace(), decoder)
.filter(collection.wrapAllowNull(filter))
.batchSize(options.getBatchSize())
.skip(options.getSkip())
.limit(options.getLimit())
.maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS)
.modifiers(collection.wrap(modifiers))
.projection(collection.wrapAllowNull(projection))
.sort(collection.wrapAllowNull(sort))
.noCursorTimeout(options.isNoCursorTimeout())
.oplogReplay(options.isOplogReplay())
.partial(options.isPartial());
if ((this.options & Bytes.QUERYOPTION_TAILABLE) != 0) {
if ((this.options & Bytes.QUERYOPTION_AWAITDATA) != 0) {
operation.cursorType(CursorType.TailableAwait);
} else {
operation.cursorType(CursorType.Tailable);
}
}
if ((this.options & Bytes.QUERYOPTION_OPLOGREPLAY) != 0) {
operation.oplogReplay(true);
}
if ((this.options & Bytes.QUERYOPTION_NOTIMEOUT) != 0) {
operation.noCursorTimeout(true);
}
if ((this.options & Bytes.QUERYOPTION_PARTIAL) != 0) {
operation.partial(true);
}
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) {
this.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 && cursor.getServerCursor() != null) {
return cursor.getServerCursor().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() {
return (int) collection.getCount(getQuery(), getKeysWanted(), 0, 0, getReadPreferenceForCursor(),
findOptions.getMaxTime(MILLISECONDS), MILLISECONDS,
collection.wrap(modifiers).get("$hint"));
}
/**
* Returns the first document that matches the query.
*
* @return the first matching document
* @since 2.12
*/
public DBObject one() {
return collection.findOne(getQuery(), getKeysWanted(), sort,
getReadPreferenceForCursor(), findOptions.getMaxTime(MILLISECONDS), MILLISECONDS);
}
/**
* 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() {
return (int) collection.getCount(getQuery(), getKeysWanted(), findOptions.getLimit(),
findOptions.getSkip(), getReadPreference(),
findOptions.getMaxTime(MILLISECONDS), MILLISECONDS);
}
/**
* Gets the fields to be returned.
*
* @return the field selector that cursor used
*/
public DBObject getKeysWanted() {
return projection;
}
/**
* 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
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) {
this.readPreference = readPreference;
return this;
}
/**
* Gets the default read preference.
*
* @return the readPreference used by this cursor
*/
public ReadPreference getReadPreference() {
return readPreference;
}
/**
* 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.resultDecoder = 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()));
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(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 currentObject(next);
}
private DBObject currentObject(final DBObject newCurrentObject){
if (newCurrentObject != null) {
currentObject = newCurrentObject;
numSeen++;
if (projection != null && !(projection.keySet().isEmpty())) {
currentObject.markAsPartialObject();
}
}
return newCurrentObject;
}
private static DBObject lookupSuitableHints(final DBObject query, 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 static 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(final ServerCursor serverCursor) {
this.serverCursor = serverCursor;
}
@Override
protected void finalize() {
if (serverCursor != null) {
mongo.addOrphanedCursor(serverCursor, namespace);
}
}
}
}