PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/casbah-core/src/main/scala/MongoCollection.scala

http://github.com/mongodb/casbah
Scala | 1208 lines | 284 code | 131 blank | 793 comment | 2 complexity | 996497af0d46472aec207baa8f024322 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * Copyright (c) 2010 MongoDB, Inc. <http://mongodb.com>
  3. * Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. * For questions and comments about this product, please see the project page at:
  18. *
  19. * http://github.com/mongodb/casbah
  20. *
  21. */
  22. // scalastyle:off file.size.limit number.of.methods
  23. package com.mongodb.casbah
  24. import scala.language.reflectiveCalls
  25. import scala.collection.JavaConverters._
  26. import scala.collection.mutable
  27. import scala.concurrent.duration.{ Duration, MILLISECONDS }
  28. import com.mongodb.casbah.Imports._
  29. import com.mongodb.casbah.TypeImports.{ DBEncoder, WriteResult }
  30. import com.mongodb.casbah.commons.Logging
  31. import com.mongodb.casbah.commons.TypeImports.DBObject
  32. import com.mongodb.casbah.map_reduce.{ MapReduceCommand, MapReduceResult }
  33. import com.mongodb.{ ParallelScanOptions => JavaParallelScanOptions, _ }
  34. /**
  35. * Scala wrapper for Mongo DBCollections,
  36. * including ones which return custom DBObject subclasses
  37. * via setObjectClass and the like.
  38. * Provides any non-parameterized methods and the basic structure.
  39. * Requires an underlying object of a DBCollection.
  40. *
  41. * This is a rewrite of the Casbah 1.0 approach which was rather
  42. * naive and unecessarily complex.... formerly was MongoCollectionWrapper
  43. *
  44. * @version 2.0, 12/23/10
  45. * @since 1.0
  46. */
  47. trait MongoCollectionBase extends Logging {
  48. self =>
  49. type T <: DBObject
  50. type CursorType
  51. /**
  52. * The underlying Java Mongo Driver Collection object we proxy.
  53. */
  54. def underlying: DBCollection
  55. /**
  56. * If defined, load the customDecoderFactory.
  57. */
  58. customDecoderFactory.foreach {
  59. decoder =>
  60. log.debug("Loading Custom DBDecoderFactory '%s' into collection (%s)", decoder, this)
  61. underlying.setDBDecoderFactory(decoder)
  62. }
  63. def customDecoderFactory: Option[DBDecoderFactory] = None
  64. def customEncoderFactory: Option[DBEncoderFactory] = Option(underlying.getDBEncoderFactory)
  65. def iterator: CursorType = find()
  66. /**
  67. * Returns the database this collection is a member of.
  68. * @return this collection's database
  69. */
  70. implicit val db: MongoDB = underlying.getDB.asScala
  71. /**
  72. * Forces creation of an index on a set of fields, if one does not already exist.
  73. * @param keys an object with a key set of the fields desired for the index
  74. */
  75. def createIndex[A <% DBObject](keys: A): Unit = underlying.createIndex(keys)
  76. /**
  77. * Creates an index on the field specified, if that index does not already exist.
  78. *
  79. * @param keys a document that contains pairs with the name of the field or fields to index and order of the index
  80. * @param options a document that controls the creation of the index.
  81. */
  82. def createIndex[A <% DBObject, B <% DBObject](keys: A, options: B): Unit =
  83. underlying.createIndex(keys, options)
  84. /**
  85. * Forces creation of an ascending index on a field with the default options.
  86. *
  87. * @param name name of field to index on
  88. * @throws MongoException if the operation failed
  89. */
  90. def createIndex(name: String): Unit = underlying.createIndex(name)
  91. /**
  92. * Forces creation of an index on a set of fields, if one does not already exist.
  93. *
  94. * @param keys a document that contains pairs with the name of the field or fields to index and order of the index
  95. * @param name an identifier for the index. If null or empty, the default name will be used.
  96. * @throws MongoException if the operation failed
  97. */
  98. def createIndex[A <% DBObject](keys: A, name: String): Unit = underlying.createIndex(keys, name)
  99. /**
  100. * Forces creation of an index on a set of fields, if one does not already exist.
  101. *
  102. * @param keys a document that contains pairs with the name of the field or fields to index and order of the index
  103. * @param name an identifier for the index. If null or empty, the default name will be used.
  104. * @param unique if the index should be unique
  105. * @throws MongoException if the operation failed
  106. */
  107. def createIndex[A <% DBObject](keys: A, name: String, unique: Boolean): Unit =
  108. underlying.createIndex(keys, name, unique)
  109. /**
  110. * Find distinct values for a key
  111. *
  112. * @param key the key to find the distinct values for
  113. * @param query the query (optional)
  114. * @param readPrefs the [[com.mongodb.ReadPreference]] for the operation.
  115. * @tparam A The DBObject type
  116. * @return
  117. */
  118. def distinct[A <% DBObject](key: String, query: A = MongoDBObject.empty, readPrefs: ReadPreference = getReadPreference): mutable.Buffer[_] =
  119. underlying.distinct(key, query, readPrefs).asScala
  120. /**
  121. * Drops (deletes) this collection
  122. */
  123. def drop(): Unit = underlying.drop()
  124. /**
  125. * Drops (deletes) this collection
  126. */
  127. def dropCollection(): Unit = underlying.drop()
  128. def dropIndex[A <% DBObject](keys: A): Unit = underlying.dropIndex(keys)
  129. def dropIndex(name: String): Unit = underlying.dropIndex(name)
  130. /**
  131. * Drops all indices from this collection
  132. */
  133. def dropIndexes(): Unit = underlying.dropIndexes()
  134. def dropIndexes(name: String): Unit = underlying.dropIndexes()
  135. /**
  136. * Queries for all objects in this collection.
  137. * @return a cursor which will iterate over every object
  138. */
  139. def find(): CursorType = _newCursor(underlying.find)
  140. /**
  141. * Queries for an object in this collection.
  142. * @param ref object for which to search
  143. * @return an iterator over the results
  144. */
  145. def find[A <% DBObject](ref: A): CursorType = _newCursor(underlying.find(ref))
  146. /**
  147. * Queries for an object in this collection.
  148. *
  149. * <p>
  150. * An empty DBObject will match every document in the collection.
  151. * Regardless of fields specified, the _id fields are always returned.
  152. * </p>
  153. * <p>
  154. * An example that returns the "x" and "_id" fields for every document
  155. * in the collection that has an "x" field:
  156. * </p>
  157. * <blockquote><pre>
  158. * BasicDBObject keys = new BasicDBObject();
  159. * keys.put("x", 1);
  160. *
  161. * DBCursor cursor = collection.find(new BasicDBObject(), keys);
  162. * </pre></blockquote>
  163. *
  164. * @param ref object for which to search
  165. * @param keys fields to return
  166. * @return a cursor to iterate over results
  167. */
  168. def find[A <% DBObject, B <% DBObject](ref: A, keys: B): CursorType = _newCursor(underlying.find(ref, keys))
  169. /**
  170. * Finds an object.
  171. * @param ref query used to search
  172. * @param fields the fields of matching objects to return
  173. * @param numToSkip will not return the first <tt>numToSkip</tt> matches
  174. * @param batchSize if positive, is the # of objects per batch sent back from the db. All objects that match will
  175. * be returned. if batchSize < 0, its a hard limit, and only 1 batch will either batchSize or
  176. * the # that fit in a batch
  177. * @return the objects, if found
  178. */
  179. @deprecated("Use `find().skip().batchSize()`.", "2.7")
  180. def find[A <% DBObject, B <% DBObject](ref: A, fields: B, numToSkip: Int, batchSize: Int): CursorType =
  181. _newCursor(underlying.find(ref, fields).skip(numToSkip).batchSize(batchSize))
  182. /**
  183. * Returns a single object from this collection.
  184. * @return (Option[T]) Some() of the object found, or <code>None</code> if this collection is empty
  185. */
  186. def findOne(): Option[T] = _typedValue(underlying.findOne())
  187. /**
  188. * Returns a single object from this collection matching the query.
  189. *
  190. * @param o the query object
  191. * @param fields (optional) fields to return
  192. * @param orderBy (optional) a document whose fields specify the attributes on which to sort the result set.
  193. * @param readPrefs (optional)
  194. * @param maxTime (optional) the maximum duration that the server will allow this operation to execute before killing it
  195. *
  196. * @return (Option[T]) Some() of the object found, or <code>None</code> if no such object exists
  197. */
  198. def findOne[A <% DBObject, B <% DBObject, C <% DBObject](
  199. o: A = MongoDBObject.empty,
  200. fields: B = MongoDBObject.empty,
  201. orderBy: C = MongoDBObject.empty,
  202. readPrefs: ReadPreference = getReadPreference,
  203. maxTime: Duration = Duration(0, MILLISECONDS)
  204. ): Option[T] = {
  205. val document = underlying.find(o, fields)
  206. .sort(orderBy)
  207. .setReadPreference(readPrefs)
  208. .maxTime(maxTime.length, maxTime.unit)
  209. .one()
  210. _typedValue(document)
  211. }
  212. /**
  213. * Find an object by its ID.
  214. * Finds an object by its id. This compares the passed in
  215. * value to the _id field of the document.
  216. *
  217. * Returns a single object from this collection matching the query.
  218. * @param id the id to match
  219. * @return (Option[T]) Some() of the object found, or <code>None</code> if no such object exists
  220. */
  221. def findOneByID(id: AnyRef): Option[T] = _typedValue(underlying.findOne(id))
  222. /**
  223. * Find an object by its ID.
  224. * Finds an object by its id. This compares the passed in
  225. * value to the _id field of the document.
  226. *
  227. * Returns a single object from this collection matching the query.
  228. *
  229. * @param id the id to match
  230. * @param fields fields to return
  231. * @return (Option[T]) Some() of the object found, or <code>None</code> if no such object exists
  232. */
  233. def findOneByID[B <% DBObject](id: AnyRef, fields: B): Option[T] =
  234. _typedValue(underlying.findOne(id, fields))
  235. /**
  236. * Finds the first document in the query (sorted) and updates it.
  237. * If remove is specified it will be removed. If new is specified then the updated
  238. * document will be returned, otherwise the old document is returned (or it would be lost forever).
  239. * You can also specify the fields to return in the document, optionally.
  240. *
  241. * @param query query to match
  242. * @param update update to apply
  243. *
  244. * @return (Option[T]) of the the found document (before, or after the update)
  245. */
  246. def findAndModify[A <% DBObject, B <% DBObject](query: A, update: B): Option[T] =
  247. _typedValue(underlying.findAndModify(query, update))
  248. /**
  249. * Finds the first document in the query (sorted) and updates it.
  250. *
  251. * @param query query to match
  252. * @param sort sort to apply before picking first document
  253. * @param update update to apply
  254. *
  255. * @return the old document
  256. */
  257. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject](query: A, sort: B, update: C): Option[T] =
  258. _typedValue(underlying.findAndModify(query, sort, update))
  259. /**
  260. * Finds the first document in the query and updates it.
  261. *
  262. * @param query query to match
  263. * @param fields fields to be returned
  264. * @param sort sort to apply before picking first document
  265. * @param remove if true, document found will be removed
  266. * @param update update to apply
  267. * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
  268. * @param upsert do upsert (insert if document not present)
  269. *
  270. * @return the document as it was before the modifications, unless `returnNew` is true, in which case it returns the document
  271. * after the changes were made
  272. */
  273. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  274. remove: Boolean, update: D,
  275. returnNew: Boolean, upsert: Boolean): Option[T] =
  276. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert))
  277. /**
  278. * Atomically modify and return a single document. By default, the returned document does not include the modifications made on the
  279. * update.
  280. *
  281. * @param query specifies the selection criteria for the modification
  282. * @param fields a subset of fields to return
  283. * @param sort determines which document the operation will modify if the query selects multiple documents
  284. * @param remove when true, removes the selected document
  285. * @param returnNew when true, returns the modified document rather than the original
  286. * @param update the modifications to apply
  287. * @param upsert when true, operation creates a new document if the query returns no documents
  288. * @param writeConcern the write concern to apply to this operation
  289. * @return the document as it was before the modifications, unless `returnNew` is true, in which case it returns the document
  290. * after the changes were made
  291. * @throws WriteConcernException if the write failed due some other failure specific to the update command
  292. * @throws MongoException if the operation failed for some other reason
  293. * @since 3.1.0
  294. */
  295. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  296. remove: Boolean, update: D,
  297. returnNew: Boolean, upsert: Boolean,
  298. writeConcern: WriteConcern): Option[T] =
  299. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert, writeConcern))
  300. /**
  301. * Finds the first document in the query and updates it.
  302. *
  303. * @param query query to match
  304. * @param fields fields to be returned
  305. * @param sort sort to apply before picking first document
  306. * @param remove if true, document found will be removed
  307. * @param update update to apply
  308. * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
  309. * @param upsert do upsert (insert if document not present)
  310. * @param maxTime the maximum duration that the server will allow this operation to execute before killing it
  311. *
  312. * @return the document as it was before the modifications, unless `returnNew` is true, in which case it returns the document
  313. * after the changes were made
  314. */
  315. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  316. remove: Boolean, update: D,
  317. returnNew: Boolean, upsert: Boolean,
  318. maxTime: Duration): Option[T] =
  319. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert,
  320. maxTime.length, maxTime.unit))
  321. /**
  322. * Finds the first document in the query and updates it.
  323. *
  324. * @param query query to match
  325. * @param fields fields to be returned
  326. * @param sort sort to apply before picking first document
  327. * @param remove if true, document found will be removed
  328. * @param update update to apply
  329. * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
  330. * @param upsert do upsert (insert if document not present)
  331. * @param maxTime the maximum duration that the server will allow this operation to execute before killing it
  332. * @param writeConcern the write concern to apply to this operation
  333. * @throws WriteConcernException if the write failed due some other failure specific to the update command
  334. * @throws MongoException if the operation failed for some other reason
  335. * @since 3.1.0
  336. */
  337. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  338. remove: Boolean, update: D,
  339. returnNew: Boolean, upsert: Boolean,
  340. maxTime: Duration,
  341. writeConcern: WriteConcern): Option[T] =
  342. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert,
  343. maxTime.length, maxTime.unit, writeConcern))
  344. /**
  345. * Finds the first document in the query and updates it.
  346. *
  347. * @note bypassDocumentValidation requires MongoDB 3.2 or greater
  348. * @param query query to match
  349. * @param fields fields to be returned
  350. * @param sort sort to apply before picking first document
  351. * @param remove if true, document found will be removed
  352. * @param update update to apply
  353. * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
  354. * @param upsert do upsert (insert if document not present)
  355. * @param bypassDocumentValidation whether to bypass document validation.
  356. * @param maxTime the maximum duration that the server will allow this operation to execute before killing it
  357. * @throws WriteConcernException if the write failed due some other failure specific to the update command
  358. * @throws MongoException if the operation failed for some other reason
  359. * @since 3.1.0
  360. */
  361. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  362. remove: Boolean, update: D,
  363. returnNew: Boolean, upsert: Boolean,
  364. bypassDocumentValidation: Boolean,
  365. maxTime: Duration): Option[T] =
  366. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert, bypassDocumentValidation,
  367. maxTime.length, maxTime.unit))
  368. /**
  369. * Finds the first document in the query and updates it.
  370. *
  371. * @param query query to match
  372. * @param fields fields to be returned
  373. * @param sort sort to apply before picking first document
  374. * @param remove if true, document found will be removed
  375. * @param update update to apply
  376. * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever)
  377. * @param upsert do upsert (insert if document not present)
  378. * @param bypassDocumentValidation whether to bypass document validation.
  379. * @param maxTime the maximum duration that the server will allow this operation to execute before killing it
  380. * @param writeConcern the write concern to apply to this operation
  381. * @throws WriteConcernException if the write failed due some other failure specific to the update command
  382. * @throws MongoException if the operation failed for some other reason
  383. * @since 3.1.0
  384. */
  385. def findAndModify[A <% DBObject, B <% DBObject, C <% DBObject, D <% DBObject](query: A, fields: B, sort: C,
  386. remove: Boolean, update: D,
  387. returnNew: Boolean, upsert: Boolean,
  388. bypassDocumentValidation: Boolean,
  389. maxTime: Duration,
  390. writeConcern: WriteConcern): Option[T] =
  391. _typedValue(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert, bypassDocumentValidation,
  392. maxTime.length, maxTime.unit, writeConcern))
  393. /**
  394. * Finds the first document in the query and removes it.
  395. * @return the removed document
  396. */
  397. def findAndRemove[A <% DBObject](query: A): Option[T] =
  398. _typedValue(underlying.findAndRemove(query))
  399. /**
  400. * Find a collection that is prefixed with this collection's name.
  401. * A typical use of this might be
  402. * <blockquote><pre>
  403. * DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
  404. * </pre></blockquote>
  405. * Which is equilalent to
  406. * <pre><blockquote>
  407. * DBCollection users = mongo.getCollection( "wiki.users" );
  408. * </pre></blockquote>
  409. * @param n the name of the collection to find
  410. * @return the matching collection
  411. *
  412. * TODO - Make this support type construction
  413. */
  414. def getCollection(n: String): MongoCollection = underlying.getCollection(n).asScala
  415. /**
  416. * Find a collection that is prefixed with this collection's name.
  417. * A typical use of this might be
  418. * <blockquote><pre>
  419. * DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
  420. * </pre></blockquote>
  421. * Which is equilalent to
  422. * <pre><blockquote>
  423. * DBCollection users = mongo.getCollection( "wiki.users" );
  424. * </pre></blockquote>
  425. * @param n the name of the collection to find
  426. * @return the matching collection
  427. *
  428. * TODO - Make this support type construction
  429. */
  430. def collection(n: String): MongoCollection = underlying.getCollection(n).asScala
  431. /**
  432. * Returns the number of documents in the collection
  433. * that match the specified query
  434. *
  435. * @param query specifies the selection criteria
  436. * @param fields this is ignored
  437. * @param limit limit the count to this value
  438. * @param skip number of documents to skip
  439. * @param readPrefs The [ReadPreference] to be used for this operation
  440. * @param maxTime the maximum duration that the server will allow this operation to execute before killing it
  441. *
  442. * @return the number of documents that matches selection criteria
  443. */
  444. def getCount[A <% DBObject, B <% DBObject](query: A = MongoDBObject.empty, fields: B = MongoDBObject.empty,
  445. limit: Long = 0, skip: Long = 0,
  446. readPrefs: ReadPreference = getReadPreference,
  447. maxTime: Duration = Duration(0, MILLISECONDS)): Int = {
  448. underlying.find(query, fields)
  449. .skip(skip.toInt)
  450. .limit(limit.toInt)
  451. .setReadPreference(readPrefs)
  452. .maxTime(maxTime.length, maxTime.unit)
  453. .count()
  454. }
  455. /**
  456. * Returns the database this collection is a member of.
  457. * @return this collection's database
  458. */
  459. def getDB: MongoDB = underlying.getDB.asScala
  460. /**
  461. * Returns the full name of this collection, with the database name as a prefix.
  462. * @return the name of this collection
  463. */
  464. def getFullName: String = underlying.getFullName
  465. /**
  466. * Returns the full name of this collection, with the database name as a prefix.
  467. * @return the name of this collection
  468. */
  469. def fullName: String = getFullName
  470. /**
  471. * Return a list of the indexes for this collection. Each object
  472. * in the list is the "info document" from MongoDB
  473. *
  474. * @return list of index documents
  475. */
  476. def getIndexInfo: mutable.Buffer[DBObject] = underlying.getIndexInfo.asScala
  477. /**
  478. * Return a list of the indexes for this collection. Each object
  479. * in the list is the "info document" from MongoDB
  480. *
  481. * @return list of index documents
  482. */
  483. def indexInfo: mutable.Buffer[DBObject] = getIndexInfo
  484. def getName: String = underlying.getName
  485. def name: String = getName
  486. /**
  487. * Gets the default class for objects in the collection
  488. * @return the class
  489. */
  490. def getObjectClass: Class[_] = underlying.getObjectClass
  491. /**
  492. * Gets the default class for objects in the collection
  493. * @return the class
  494. */
  495. def objectClass: Class[_] = getObjectClass
  496. /**
  497. * setObjectClass
  498. *
  499. * Set a subtype of DBObject which will be used
  500. * to deserialize documents returned from MongoDB.
  501. *
  502. * This method will return a new <code>MongoTypedCollection[A]</code>
  503. * which you should capture if you want explicit casting.
  504. * Else, this collection will instantiate instances of A but cast them to
  505. * the current <code>T</code> (DBObject if you have a generic collection)
  506. *
  507. * @param c (Class[A])
  508. * @tparam A A Subtype of DBObject
  509. *
  510. * TODO - Ensure proper subtype return
  511. */
  512. def setObjectClass[A <: DBObject: Manifest](c: Class[A]): MongoGenericTypedCollection[A] = {
  513. underlying.setObjectClass(c)
  514. new MongoGenericTypedCollection[A](underlying = self.underlying)
  515. }
  516. /**
  517. * setObjectClass
  518. *
  519. * Set a subtype of DBObject which will be used
  520. * to deserialize documents returned from MongoDB.
  521. *
  522. * This method will return a new <code>MongoTypedCollection[A]</code>
  523. * which you should capture if you want explicit casting.
  524. * Else, this collection will instantiate instances of A but cast them to
  525. * the current <code>T</code> (DBObject if you have a generic collection)
  526. *
  527. * @param c (Class[A])
  528. * @tparam A A Subtype of DBObject
  529. *
  530. */
  531. def objectClass_=[A <: DBObject: Manifest](c: Class[A]): MongoGenericTypedCollection[A] = setObjectClass(c)
  532. def stats: CommandResult = getStats
  533. def getStats: CommandResult = underlying.getStats
  534. // scalastyle:off null
  535. /**
  536. * Applies a group operation
  537. *
  538. * @param key the key to group <code>{ a : true }</code>
  539. * @param cond optional condition on query
  540. * @param reduce javascript reduce function
  541. * @param initial initial value for first match on a key
  542. * @param finalize An optional function that can operate on the result(s) of the reduce function.
  543. * @param readPrefs ReadPreferences for this command
  544. * @return The results of the group
  545. */
  546. def group[A <% DBObject, B <% DBObject, C <% DBObject](key: A, cond: B, initial: C,
  547. reduce: String, finalize: String = null,
  548. readPrefs: ReadPreference = getReadPreference): Iterable[T] = {
  549. underlying.group(key, cond, initial, reduce, finalize, readPrefs).map(_._2.asInstanceOf[T])
  550. }
  551. // scalastyle:on null
  552. /**
  553. * Saves document(s) to the database.
  554. * if doc doesn't have an _id, one will be added
  555. * you can get the _id that was added from doc after the insert
  556. *
  557. * @param doc array of documents (<% DBObject) to save
  558. * @param concern the WriteConcern for the insert
  559. */
  560. def insert[A](doc: A, concern: com.mongodb.WriteConcern)(implicit dbObjView: A => DBObject): WriteResult =
  561. insert(doc)(dbObjView, concern = concern)
  562. /**
  563. * Saves document(s) to the database.
  564. * if doc doesn't have an _id, one will be added
  565. * you can get the _id that was added from doc after the insert
  566. *
  567. * @param docs array of documents (<% DBObject) to save
  568. * TODO - Wrapper for WriteResult?
  569. */
  570. def insert[A](docs: A*)(implicit dbObjView: A => DBObject, concern: com.mongodb.WriteConcern = writeConcern,
  571. encoder: DBEncoder = customEncoderFactory.map(_.create).orNull): WriteResult =
  572. insert(new InsertOptions().writeConcern(writeConcern).dbEncoder(encoder), docs: _*)
  573. /**
  574. * Saves document(s) to the database.
  575. * if doc doesn't have an _id, one will be added
  576. * you can get the _id that was added from doc after the insert
  577. *
  578. * @param docs array of documents (<% DBObject) to save
  579. * @param insertOptions the insertOptions
  580. */
  581. def insert[A](insertOptions: InsertOptions, docs: A*)(implicit dbObjView: A => DBObject): WriteResult = {
  582. val b = new scala.collection.mutable.ArrayBuilder.ofRef[DBObject]
  583. b.sizeHint(docs.size)
  584. for { x <- docs } b += dbObjView(x)
  585. underlying.insert(b.result().toList.asJava, insertOptions)
  586. }
  587. def isCapped: Boolean = underlying.isCapped
  588. /**
  589. * performs an aggregation operation
  590. *
  591. * @param pipeline the aggregation pipeline
  592. *
  593. * @return The aggregation operation's result set
  594. * @deprecated @see aggregate(List[DBObject]) instead
  595. */
  596. @deprecated("Use aggregate(List(DBObject) instead", "2.7")
  597. def aggregate(pipeline: DBObject*): AggregationOutput =
  598. underlying.aggregate(pipeline.toList.asJava).asScala
  599. /**
  600. * performs an aggregation operation
  601. *
  602. * @param pipeline the aggregation pipeline
  603. *
  604. * @return The aggregation operation's result set
  605. *
  606. */
  607. def aggregate[A <% DBObject](pipeline: Iterable[A]): AggregationOutput =
  608. underlying.aggregate(pipeline.map(_.asInstanceOf[DBObject]).toList.asJava).asScala
  609. /**
  610. * performs an aggregation operation
  611. *
  612. * @param pipeline the aggregation pipeline
  613. * @param options the aggregation options
  614. *
  615. * @return The aggregation operation's result set
  616. *
  617. */
  618. def aggregate[A <% DBObject](pipeline: Iterable[A], options: AggregationOptions): Cursor =
  619. aggregate(pipeline, options, getReadPreference)
  620. /**
  621. * performs an aggregation operation
  622. *
  623. * @param pipeline the aggregation pipeline
  624. * @param readPreference The readPreference for the aggregation
  625. *
  626. * @return The aggregation operation's result set
  627. *
  628. */
  629. def aggregate[A <% DBObject](pipeline: Iterable[A], readPreference: ReadPreference): AggregationOutput =
  630. underlying.aggregate(pipeline.map(_.asInstanceOf[DBObject]).toList.asJava, readPreference).asScala
  631. /**
  632. * performs an aggregation operation
  633. *
  634. * @param pipeline the aggregation pipeline
  635. * @param options the aggregation options
  636. * @param readPreference The readPreference for the aggregation
  637. *
  638. * @return The aggregation operation's result set
  639. *
  640. */
  641. def aggregate[A <% DBObject](pipeline: Iterable[A], options: AggregationOptions, readPreference: ReadPreference): Cursor =
  642. underlying.aggregate(pipeline.map(_.asInstanceOf[DBObject]).toList.asJava, options, readPreference).asScala
  643. /**
  644. * Return the explain plan for the aggregation pipeline.
  645. *
  646. * @param pipeline the aggregation pipeline to explain
  647. * @param options the options to apply to the aggregation
  648. * @return the command result. The explain output may change from release to
  649. * release, so best to simply log this.
  650. */
  651. def explainAggregate[A <% DBObject](pipeline: Iterable[A], options: AggregationOptions): mutable.Map[String, AnyRef] =
  652. underlying.explainAggregate(pipeline.map(_.asInstanceOf[DBObject]).toList.asJava, options).asScala
  653. /**
  654. * Return a list of cursors over the collection that can be used to scan it in parallel.
  655. *
  656. * '''Note:''' As of MongoDB 2.6, this method will work against a mongod, but not a mongos.
  657. *
  658. * @param options the parallel scan options
  659. * @return a list of cursors, whose size may be less than the number requested
  660. * @since 2.7
  661. */
  662. def parallelScan(options: ParallelScanOptions): mutable.Buffer[Cursor] = {
  663. val builder = JavaParallelScanOptions.builder()
  664. builder.numCursors(options.numCursors)
  665. builder.batchSize(options.batchSize)
  666. builder.readPreference(options.readPreference.getOrElse(getReadPreference))
  667. underlying.parallelScan(builder.build()).asScala.map(_.asScala)
  668. }
  669. /**
  670. * Creates a builder for an ordered bulk operation. Write requests included in the bulk operations will be executed in order,
  671. * and will halt on the first failure.
  672. *
  673. * @return the builder
  674. *
  675. * @since 2.7
  676. */
  677. def initializeOrderedBulkOperation: BulkWriteOperation = BulkWriteOperation(underlying.initializeOrderedBulkOperation())
  678. /**
  679. * Creates a builder for an unordered bulk operation. Write requests included in the bulk operation will be executed in an undefined
  680. * order, and all requests will be executed even if some fail.
  681. *
  682. * @return the builder
  683. *
  684. * @since 2.7
  685. */
  686. def initializeUnorderedBulkOperation: BulkWriteOperation = BulkWriteOperation(underlying.initializeUnorderedBulkOperation())
  687. // scalastyle:off parameter.number
  688. /**
  689. * mapReduce execute a mapReduce against this collection.
  690. *
  691. * @param mapFunction the map function (JSFunction is just a type alias for String)
  692. * @param reduceFunction the reduce function (JSFunction is just a type alias for String)
  693. * @param output the location of the result of the map-reduce operation, defaults to inline.
  694. * You can output to a collection, output to a collection with an action, or output inline.
  695. * @param query (optional) the selection criteria for the documents input to the map function.
  696. * @param sort (optional) the input documents, useful for optimization.
  697. * @param limit (optional) the maximum number of documents to return from the collection before map reduce
  698. * @param finalizeFunction (optional) the finalize function (JSFunction is just a type alias for String)
  699. * @param jsScope (optional) global variables that are accessible in the map, reduce and finalize functions
  700. * @param verbose (optional) include the timing information in the result information
  701. * @param maxTime (optional) the maximum duration that the server will allow this operation to execute before killing it
  702. */
  703. def mapReduce(
  704. mapFunction: JSFunction,
  705. reduceFunction: JSFunction,
  706. output: MapReduceOutputTarget,
  707. query: Option[DBObject] = None,
  708. sort: Option[DBObject] = None,
  709. limit: Option[Int] = None,
  710. finalizeFunction: Option[JSFunction] = None,
  711. jsScope: Option[DBObject] = None,
  712. verbose: Boolean = false,
  713. maxTime: Option[Duration] = None
  714. ): MapReduceResult = {
  715. val cmd = MapReduceCommand(name, mapFunction, reduceFunction, output, query, sort, limit,
  716. finalizeFunction, jsScope, verbose, maxTime)
  717. mapReduce(cmd)
  718. }
  719. // scalastyle:on parameter.number
  720. /**
  721. * mapReduce execute a mapReduce against this collection.
  722. *
  723. * Throws a MongoExecutionTimeoutException if exceeds max duration limit otherwise returns a MapReduceResult
  724. *
  725. * @param cmd the MapReduceCommand
  726. */
  727. def mapReduce(cmd: MapReduceCommand): MapReduceResult = {
  728. val result = getDB.command(cmd.toDBObject)
  729. // Normally we catch MapReduce Errors and return them into a MapReduceResult
  730. // However, we we now preserve a MongoExecutionTimeoutException to keep in line with other methods
  731. try {
  732. result.throwOnError()
  733. } catch {
  734. case e: MongoExecutionTimeoutException => throw e
  735. case t: Throwable => true
  736. }
  737. MapReduceResult(result)
  738. }
  739. /**
  740. * Removes objects from the database collection.
  741. * @param o the object that documents to be removed must match
  742. * @param concern WriteConcern for this operation
  743. * TODO - Wrapper for WriteResult?
  744. */
  745. def remove[A](o: A, concern: com.mongodb.WriteConcern = getWriteConcern)(implicit
  746. dbObjView: A => DBObject,
  747. encoder: DBEncoder = customEncoderFactory.map(_.create).orNull): WriteResult =
  748. underlying.remove(dbObjView(o), concern, encoder)
  749. /**
  750. * Saves an object to this collection.
  751. * @param o the <code>DBObject</code> to save
  752. * will add <code>_id</code> field to o if needed
  753. * TODO - Wrapper for WriteResult?
  754. */
  755. def save[A](o: A, concern: com.mongodb.WriteConcern = getWriteConcern)(implicit dbObjView: A => DBObject): WriteResult =
  756. underlying.save(dbObjView(o), concern)
  757. /**
  758. * Set hint fields for this collection.
  759. * @param docs a list of <code>DBObject</code>s to be used as hints
  760. */
  761. def setHintFields[A <% DBObject](docs: List[A]) {
  762. val b = List.newBuilder[DBObject]
  763. for { x <- docs } b += x
  764. underlying.setHintFields(b.result().asJava)
  765. }
  766. /**
  767. * Set hint fields for this collection.
  768. * @param docs a list of <code>DBObject</code>s to be used as hints
  769. */
  770. def hintFields_=[A <% DBObject](docs: List[A]): Unit = setHintFields(docs)
  771. def setInternalClass[A <: DBObject](path: String, c: Class[A]): Unit = underlying.setInternalClass(path, c)
  772. def internalClass_=[A <: DBObject](path: String, c: Class[A]): Unit = setInternalClass(path, c)
  773. override def toString: String = underlying.toString
  774. /**
  775. * Performs an update operation.
  776. * @param q search query for old object to update
  777. * @param o object with which to update `q`
  778. */
  779. def update[A, B](q: A, o: B, upsert: Boolean = false, multi: Boolean = false,
  780. concern: com.mongodb.WriteConcern = this.writeConcern,
  781. bypassDocumentValidation: Option[Boolean] = None)(implicit queryView: A => DBObject, objView: B => DBObject,
  782. encoder: DBEncoder = customEncoderFactory.map(_.create).orNull): WriteResult = {
  783. bypassDocumentValidation match {
  784. case None => underlying.update(queryView(q), objView(o), upsert, multi, concern, encoder)
  785. case Some(bypassValidation) => underlying.update(queryView(q), objView(o), upsert, multi, concern, bypassValidation, encoder)
  786. }
  787. }
  788. /**
  789. * Perform a multi update
  790. * @param q search query for old object to update
  791. * @param o object with which to update <tt>q</tt>
  792. */
  793. @deprecated("In the face of default arguments this is a bit silly. Please use `update(multi=True)`.", "2.3.0")
  794. def updateMulti[A <% DBObject, B <% DBObject](q: A, o: B): WriteResult = underlying.updateMulti(q, o)
  795. override def hashCode(): Int = underlying.hashCode
  796. /**
  797. * Checks if this collection is equal to another object.
  798. * @param obj object with which to compare this collection
  799. * @return if the two collections are the same object
  800. */
  801. override def equals(obj: Any): Boolean = obj match {
  802. case other: MongoCollectionBase => underlying.equals(other.underlying)
  803. case _ => false
  804. }
  805. def count[A <% DBObject, B <% DBObject](query: A = MongoDBObject.empty, fields: B = MongoDBObject.empty,
  806. limit: Long = 0, skip: Long = 0, readPrefs: ReadPreference = getReadPreference,
  807. maxTime: Duration = Duration(0, MILLISECONDS)): Int =
  808. getCount(query, fields, limit, skip, readPrefs, maxTime)
  809. // scalastyle:off method.name
  810. /**
  811. * Save an object to the Collection
  812. *
  813. * @param x object to save to the collection
  814. */
  815. def +=[A <% DBObject](x: A): WriteResult = save(x)
  816. /**
  817. * Remove a matching object from the collection
  818. *
  819. * @param x object to remove from the collection
  820. */
  821. def -=[A <% DBObject](x: A): WriteResult = remove(x)
  822. // scalastyle:on method.name
  823. /**
  824. *
  825. * Set the write concern for this database.
  826. * Will be used for writes to any collection in this database.
  827. * See the documentation for [[com.mongodb.WriteConcern]] for more info.
  828. *
  829. * @param concern (WriteConcern) The write concern to use
  830. * @see WriteConcern
  831. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  832. */
  833. def setWriteConcern(concern: WriteConcern): Unit = underlying.setWriteConcern(concern)
  834. /**
  835. *
  836. * Set the write concern for this database.
  837. * Will be used for writes to any collection in this database.
  838. * See the documentation for [[com.mongodb.WriteConcern]] for more info.
  839. *
  840. * @param concern (WriteConcern) The write concern to use
  841. * @see WriteConcern
  842. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  843. */
  844. def writeConcern_=(concern: WriteConcern): Unit = setWriteConcern(concern)
  845. /**
  846. *
  847. * get the write concern for this database,
  848. * which is used for writes to any collection in this database.
  849. * See the documentation for [[com.mongodb.WriteConcern]] for more info.
  850. *
  851. * @see WriteConcern
  852. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  853. */
  854. def getWriteConcern: WriteConcern = underlying.getWriteConcern
  855. /**
  856. *
  857. * get the write concern for this database,
  858. * which is used for writes to any collection in this database.
  859. * See the documentation for [[com.mongodb.WriteConcern]] for more info.
  860. *
  861. * @see WriteConcern
  862. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  863. */
  864. def writeConcern: WriteConcern = getWriteConcern
  865. /**
  866. * Sets the read preference for this collection. Will be used as default for
  867. * reads from any collection in this collection. See the
  868. * documentation for [[com.mongodb.casbah.ReadPreference]] for more information.
  869. *
  870. * @param pref Read Preference to use
  871. */
  872. def readPreference_=(pref: ReadPreference): Unit = setReadPreference(pref)
  873. /**
  874. * Sets the read preference for this collection. Will be used as default for
  875. * reads from any collection in this collection. See the
  876. * documentation for [[com.mongodb.casbah.ReadPreference]] for more information.
  877. *
  878. * @param pref Read Preference to use
  879. */
  880. def setReadPreference(pref: ReadPreference): Unit = underlying.setReadPreference(pref)
  881. /**
  882. * Gets the read preference for this collection. Will be used as default for
  883. * reads from any collection in this collection. See the
  884. * documentation for [[com.mongodb.casbah.ReadPreference]] for more information.
  885. */
  886. def readPreference: ReadPreference = getReadPreference
  887. /**
  888. * Gets the read preference for this collection. Will be used as default for
  889. * reads from any collection in this collection. See the
  890. * documentation for [[com.mongodb.casbah.ReadPreference]] for more information.
  891. */
  892. def getReadPreference: ReadPreference = underlying.getReadPreference
  893. /**
  894. * Manipulate Network Options
  895. *
  896. * @see com.mongodb.Mongo
  897. * @see com.mongodb.Bytes
  898. */
  899. def addOption(option: Int): Unit = underlying.addOption(option)
  900. /**
  901. * Manipulate Network Options
  902. *
  903. * @see com.mongodb.Mongo
  904. * @see com.mongodb.Bytes
  905. */
  906. def resetOptions(): Unit = underlying.resetOptions() // use parens because this side-effects
  907. /**
  908. * Manipulate Network Options
  909. *
  910. * @see com.mongodb.Mongo
  911. * @see com.mongodb.Bytes
  912. */
  913. def getOptions: Int = underlying.getOptions
  914. /**
  915. * Manipulate Network Options
  916. *
  917. * @see com.mongodb.Mongo
  918. * @see com.mongodb.Bytes
  919. */
  920. def options: Int = getOptions
  921. /**
  922. * Sets queries to be OK to run on slave nodes.
  923. */
  924. @deprecated("Replaced with `ReadPreference.SECONDARY`", "2.3.0")
  925. @SuppressWarnings(Array("deprecation"))
  926. def slaveOk(): Unit = underlying.slaveOk() // use parens because this side-effects
  927. /**
  928. * does a rename of this collection to newName
  929. * As per the Java API this returns a *NEW* Collection,
  930. * and the old collection is probably no good anymore.
  931. *
  932. * This collection *WILL NOT* mutate --- the instance will
  933. * still point at a now nonexistant collection with the old name
  934. * ... You must capture the return value for the new instance.
  935. *
  936. * @param newName new collection name (not a full namespace)
  937. * @return the new collection
  938. */
  939. def rename(newName: String): MongoCollection =
  940. new MongoCollection(self.underlying.rename(newName))
  941. /**
  942. *
  943. * does a rename of this collection to newName
  944. * As per the Java API this returns a *NEW* Collection,
  945. * and the old collection is probably no good anymore.
  946. *
  947. * This collection *WILL NOT* mutate --- the instance will
  948. * still point at a now nonexistant collection with the old name
  949. * ... You must capture the return value for the new instance.
  950. *
  951. * @param newName new collection name (not a full namespace)
  952. * @param dropTarget if a collection with the new name exists, whether or not to drop it
  953. * @return the new collection
  954. */
  955. def rename(newName: String, dropTarget: Boolean): MongoCollection =
  956. new MongoCollection(self.underlying.rename(newName, dropTarget))
  957. // scalastyle:off method.name
  958. /**
  959. * _newCursor
  960. *
  961. * Utility method which concrete subclasses
  962. * are expected to implement for creating a new
  963. * instance of the correct cursor implementation from a
  964. * Java cursor. Good with cursor calls that return a new cursor.
  965. * Should figure out the right type to return based on typing setup.
  966. *
  967. * @param cursor (DBCursor)
  968. * @return (MongoCursorBase)
  969. */
  970. def _newCursor(cursor: DBCursor): CursorType
  971. /**
  972. * _newInstance
  973. *
  974. * Utility method which concrete subclasses
  975. * are expected to implement for creating a new
  976. * instance of THIS concrete implementation from a
  977. * Java collection. Good with calls that return a new collection.
  978. *
  979. * @param collection (DBCollection)
  980. * @return (this.type)
  981. */
  982. def _newInstance(collection: DBCollection): MongoCollectionBase
  983. protected def _typedValue(dbObj: DBObject): Option[T] = Option(dbObj.asInstanceOf[T])
  984. // scalastyle:on method.name
  985. }
  986. /**
  987. * Concrete collection implementation expecting standard DBObject operation
  988. * This is the version of MongoCollectionBase you should expect to use in most cases.
  989. *
  990. * @version 2.0, 12/23/10
  991. * @since 1.0
  992. *
  993. */
  994. class MongoCollection(val underlying: DBCollection) extends MongoCollectionBase with Iterable[DBObject] {
  995. type T = DBObject
  996. type CursorType = MongoCursor
  997. // scalastyle:off method.name
  998. /**
  999. * _newCursor
  1000. *
  1001. * Utility method which concrete subclasses
  1002. * are expected to implement for creating a new
  1003. * instance of the correct cursor implementation from a
  1004. * Java cursor. Good with cursor calls that return a new cursor.
  1005. * Should figure out the right type to return based on typing setup.
  1006. *
  1007. * @param cursor (DBCursor)
  1008. * @return (MongoCursorBase)
  1009. */
  1010. def _newCursor(cursor: DBCursor): MongoCursor = new MongoCursor(cursor)
  1011. /**
  1012. * _newInstance
  1013. *
  1014. * Utility method which concrete subclasses
  1015. * are expected to implement for creating a new
  1016. * instance of THIS concrete implementation from a
  1017. * Java collection. Good with calls that return a new collection.
  1018. *
  1019. * @param collection (DBCollection)
  1020. * @return (this.type)
  1021. */
  1022. def _newInstance(collection: DBCollection): MongoCollection = new MongoCollection(collection)
  1023. override protected def _typedValue(dbObj: DBObject): Option[DBObject] = Option(dbObj)
  1024. // scalastyle:on method.name
  1025. override def head: T = headOption.get
  1026. override def headOption: Option[T] = findOne()
  1027. override def tail: Iterable[DBObject] = find().skip(1).toIterable
  1028. override def iterator: CursorType = find()
  1029. override def size: Int = count()
  1030. override def toString(): String = name
  1031. }
  1032. /**
  1033. * Concrete collection implementation for typed Cursor operations via Collection.setObjectClass et al
  1034. * This is a special case collection for typed operations
  1035. *
  1036. * @version 2.0, 12/23/10
  1037. * @since 1.0
  1038. *
  1039. */
  1040. trait MongoTypedCollection extends MongoCollectionBase {}
  1041. class MongoGenericTypedCollection[A <: DBObject](val underlying: DBCollection) extends MongoTypedCollection {
  1042. type T = A
  1043. type CursorType = MongoGenericTypedCursor[A]
  1044. // scalastyle:off method.name
  1045. /**
  1046. * _newCursor
  1047. *
  1048. * Utility method which concrete subclasses
  1049. * are expected to implement for creating a new
  1050. * instance of the correct cursor implementation from a
  1051. * Java cursor. Good with cursor calls that return a new cursor.
  1052. * Should figure out the right type to return based on typing setup.
  1053. *
  1054. * @param cursor (DBCursor)
  1055. * @return (MongoCollectionBase)
  1056. */
  1057. def _newCursor(cursor: DBCursor): MongoGenericTypedCursor[T] = new MongoGenericTypedCursor[T](cursor)
  1058. /**
  1059. * _newInstance
  1060. *
  1061. * Utility method which concrete subclasses
  1062. * are expected to implement for creating a new
  1063. * instance of THIS concrete implementation from a
  1064. * Java collection. Good with calls that return a new collection.
  1065. *
  1066. * @param collection (DBCollection)
  1067. * @return (this.type)
  1068. */
  1069. def _newInstance(collection: DBCollection): MongoGenericTypedCollection[T] =
  1070. new MongoGenericTypedCollection[T](collection)
  1071. // scalastyle:on method.name
  1072. }
  1073. /**
  1074. * Helper object for some static methods
  1075. */
  1076. object MongoCollection extends Logging {
  1077. }