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

asbah-core_2.10.3.1.1.source-code.MongoCursor.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2010 MongoDB, Inc. 
 * Copyright (c) 2009, 2010 Novus Partners, 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.
 *
 * For questions and comments about this product, please see the project page at:
 *
 * http://github.com/mongodb/casbah
 *
 */

package com.mongodb.casbah

import com.mongodb.DBCursor
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.Logging

import scala.concurrent.duration.Duration

// scalastyle:off number.of.methods

/**
 * Scala wrapper for Mongo DBCursors,
 * including ones which return custom DBObject subclasses.
 *
 * This is a rewrite of the Casbah 1.0 approach which was rather
 * naive and unnecessarily complex.... formerly was MongoCursorWrapper
 *
 * @version 2.0, 12/23/10
 * @since 2.0
 *
 * @tparam T (DBObject or subclass thereof)
 */
trait MongoCursorBase extends Logging {

  type T <: DBObject

  val underlying: DBCursor

  /**
   * next
   *
   * Iterator increment.
   *
   * TODO: The cast to T should be examined for sanity/safety.
   *
   * {@inheritDoc}
   *
   * @return The next element in the cursor
   */
  def next(): T = underlying.next.asInstanceOf[T]

  /**
   * hasNext
   *
   * Is there another element in the cursor?
   *
   * {@inheritDoc}
   *
   * @return (Boolean Next)
   */
  def hasNext: Boolean = underlying.hasNext

  /**
   * sort
   *
   * Sort this cursor's elements
   *
   * @param  orderBy (A) The fields on which to sort
   * @tparam A  A view of DBObject to sort by
   * @return A cursor pointing to the first element of the sorted results
   */
  def sort[A <% DBObject](orderBy: A): this.type = {
    // The Java code returns a copy of itself (via _this_) so no clone/_newInstance
    underlying.sort(orderBy)
    this
  }

  /**
   * count
   *
   * The DBCursor's count of elements in the query, passed through
   * from the Java object.  Note that Scala Iterator[_] base trait
   * has a count method which tests predicates and you should
   * be careful about using that.
   *
   * NOTE: count() ignores any skip/limit settings on a cursor;
   * it is the count of the ENTIRE query results.
   * If you want to get a count post-skip/limit
   * you must use size()
   *
   * @see size()
   *
   * @return Int indicating the number of elements returned by the query
   * @throws MongoException()
   */
  def count(): Int = underlying.count() /* calls the db */

  /**
   * Manipulate Query Options
   *
   * Adds an option - see Bytes.QUERYOPTION_* for list
   * TODO - Create Scala version of Bytes.QUERYOPTION_*
   *
   * @see com.mongodb.Mongo
   * @see com.mongodb.Bytes
   */
  def option_=(option: Int): Unit = underlying.addOption(option)

  /**
   * Manipulate Query Options
   *
   * Gets current option settings - see Bytes.QUERYOPTION_* for list
   *
   * @see com.mongodb.Mongo
   * @see com.mongodb.Bytes
   */
  def option: Int = underlying.getOptions

  /**
   * Manipulate Query Options
   *
   * Resets options to default.
   *
   * @see com.mongodb.Mongo
   * @see com.mongodb.Bytes
   */
  def resetOptions(): DBCursor = underlying.resetOptions() // use parens because this side-effects

  /**
   * Manipulate Query Options
   *
   * Gets current option settings - see Bytes.QUERYOPTION_* for list
   *
   * @see com.mongodb.Mongo
   * @see com.mongodb.Bytes
   */
  def options: Int = underlying.getOptions

  /**
   * Manipulate Query Options
   *
   * Sets current option settings - see Bytes.QUERYOPTION_* for list
   *
   * @see com.mongodb.Mongo
   * @see com.mongodb.Bytes
   */
  def options_=(opts: Int): Unit = underlying.setOptions(opts)

  /**
   * hint
   *
   * Provide the Database a hint of which indexed fields of a collection to use
   * to improve performance.
   *
   * @param  indexKeys (A) A DBObject of the index names as keys
   * @tparam A A view of DBObject to use for the indexKeys
   * @return the same DBCursor, useful for chaining operations
   */
  def hint[A <% DBObject](indexKeys: A): this.type = {
    underlying.hint(indexKeys)
    this
  }

  /**
   * hint
   *
   * Provide the Database a hint of an indexed field of a collection to use
   * to improve performance.
   *
   * @param  indexName (String) The name of an index
   * @return the same DBCursor, useful for chaining operations
   */
  def hint(indexName: String): this.type = {
    underlying.hint(indexName)
    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 the same DBCursor, useful for chaining operations
   */
  def snapshot(): this.type = {
    // The Java code returns a copy of itself (via _this_) so no clone/_newInstance
    underlying.snapshot() // parens for side-effecting
    this
  }

  /**
   * explain
   *
   * Returns an object containing basic information about the execution
   * of the query that created this cursor.
   * This creates an instance of CursorExplanation which is a custom
   * dbObject with the key/value pairs:
   * - cursor = Cursor Type
   * - nscanned = Number of items examined by Mongo for this query
   * - nscannedObjects = Number of objects examined by Mongo
   * - n = the number of records which Mongo returned
   * - millis = how long it took Mongo to execute the query
   * - nYields = number of times this query yielded the read lock to let writes in
   * - indexBounds = the index boundaries used.
   *
   * CursorExplanation provides utility methods to access these fields.
   *
   * @see http://dochub.mongodb.org/core/explain
   * @return an instance of CursorExplanation
   */
  def explain: CursorExplanation = new CursorExplanation(underlying.explain)

  /**
   * limit
   *
   * Limits the number of elements returned.
   *
   * NOTE: Specifying a negative number instructs
   * the server to retrun that number of items and close the cursor.
   * It will only return what can fit into a single 4MB response
   *
   * @param  n (Int)  The number of elements to return
   * @return A cursor pointing to the first element of the limited results
   *
   * @see http://dochub.mongodb.org/core/limit
   */
  def limit(n: Int): this.type = {
    underlying.limit(n)
    this
  }

  /**
   * skip
   *
   * Discards a given number of elements at the beginning of the cursor.
   *
   * @param  n (Int)  The number of elements to skip
   * @return A cursor pointing to the first element of the results
   *
   * @see http://dochub.mongodb.org/core/skip
   */
  def skip(n: Int): this.type = {
    underlying.skip(n)
    this
  }

  /**
   * cursorId
   *
   * @return A long representing the cursorID on the server; 0 = no cursor
   *
   */
  def cursorId: Long = underlying.getCursorId

  /**
   * close
   *
   * Kill the current cursor on the server
   */
  def close(): Unit = underlying.close() // parens for side-effect

  /**
   * slaveOk
   *
   * Makes this query OK to run on a non-master node.
   */
  @deprecated("Replaced with `ReadPreference.SECONDARY`", "2.3.0")
  @SuppressWarnings(Array("deprecation"))
  def slaveOk(): DBCursor = underlying.slaveOk() // parens for side-effect

  /**
   * numSeen
   *
   * Returns the number of objects through which this cursor has iterated,
   * as tracked by the java driver.
   *
   * @return The number of objects seen
   */
  def numSeen: Int = underlying.numSeen

  /**
   * batchSize
   *
   * Limits the number of elements returned in one batch.
   *
   * @param  n (Int) The number of elements to return in a batch
   * @return the same DBCursor, useful for chaining operations
   */
  def batchSize(n: Int): MongoCursorBase = {
    underlying.batchSize(n)
    this
  }

  def keysWanted: DBObject = underlying.getKeysWanted

  def query: DBObject = underlying.getQuery

  /**
   * "Special" Operators for cursors
   *
   * adds a special operator like \$maxScan or \$returnKey
   * @see http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-Specialoperators
   *      { @inheritDoc}
   * @return the same DBCursor, useful for chaining operations
   */
  def addSpecial(name: String, o: Any): this.type = {
    // The Java code returns a copy of itself (via _this_) so no clone/_newInstance
    underlying.addSpecial(name, o.asInstanceOf[AnyRef])
    this
  }

  // scalastyle:off method.name
  /**
   * \$returnKey
   *
   * Sets a special operator of \$returnKey
   * If true, returns ONLY the index key.
   * Defaults to true if you just call \$returnKey
   *
   * @param  bool (Boolean = true)
   * @return the same DBCursor, useful for chaining operations
   */
  def $returnKey(bool: Boolean = true): this.type = addSpecial("$returnKey", bool)

  /**
   * \$maxScan
   *
   * Sets a special operator of \$maxScan
   * Which defines the max number of items to scan.
   *
   * @param  max (A)
   * @tparam A : Numeric
   * @return the same DBCursor, useful for chaining operations
   */
  def $maxScan[A: Numeric](max: T): this.type = addSpecial("$maxScan", max)

  /**
   * \$query
   *
   * Sets a special operator of \$query
   * Which defines the query for this cursor.
   *
   * This is the same as running find() on a Collection with the query.
   *
   * @param  q (DBObject)
   * @return the same DBCursor, useful for chaining operations
   */
  def $query[A <% DBObject](q: A): this.type = addSpecial("$query", q)

  /**
   * \$orderby
   *
   * Sets a special operator of \$orderby
   * which defines the sort spec for this cursor.
   *
   * This is the same as calling sort on the cursor.
   *
   * @param  obj (DBObject)
   * @return the same DBCursor, useful for chaining operations
   */
  def $orderby[A <% DBObject](obj: A): this.type = addSpecial("$orderby", obj)

  /**
   * \$explain
   *
   * Sets a special operator of \$explain
   * which, if true, explains the query instead of returning results.
   *
   * This is the same as calling the explain() method on the cursor.
   *
   * @param  bool (Boolean = true)
   * @return the same DBCursor, useful for chaining operations
   */
  def $explain(bool: Boolean = true): this.type = addSpecial("$explain", bool)

  /**
   * \$snapshot
   *
   * Sets a special operator of \$snapshot
   * which, if True, sets snapshot mode on the query.
   *
   * This is the same as calling the snapshot() method on the cursor.
   *
   * @param  bool (Boolean = true)
   * @return the same DBCursor, useful for chaining operations
   */
  def $snapshot(bool: Boolean = true): this.type = addSpecial("$snapshot", bool)

  /**
   * \$min
   *
   * Sets minimum index bounds - commonly paired with \$max
   *
   * @param  obj (DBObject)
   * @see http://www.mongodb.org/display/DOCS/min+and+max+Query+Specifiers
   *
   * @return the same DBCursor, useful for chaining operations
   */
  def $min[A <% DBObject](obj: A): this.type = addSpecial("$min", obj)

  /**
   * \$max
   *
   * Sets maximum index bounds - commonly paired with \$max
   *
   * @param  obj (DBObject)
   * @see http://www.mongodb.org/display/DOCS/max+and+max+Query+Specifiers
   *
   * @return the same DBCursor, useful for chaining operations
   */
  def $max[A <% DBObject](obj: A): this.type = addSpecial("$max", obj)

  /**
   * \$showDiskLoc
   *
   * Sets a special operator \$showDiskLoc which, if true,
   * shows the disk location of results.
   *
   * @param  bool (Boolean = true)
   * @return the same DBCursor, useful for chaining operations
   */
  def $showDiskLoc(bool: Boolean = true): this.type = addSpecial("$showDiskLoc", bool)

  /**
   * \$hint
   *
   * Sets a special operator \$hint which
   * forces the query to use a given index.
   *
   * This is the same as calling hint() on the cursor.
   *
   * @param obj (DBObject)
   * @return the same DBCursor, useful for chaining operations
   */
  def $hint[A <% DBObject](obj: A): this.type = addSpecial("$hint", obj)

  /**
   * _newInstance
   *
   * Utility method which concrete subclasses
   * are expected to implement for creating a new
   * instance of THIS concrete implementation from a
   * Java cursor.  Good with cursor calls that return a new cursor.
   *
   * @param  cursor (DBCursor)
   * @return (this.type)
   */
  def _newInstance(cursor: DBCursor): MongoCursorBase

  // scalastyle:on method.name

  /**
   * copy
   *
   * Creates a new copy of an existing database cursor.
   * The new cursor is an iterator even if the original
   * was an array.
   *
   * @return The new cursor
   */
  def copy(): MongoCursorBase = _newInstance(underlying.copy()) // parens for side-effects

}

/**
 * Concrete cursor implementation expecting standard DBObject operation
 * This is the version of MongoCursorBase you should expect to use in most cases.
 *
 * @version 2.0, 12/23/10
 * @since 1.0
 *
 * @param  underlying (com.mongodb.DBCursor)
 * @tparam DBObject
 */
class MongoCursor(val underlying: DBCursor) extends MongoCursorBase with Iterator[DBObject] {

  type T = DBObject

  /**
   * size
   *
   * The DBCursor's count of elements in the query,
   * AFTER the application of skip/limit, passed through
   * from the Java object.
   *
   * NOTE: size() takes into account any skip/limit settings on a cursor;
   * it is the size of just the window.
   * If you want to get a count of the entire query ignoring skip/limit
   * you must use count()
   *
   * @see count()
   *
   * @return Int indicating the number of elements returned by the query after skip/limit
   * @throws MongoException if errors
   */
  override def size: Int = underlying.size

  // scalastyle:off method.name
  /**
   * _newInstance
   *
   * Utility method which concrete subclasses
   * are expected to implement for creating a new
   * instance of THIS concrete implementation from a
   * Java cursor.  Good with cursor calls that return a new cursor.
   *
   * @param  cursor (DBCursor)
   * @return (this.type)
   */
  def _newInstance(cursor: DBCursor): MongoCursor = new MongoCursor(cursor)
  // scalastyle:off method.name

  /**
   * copy
   *
   * Creates a new copy of an existing database cursor.
   * The new cursor is an iterator even if the original
   * was an array.
   *
   * @return The new cursor
   */
  override def copy(): MongoCursor = _newInstance(underlying.copy()) // parens for side-effects

  /**
   * 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
   * @return the new cursor
   *
   * @since 2.7
   */
  def maxTime(maxTime: Duration): MongoCursor = _newInstance(underlying.maxTime(maxTime.length, maxTime.unit))

  /**
   * @return the first matching document
   *
   * @since 2.7
   */
  def one(): DBObject = underlying.one()
}

object MongoCursor extends Logging {
  /**
   * Initialize a new cursor with your own custom settings
   *
   * @param  collection (MongoCollection)  collection to use
   * @param  query (Q) Query to perform
   * @param  keys (K) Keys to return from the query
   * @return (instance) A new MongoCursor
   */
  def apply[T <: DBObject: Manifest](collection: MongoCollectionBase, query: DBObject, keys: DBObject): MongoCursorBase =
    apply(collection, query, keys, collection.readPreference)

  /**
   * Initialize a new cursor with your own custom settings
   *
   * @param  collection (MongoCollection)  collection to use
   * @param  query (Q) Query to perform
   * @param  keys (K) Keys to return from the query
   * @param  readPref the read preference for the cursor
   * @return (instance) A new MongoCursor
   */
  def apply[T <: DBObject: Manifest](collection: MongoCollectionBase, query: DBObject, keys: DBObject, readPref: ReadPreference): MongoCursorBase = {
    val cursor = new DBCursor(collection.underlying, query, keys, readPref)

    if (manifest[T] == manifest[DBObject]) new MongoCursor(cursor)
    else new MongoGenericTypedCursor[T](cursor)
  }
}

/**
 * Concrete cursor implementation for typed Cursor operations via Collection.setObjectClass
 * This is a special case cursor for typed operations.
 *
 * @version 2.0, 12/23/10
 * @since 1.0
 *
 * @param  underlying (com.mongodb.DBCollection)
 * @tparam A A Subclass of DBObject
 */
class MongoGenericTypedCursor[A <: DBObject](val underlying: DBCursor) extends MongoCursorBase {
  type T = A

  // scalastyle:off method.name
  /**
   * _newInstance
   *
   * Utility method which concrete subclasses
   * are expected to implement for creating a new
   * instance of THIS concrete implementation from a
   * Java cursor.  Good with cursor calls that return a new cursor.
   *
   * @param  cursor (DBCursor)
   * @return (this.type)
   */
  def _newInstance(cursor: DBCursor): MongoGenericTypedCursor[T] = new MongoGenericTypedCursor[T](cursor)
  // scalastyle:on method.name

  /**
   * copy
   *
   * Creates a new copy of an existing database cursor.
   * The new cursor is an iterator even if the original
   * was an array.
   *
   * @return The new cursor
   */
  override def copy(): MongoGenericTypedCursor[T] = _newInstance(underlying.copy()) // parens for side-effects
}

/**
 * A wrapper for the new com.mongodb.Cursor - which is used when returning a
 * cursor from the aggregation framework.
 *
 * @version 2.7
 *
 * @param  underlying (com.mongodb.Cursor)
 * @tparam T DBObject
 */
case class Cursor(underlying: com.mongodb.Cursor) extends Iterator[DBObject] {

  type T = DBObject

  def hasNext: Boolean = underlying.hasNext

  def next(): DBObject = underlying.next()
}

/**
 *
 *
 * @version 1.0, 12/15/10
 * @since 2.0
 *
 * @param  underlying (DBObject)
 * @see http://dochub.mongodb.org/core/explain
 */
sealed class CursorExplanation(override val underlying: DBObject) extends MongoDBObject {

  /**
   * cursor
   *
   * The cursor type for the query
   */
  def cursor: Option[String] = getAs[String]("cursor")

  /**
   * nscanned
   *
   * Number of items examined by Mongo for this query.
   * Items could be objects or index keys---if a "covered" index
   * is involved, nscanned may be higher than nscannedObjects
   *
   * @return a Long value indicating 'nscanned'
   */
  def nscanned: Option[Long] = getAs[Long]("nscanned")

  /**
   * nscannedObjects
   *
   * @return a Long value of # objects examined by Mongo for this query.
   */
  def nscannedObjects: Option[Long] = getAs[Long]("nscannedObjects")

  /**
   * nYields
   *
   * @return A long value of the number of times the query yielded the read lock to let writes in
   */
  def nYields: Option[Long] = getAs[Long]("nYields")

  /**
   * n
   *
   * @return a Long value of the number of objects which Mongo returned
   */
  def n: Option[Long] = getAs[Long]("n")

  /**
   * millis
   *
   * @return The number of milliseconds the query took to execute
   */
  def millis: Option[Long] = getAs[Long]("millis")

  /**
   * indexBounds
   *
   * @return the index boundaries this query used.
   */
  def indexBounds: Option[MongoDBList] = getAs[MongoDBList]("indexBounds")

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy