PageRenderTime 78ms CodeModel.GetById 44ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/marcello3d/casbah
Scala | 964 lines | 307 code | 98 blank | 559 comment | 6 complexity | 3038e9044cf3c306b59a9624c567f0fb MD5 | raw file
  1. /**
  2. * Copyright (c) 2010 10gen, Inc. <http://10gen.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. package com.mongodb.casbah
  23. import com.mongodb.casbah.Imports._
  24. import com.mongodb.casbah.commons.Logging
  25. import com.mongodb.casbah.map_reduce.{MapReduceResult, MapReduceCommand}
  26. import scalaj.collection.Imports._
  27. import collection.mutable.ArrayBuffer
  28. /**
  29. * Base trait for all MongoCollection wrapper objects.
  30. * Provides any non-parameterized methods and the basic structure.
  31. * Requires an underlying object of a DBCollection.
  32. *
  33. * @author Brendan W. McAdams <brendan@10gen.com>
  34. */
  35. trait MongoCollectionWrapper extends Logging {
  36. /**
  37. * The underlying Java Mongo Driver Collection object we proxy.
  38. */
  39. val underlying: com.mongodb.DBCollection
  40. /** Returns the database this collection is a member of.
  41. * @return this collection's database
  42. */
  43. implicit val db = underlying.getDB().asScala
  44. /** Adds the "private" fields _id to an object.
  45. * @param o <code>DBObject</code> to which to add fields
  46. * @return the modified parameter object
  47. */
  48. def apply[A <% DBObject : Manifest](o: A) = underlying.apply(o)
  49. /** Adds the "private" fields _id to an object.
  50. * @param jo object to which to add fields
  51. * @param ensureID whether to add an <code>_id</code> field or not
  52. * @return the modified object <code>o</code>
  53. */
  54. def apply[A <% DBObject : Manifest](jo: A, ensureID: Boolean) = underlying.apply(jo, ensureID)
  55. /** Forces creation of an index on a set of fields, if one does not already exist.
  56. * @param keys an object with a key set of the fields desired for the index
  57. */
  58. def createIndex[A <% DBObject : Manifest](keys: A) = underlying.createIndex(keys)
  59. def createIndex[A <% DBObject : Manifest, B <% DBObject : Manifest](keys: A, options: B) =
  60. underlying.createIndex(keys, options)
  61. /**
  62. * find distinct values for a key
  63. */
  64. def distinct(key: String) = underlying.distinct(key).asScala
  65. /**
  66. * find distinct values for a key
  67. * @param query query to apply on collection
  68. */
  69. def distinct[A <% DBObject : Manifest](key: String, query: A) =
  70. underlying.distinct(key, query).asScala
  71. /** Drops (deletes) this collection
  72. */
  73. def drop() = underlying.drop
  74. /** Drops (deletes) this collection
  75. */
  76. def dropCollection() = underlying.drop
  77. def dropIndex[A <% DBObject : Manifest](keys: A) =
  78. underlying.dropIndex(keys)
  79. def dropIndex(name: String) = underlying.dropIndex(name)
  80. /** Drops all indices from this collection
  81. */
  82. def dropIndexes() = underlying.dropIndexes
  83. def dropIndexes(name: String) = underlying.dropIndexes
  84. /** Creates an index on a set of fields, if one does not already exist.
  85. * @param keys an object with a key set of the fields desired for the index
  86. */
  87. def ensureIndex[A <% DBObject : Manifest](keys: A) =
  88. underlying.ensureIndex(keys)
  89. /** Ensures an index on this collection (that is, the index will be created if it does not exist).
  90. * ensureIndex is optimized and is inexpensive if the index already exists.
  91. * @param keys fields to use for index
  92. * @param name an identifier for the index
  93. * @dochub indexes
  94. */
  95. def ensureIndex[A <% DBObject : Manifest](keys: A, name: String) = underlying.ensureIndex(keys, name)
  96. /** Ensures an optionally unique index on this collection.
  97. * @param keys fields to use for index
  98. * @param name an identifier for the index
  99. * @param unique if the index should be unique
  100. */
  101. def ensureIndex[A <% DBObject : Manifest](keys: A, name: String, unique: Boolean) = underlying.ensureIndex(keys, name, unique)
  102. def ensureIndex(name: String) = underlying.ensureIndex(name)
  103. /** Find a collection that is prefixed with this collection's name.
  104. * A typical use of this might be
  105. * <blockquote><pre>
  106. * DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" );
  107. * </pre></blockquote>
  108. * Which is equilalent to
  109. * <pre><blockquote>
  110. * DBCollection users = mongo.getCollection( "wiki.users" );
  111. * </pre></blockquote>
  112. * @param n the name of the collection to find
  113. * @return the matching collection
  114. */
  115. def getCollection(n: String) = underlying.getCollection(n)
  116. /**
  117. * Returns the number of documents in the collection
  118. * @return number of documents that match query
  119. */
  120. def getCount() = underlying.getCount()
  121. /**
  122. * Returns the number of documents in the collection
  123. * that match the specified query
  124. *
  125. * @param query query to select documents to count
  126. * @return number of documents that match query
  127. */
  128. def getCount[A <% DBObject : Manifest](query: A) = underlying.getCount(query)
  129. /**
  130. * Returns the number of documents in the collection
  131. * that match the specified query
  132. *
  133. * @param query query to select documents to count
  134. * @param fields fields to return
  135. * @return number of documents that match query and fields
  136. */
  137. def getCount[A <% DBObject : Manifest, B<% DBObject : Manifest](query: A, fields: B) =
  138. underlying.getCount(query, fields)
  139. /**
  140. * Returns the number of documents in the collection
  141. * that match the specified query
  142. *
  143. * @param query query to select documents to count
  144. * @param fields fields to return
  145. * @param limit Max # of fields
  146. * @param skip # of fields to skip
  147. * @return number of documents that match query and fields
  148. */
  149. def getCount[A <% DBObject : Manifest, B<% DBObject : Manifest](query: A, fields: B, limit: Long, skip: Long) =
  150. underlying.getCount(query, fields, limit, skip)
  151. /** Returns the database this collection is a member of.
  152. * @return this collection's database
  153. */
  154. def getDB() = underlying.getDB().asScala
  155. /** Returns the full name of this collection, with the database name as a prefix.
  156. * @return the name of this collection
  157. */
  158. def getFullName() = underlying.getFullName
  159. /** Returns the full name of this collection, with the database name as a prefix.
  160. * @return the name of this collection
  161. */
  162. def fullName = getFullName()
  163. /**
  164. * Return a list of the indexes for this collection. Each object
  165. * in the list is the "info document" from MongoDB
  166. *
  167. * @return list of index documents
  168. */
  169. def getIndexInfo() = underlying.getIndexInfo.asScala
  170. /**
  171. * Return a list of the indexes for this collection. Each object
  172. * in the list is the "info document" from MongoDB
  173. *
  174. * @return list of index documents
  175. */
  176. def indexInfo = getIndexInfo()
  177. def getName() = underlying.getName
  178. def name = getName()
  179. /** Gets the default class for objects in the collection
  180. * @return the class
  181. */
  182. def getObjectClass() = underlying.getObjectClass
  183. /** Gets the default class for objects in the collection
  184. * @return the class
  185. */
  186. def objectClass = getObjectClass()
  187. def stats = getStats()
  188. def getStats() = underlying.getStats()
  189. def group[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest](key: A, cond: B, initial: C, reduce: String) = {
  190. val result = underlying.group(key, cond, initial, reduce)
  191. result.map(_._2.asInstanceOf[DBObject])
  192. }
  193. /**
  194. * Perform an absurdly simple grouping with no initial object or reduce function.
  195. */
  196. def group[A <% DBObject : Manifest, B <% DBObject : Manifest](key: A, cond: B): Iterable[DBObject] = group(key, cond, MongoDBObject.empty, "function(obj, prev) {}")
  197. def group[A <% DBObject : Manifest, B <% DBObject : Manifest](key: A, cond: B, function: String): Iterable[DBObject] = group(key, cond, MongoDBObject.empty, function)
  198. /**
  199. * Enables you to call group with the finalize parameter (a function that runs on each
  200. * row of the output for calculations before sending a return) which the Mongo Java driver does not yet
  201. * support, by sending a direct DBObject command. Messy, but it works.
  202. */
  203. def group[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest](key: A, cond: B, initial: C, reduce: String, finalize: String) = {
  204. val cmdData = MongoDBObject(
  205. "ns" -> getName,
  206. "key" -> key,
  207. "cond" -> cond,
  208. "$reduce" -> reduce,
  209. "initial" -> initial,
  210. "finalize" -> finalize)
  211. log.trace("Executing group command: %s", cmdData)
  212. val result = getDB.command(MongoDBObject("group" -> cmdData))
  213. if (result.get("ok").asInstanceOf[Double] != 1) {
  214. log.warning("Group Statement Failed.")
  215. }
  216. log.trace("Group command result count : %s keys: %s ", result.get("count"), result.get("keys"))
  217. result.get("retval").asInstanceOf[DBObject].toMap.asScala.map(_._2.asInstanceOf[DBObject]).asInstanceOf[ArrayBuffer[DBObject]]
  218. }
  219. /** Emulates a SQL MAX() call ever so gently */
  220. def maxValue[A <% DBObject : Manifest](field: String, condition: A) = {
  221. val initial = MongoDBObject("max" -> "")
  222. val groupResult = group(MongoDBObject.empty,
  223. condition,
  224. initial,
  225. """
  226. function(obj, aggr) {
  227. if (aggr.max == '') {
  228. aggr.max = obj.%s;
  229. } else if (obj.%s > aggr.max) {
  230. aggr.max = obj.%s;
  231. }
  232. }""".format(field, field, field), "")
  233. log.trace("Max Grouping Result: %s", groupResult)
  234. groupResult.head.get("max").asInstanceOf[Double]
  235. }
  236. def maxDate[A <% DBObject : Manifest](field: String, condition: A) = {
  237. val initial = Map("max" -> "").asDBObject
  238. val groupResult = group(MongoDBObject.empty,
  239. condition,
  240. initial,
  241. """
  242. function(obj, aggr) {
  243. if (aggr.max == '') {
  244. aggr.max = obj.%s;
  245. } else if (obj.%s > aggr.max) {
  246. aggr.max = obj.%s;
  247. }
  248. }""".format(field, field, field), "")
  249. log.trace("Max Date Grouping Result: %s", groupResult)
  250. groupResult.head.get("max").asInstanceOf[java.util.Date]
  251. }
  252. def minDate[A <% DBObject : Manifest](field: String, condition: A) = {
  253. val initial = Map("max" -> "").asDBObject
  254. val groupResult = group(MongoDBObject.empty,
  255. condition,
  256. initial,
  257. """
  258. function(obj, aggr) {
  259. if (aggr.max == '') {
  260. aggr.max = obj.%s;
  261. } else if (obj.%s > aggr.max) {
  262. aggr.max = obj.%s;
  263. }
  264. }""".format(field, field, field), "")
  265. log.trace("Max Date Grouping Result: %s", groupResult)
  266. groupResult.head.get("max").asInstanceOf[java.util.Date]
  267. }
  268. /** Emulates a SQL MIN() call ever so gently */
  269. def minValue[A <% DBObject : Manifest](field: String, condition: A) = {
  270. val initial = Map("min" -> "").asDBObject
  271. group(MongoDBObject.empty,
  272. condition,
  273. initial,
  274. """
  275. function(obj, aggr) {
  276. if (aggr.min == '') {
  277. aggr.min = obj.%s;
  278. } else if (obj.%s < aggr.min) {
  279. aggr.min = obj.%s;
  280. }
  281. }""".format(field, field, field), "").
  282. head.get("min").asInstanceOf[Double]
  283. }
  284. /** Emulates a SQL AVG() call ever so gently */
  285. def avgValue[A <% DBObject : Manifest](field: String, condition: A) = {
  286. val initial = Map("count" -> 0, "total" -> 0, "avg" -> 0).asDBObject
  287. group(MongoDBObject.empty,
  288. condition,
  289. initial,
  290. """
  291. function(obj, aggr) {
  292. aggr.total += obj.%s;
  293. aggr.count += 1;
  294. }
  295. """.format(field),
  296. "function(aggr) { aggr.avg = aggr.total / aggr.count }").head.get("avg").asInstanceOf[Double]
  297. }
  298. override def hashCode() = underlying.hashCode
  299. /**
  300. * Saves document(s) to the database.
  301. * if doc doesn't have an _id, one will be added
  302. * you can get the _id that was added from doc after the insert
  303. *
  304. * @param arr array of documents to save
  305. * @dochub insert
  306. */
  307. def insert[A <% DBObject : Manifest](docs: A*) = {
  308. val b = new scala.collection.mutable.ArrayBuilder.ofRef[DBObject]
  309. b.sizeHint(docs.size)
  310. for (x <- docs) b += x
  311. underlying.insert(b.result: _*)
  312. }
  313. /**
  314. * Saves document(s) to the database.
  315. * if doc doesn't have an _id, one will be added
  316. * you can get the _id that was added from doc after the insert
  317. *
  318. * @param arr array of documents to save
  319. * @dochub insert
  320. */
  321. def insert[A <: Array[B] : Manifest, B <% DBObject : Manifest](docs: A, writeConcern: WriteConcern) = {
  322. val b = new scala.collection.mutable.ArrayBuilder.ofRef[DBObject]
  323. b.sizeHint(docs.size)
  324. for (x <- docs) b += x
  325. underlying.insert(b.result, writeConcern)
  326. }
  327. /**
  328. * Inserts a document into the database.
  329. * if doc doesn't have an _id, one will be added
  330. * you can get the _id that was added from doc after the insert
  331. *
  332. * @param arr array of documents to save
  333. * @dochub insert
  334. */
  335. def insert[A <% DBObject : Manifest](doc: A, writeConcern: WriteConcern) =
  336. underlying.insert(doc, writeConcern)
  337. /**
  338. * Saves document(s) to the database.
  339. * if doc doesn't have an _id, one will be added
  340. * you can get the _id that was added from doc after the insert
  341. *
  342. * @param list list of documents to save
  343. * @dochub insert
  344. */
  345. def insert[A <% DBObject : Manifest](docs: List[A]) = {
  346. val b = List.newBuilder[DBObject]
  347. for (x <- docs) b += x
  348. underlying.insert(b.result.asJava)
  349. }
  350. def isCapped = underlying.isCapped()
  351. /**
  352. * @deprecated Due to poor original design on my part, you should probably use the explicitly parameterized fversion of this on collection * @param command An instance of MapReduceCommand representing the required MapReduce
  353. * @return MapReduceResult a wrapped result object. This contains the returns success, counts etc, but implements iterator and can be iterated directly
  354. */
  355. def mapReduce(command: MapReduceCommand): MapReduceResult = {
  356. val result = getDB.command(command.asDBObject)
  357. new MapReduceResult(result)
  358. }
  359. /**
  360. *
  361. * @param command An instance of MapReduceCommand representing the required MapReduce
  362. * @return MapReduceResult a wrapped result object. This contains the returns success, counts etc, but implements iterator and can be iterated directly
  363. */
  364. def mapReduce(mapFunction: JSFunction,
  365. reduceFunction: JSFunction,
  366. outputCollection: Option[String] = None,
  367. query: Option[DBObject] = None,
  368. sort: Option[DBObject] = None,
  369. finalizeFunction: Option[JSFunction] = None,
  370. jsScope: Option[String] = None): MapReduceResult =
  371. new MapReduceResult(getDB.command(MapReduceCommand(name, mapFunction, reduceFunction,
  372. outputCollection, query, sort, finalizeFunction,
  373. jsScope).asDBObject))
  374. /** Removes objects from the database collection.
  375. * @param o the object that documents to be removed must match
  376. * @dochub remove
  377. */
  378. def remove[A <% DBObject : Manifest](o: A) = underlying.remove(o)
  379. /** Removes objects from the database collection.
  380. * @param o the object that documents to be removed must match
  381. * @param concern WriteConcern for this operation
  382. * @dochub remove
  383. */
  384. def remove[A <% DBObject : Manifest](o: A, writeConcern: WriteConcern) =
  385. underlying.remove(o, writeConcern)
  386. /**
  387. * does a rename of this collection to newName
  388. * @param newName new collection name (not a full namespace)
  389. * @return the new collection
  390. */
  391. def rename(newName: String) = underlying.rename(newName)
  392. /** Clears all indices that have not yet been applied to this collection. */
  393. def resetIndexCache() = underlying.resetIndexCache()
  394. /** Saves an object to this collection.
  395. * @param jo the <code>DBObject</code> to save
  396. * will add <code>_id</code> field to jo if needed
  397. */
  398. def save[A <% DBObject : Manifest](jo: A) = underlying.save(jo)
  399. /** Saves an object to this collection.
  400. * @param jo the <code>DBObject</code> to save
  401. * will add <code>_id</code> field to jo if needed
  402. */
  403. def save[A <% DBObject : Manifest](jo: A, writeConcern: WriteConcern) = underlying.save(jo, writeConcern)
  404. /** Set hint fields for this collection.
  405. * @param lst a list of <code>DBObject</code>s to be used as hints
  406. */
  407. def setHintFields[A <% DBObject : Manifest](docs: List[A]) = {
  408. val b = List.newBuilder[DBObject]
  409. for (x <- docs) b += x
  410. underlying.setHintFields(b.result.asJava)
  411. }
  412. def setInternalClass(path: String, c: Class[_]) = underlying.setInternalClass(path, c)
  413. override def toString() = underlying.toString
  414. /**
  415. * @dochub update
  416. */
  417. def update[A <% DBObject : Manifest, B <% DBObject : Manifest](q: A, o: B) = underlying.update(q, o)
  418. /**
  419. * Performs an update operation.
  420. * @param q search query for old object to update
  421. * @param o object with which to update <tt>q</tt>
  422. * @param upsert if the database should create the element if it does not exist
  423. * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above)
  424. * See http://www.mongodb.org/display/DOCS/Atomic+Operations
  425. * @dochub update
  426. */
  427. def update[A <% DBObject : Manifest, B <% DBObject : Manifest](q: A, o: B, upsert: Boolean, multi: Boolean) = underlying.update(q, o, upsert, multi)
  428. /**
  429. * Performs an update operation.
  430. * @param q search query for old object to update
  431. * @param o object with which to update <tt>q</tt>
  432. * @param upsert if the database should create the element if it does not exist
  433. * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above)
  434. * See http://www.mongodb.org/display/DOCS/Atomic+Operations
  435. * @param writeConcern WriteConcern for this operation
  436. * @dochub update
  437. */
  438. def update[A <% DBObject : Manifest, B <% DBObject : Manifest](q: A, o: B, upsert: Boolean, multi: Boolean, writeConcern: WriteConcern) =
  439. underlying.update(q, o, upsert, multi, writeConcern)
  440. /**
  441. * @dochub update
  442. */
  443. def updateMulti[A <% DBObject : Manifest, B <% DBObject : Manifest](q: A, o: B) = underlying.updateMulti(q, o)
  444. /** Checks if this collection is equal to another object.
  445. * @param o object with which to compare this collection
  446. * @return if the two collections are the same object
  447. */
  448. override def equals(obj: Any) = obj match {
  449. case other: MongoCollectionWrapper => underlying.equals(other.underlying)
  450. case _ => false
  451. }
  452. def count = getCount
  453. def count[A <% DBObject : Manifest](query: A) = getCount(query)
  454. def count[A <% DBObject : Manifest, B <% DBObject : Manifest](query: A, fields: B) = getCount(query, fields)
  455. def lastError = underlying.getDB.getLastError
  456. /**
  457. * MongoDB DB collection.save method
  458. *
  459. * @author Alexander Azarov <azarov@osinka.ru>
  460. *
  461. * @param x object to save to the collection
  462. */
  463. def +=[A <% DBObject : Manifest](x: A) = save(x)
  464. /**
  465. * MongoDB DBCollection.remove method
  466. *
  467. * @author Alexander Azarov <azarov@osinka.ru>
  468. *
  469. * @param x object to remove from the collection
  470. */
  471. def -=[A <% DBObject : Manifest](x: A) = remove(x)
  472. /**
  473. *
  474. * Set the write concern for this database.
  475. * Will be used for writes to any collection in this database.
  476. * See the documentation for {@link WriteConcern} for more info.
  477. *
  478. * @param concern (WriteConcern) The write concern to use
  479. * @see WriteConcern
  480. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  481. */
  482. def setWriteConcern(concern: WriteConcern) = underlying.setWriteConcern(concern)
  483. /**
  484. *
  485. * Set the write concern for this database.
  486. * Will be used for writes to any collection in this database.
  487. * See the documentation for {@link WriteConcern} for more info.
  488. *
  489. * @param concern (WriteConcern) The write concern to use
  490. * @see WriteConcern
  491. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  492. */
  493. def writeConcern_=(concern: WriteConcern) = setWriteConcern(concern)
  494. /**
  495. *
  496. * get the write concern for this database,
  497. * which is used for writes to any collection in this database.
  498. * See the documentation for {@link WriteConcern} for more info.
  499. *
  500. * @see WriteConcern
  501. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  502. */
  503. def getWriteConcern() = underlying.getWriteConcern()
  504. /**
  505. *
  506. * get the write concern for this database,
  507. * which is used for writes to any collection in this database.
  508. * See the documentation for {@link WriteConcern} for more info.
  509. *
  510. * @see WriteConcern
  511. * @see http://www.thebuzzmedia.com/mongodb-single-server-data-durability-guide/
  512. */
  513. def writeConcern = getWriteConcern
  514. /**
  515. * Manipulate Network Options
  516. *
  517. * @see com.mongodb.Mongo
  518. * @see com.mongodb.Bytes
  519. */
  520. def addOption(option: Int) = underlying.addOption(option)
  521. /**
  522. * Manipulate Network Options
  523. *
  524. * @see com.mongodb.Mongo
  525. * @see com.mongodb.Bytes
  526. */
  527. def resetOptions() = underlying.resetOptions() // use parens because this side-effects
  528. /**
  529. * Manipulate Network Options
  530. *
  531. * @see com.mongodb.Mongo
  532. * @see com.mognodb.Bytes
  533. */
  534. def getOptions() = underlying.getOptions
  535. /**
  536. * Manipulate Network Options
  537. *
  538. * @see com.mongodb.Mongo
  539. * @see com.mognodb.Bytes
  540. */
  541. def options = getOptions
  542. /**
  543. * Sets queries to be OK to run on slave nodes.
  544. */
  545. def slaveOk() = underlying.slaveOk() // use parens because this side-effects
  546. }
  547. /**
  548. * A Non-Generic, DBObject returning implementation of the <code>ScalaMongoCollectionWrapper</code>
  549. * Which implements Iterable, to allow iterating directly over it to list all of the underlying objects.
  550. *
  551. * @author Brendan W. McAdams <brendan@10gen.com>
  552. *
  553. * @param underlying DBCollection object to proxy
  554. */
  555. class MongoCollection(val underlying: com.mongodb.DBCollection)
  556. extends MongoCollectionWrapper
  557. with Iterable[DBObject] {
  558. override def elements: MongoCursor = find
  559. override def iterator: MongoCursor = find
  560. /** Queries for all objects in this collection.
  561. * @return a cursor which will iterate over every object
  562. * @dochub find
  563. */
  564. def find() = underlying.find.asScala
  565. /** Queries for an object in this collection.
  566. * @param ref object for which to search
  567. * @return an iterator over the results
  568. * @dochub find
  569. */
  570. def find(ref: DBObject) = underlying.find(ref) asScala
  571. /** Queries for an object in this collection.
  572. *
  573. * <p>
  574. * An empty DBObject will match every document in the collection.
  575. * Regardless of fields specified, the _id fields are always returned.
  576. * </p>
  577. * <p>
  578. * An example that returns the "x" and "_id" fields for every document
  579. * in the collection that has an "x" field:
  580. * </p>
  581. * <blockquote><pre>
  582. * BasicDBObject keys = new BasicDBObject();
  583. * keys.put("x", 1);
  584. *
  585. * DBCursor cursor = collection.find(new BasicDBObject(), keys);
  586. * </pre></blockquote>
  587. *
  588. * @param ref object for which to search
  589. * @param keys fields to return
  590. * @return a cursor to iterate over results
  591. * @dochub find
  592. */
  593. def find(ref: DBObject, keys: DBObject) = underlying.find(ref, keys) asScala
  594. /** Finds an object.
  595. * @param ref query used to search
  596. * @param fields the fields of matching objects to return
  597. * @param numToSkip will not return the first <tt>numToSkip</tt> matches
  598. * @param batchSize if positive, is the # of objects per batch sent back from the db. all objects that match will be returned. if batchSize < 0, its a hard limit, and only 1 batch will either batchSize or the # that fit in a batch
  599. * @param options - see Bytes QUERYOPTION_*
  600. * @return the objects, if found
  601. * @dochub find
  602. */
  603. def find(ref: DBObject, fields: DBObject, numToSkip: Int, batchSize: Int) = underlying.find(ref, fields, numToSkip, batchSize) asScala
  604. /**
  605. * Returns a single object from this collection.
  606. * @return the object found, or <code>null</code> if the collection is empty
  607. */
  608. def findOne(): Option[DBObject] = optWrap(underlying.findOne())
  609. /**
  610. * Returns a single object from this collection matching the query.
  611. * @param o the query object
  612. * @return the object found, or <code>null</code> if no such object exists
  613. */
  614. def findOne(o: DBObject): Option[DBObject] = optWrap(underlying.findOne(o))
  615. /**
  616. * Returns a single object from this collection matching the query.
  617. * @param o the query object
  618. * @param fields fields to return
  619. * @return the object found, or <code>null</code> if no such object exists
  620. * @dochub find
  621. */
  622. def findOne(o: DBObject, fields: DBObject): Option[DBObject] = optWrap(underlying.findOne(o, fields))
  623. def findOneView[A <% DBObject : Manifest](o: A) = optWrap(underlying.findOne(o))
  624. def findOneView[A <% DBObject : Manifest, B <% DBObject : Manifest](o: A, fields: B) =
  625. optWrap(underlying.findOne(o, fields))
  626. /**
  627. * Finds the first document in the query (sorted) and updates it.
  628. * If remove is specified it will be removed. If new is specified then the updated
  629. * document will be returned, otherwise the old document is returned (or it would be lost forever).
  630. * You can also specify the fields to return in the document, optionally.
  631. * @return the found document (before, or after the update)
  632. */
  633. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest](query: A, update: B) = optWrap(underlying.findAndModify(query, update))
  634. /**
  635. * Finds the first document in the query (sorted) and updates it.
  636. * @return the old document
  637. */
  638. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest](query: A, sort: B, update: C) = optWrap(underlying.findAndModify(query, sort, update))
  639. /**
  640. * Finds the first document in the query and updates it.
  641. * @return the old document
  642. */
  643. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest, D <% DBObject : Manifest](
  644. query: A, fields: B, sort: C, remove: Boolean, update: D, returnNew: Boolean, upsert: Boolean
  645. ) = optWrap(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert))
  646. /**
  647. * Finds the first document in the query and removes it.
  648. * @return the removed document
  649. */
  650. def findAndRemove[A <% DBObject : Manifest](query: A) = optWrap(underlying.findAndRemove(query))
  651. /**
  652. * Finds an object by its id. This compares the passed in value to the _id field of the document
  653. * I've put some hackery in to try to detect possible conversions....
  654. * Because of the way we're using context and view bounds in Scala
  655. * the broad match of this method can be problematic.
  656. *
  657. *
  658. * @param obj any valid object
  659. * @return the object, if found, otherwise <code>null</code>
  660. */
  661. def findOne(obj: Object): Option[DBObject] = obj match {
  662. case dbobj: MongoDBObject => {
  663. log.debug("View convertable[mongodbobject] - rerouting.")
  664. findOne(dbobj.asDBObject)
  665. }
  666. case map: Map[String, Any] => {
  667. log.debug("View convertable[map]- rerouting.")
  668. findOne(map.asDBObject)
  669. }
  670. case _ => optWrap(underlying.findOne(obj))
  671. }
  672. /**
  673. * Finds an object by its id. This compares the passed in value to the _id field of the document
  674. * Because of the way we're using context and view bounds in Scala
  675. * the broad match of this method can be problematic.
  676. * I've put some hackery in to try to detect possible conversions....
  677. *
  678. * @param obj any valid object
  679. * @param fields fields to return
  680. * @return the object, if found, otherwise <code>null</code>
  681. * @dochub find
  682. */
  683. def findOne(obj: AnyRef, fields: DBObject): Option[DBObject] = obj match {
  684. case dbobj: MongoDBObject => {
  685. log.debug("View convertable[mongodbobject] - rerouting.")
  686. findOneView(dbobj.asDBObject, fields)
  687. }
  688. case map: Map[String, Any] => {
  689. log.debug("View convertable[map]- rerouting.")
  690. findOneView(map.asDBObject, fields)
  691. }
  692. case _ => optWrap(underlying.findOne(obj, fields))
  693. }
  694. override def head = headOption.get
  695. override def headOption = findOne
  696. override def tail = find.skip(1).toList
  697. /**
  698. * write concern aware write op block.
  699. *
  700. * Guarantees that the operations in the passed
  701. * block are executed in the same connection
  702. * via requestStart() and requestDone().
  703. *
  704. * Calls &amp; throws getLastError afterwards,
  705. * so if you run multiple ops you'll only get the final
  706. * error.
  707. *
  708. * Your op function gets a copy of this MongoDB.
  709. *
  710. * This is for update ops only - you cannot return data from it.
  711. *
  712. *
  713. * @throws MongoException
  714. */
  715. def request(op: MongoCollection => Unit) = getDB.request(db => op(db(name)))
  716. }
  717. /**
  718. * A Generic, parameterized DBObject-subclass returning implementation of the <code>ScalaMongoCollectionWrapper</code>
  719. * This is instantiated with a type (and an implicitly discovered or explicitly passed Manifest object) to determine it's underlying type.
  720. *
  721. * It will attempt to deserialize *ALL* returned results (except for things like group and mapReduce which don't return collection objects)
  722. * to it's type, on the assumption that the collection matches the type's spec.
  723. *
  724. *
  725. * implements Iterable, to allow iterating directly over it to list all of the underlying objects.
  726. *
  727. * @author Brendan W. McAdams <brendan@10gen.com>
  728. *
  729. * @param A type representing a DBObject subclass which this class should return instead of generic DBObjects
  730. * @param underlying DBCollection object to proxy
  731. */
  732. class MongoTypedCollection[T <: DBObject : Manifest](val underlying: com.mongodb.DBCollection) extends Iterable[T]
  733. with MongoCollectionWrapper {
  734. type UnderlyingObj = T
  735. val m = manifest[T]
  736. log.debug("Manifest erasure: " + m.erasure)
  737. underlying.setObjectClass(m.erasure)
  738. override def elements = find
  739. override def iterator = find
  740. /** Queries for all objects in this collection.
  741. * @return a cursor which will iterate over every object
  742. * @dochub find
  743. */
  744. def find() = underlying.find.asScalaTyped
  745. /** Queries for an object in this collection.
  746. * @param ref object for which to search
  747. * @return an iterator over the results
  748. * @dochub find
  749. */
  750. def find(ref: DBObject) = underlying.find(ref) asScalaTyped
  751. /** Queries for an object in this collection.
  752. *
  753. * <p>
  754. * An empty DBObject will match every document in the collection.
  755. * Regardless of fields specified, the _id fields are always returned.
  756. * </p>
  757. * <p>
  758. * An example that returns the "x" and "_id" fields for every document
  759. * in the collection that has an "x" field:
  760. * </p>
  761. * <blockquote><pre>
  762. * BasicDBObject keys = new BasicDBObject();
  763. * keys.put("x", 1);
  764. *
  765. * DBCursor cursor = collection.find(new BasicDBObject(), keys);
  766. * </pre></blockquote>
  767. *
  768. * @param ref object for which to search
  769. * @param keys fields to return
  770. * @return a cursor to iterate over results
  771. * @dochub find
  772. */
  773. def find(ref: DBObject, keys: DBObject) = underlying.find(ref, keys) asScalaTyped
  774. /** Finds an object.
  775. * @param ref query used to search
  776. * @param fields the fields of matching objects to return
  777. * @param numToSkip will not return the first <tt>numToSkip</tt> matches
  778. * @param batchSize if positive, is the # of objects per batch sent back from the db. all objects that match will be returned. if batchSize < 0, its a hard limit, and only 1 batch will either batchSize or the # that fit in a batch
  779. * @param options - see Bytes QUERYOPTION_*
  780. * @return the objects, if found
  781. * @dochub find
  782. */
  783. def find(ref: DBObject, fields: DBObject, numToSkip: Int, batchSize: Int) = underlying.find(ref, fields, numToSkip, batchSize) asScalaTyped
  784. /**
  785. * Returns a single object from this collection.
  786. * @return the object found, or <code>null</code> if the collection is empty
  787. */
  788. def findOne() = optWrap(underlying.findOne().asInstanceOf[T])
  789. /**
  790. * Returns a single object from this collection matching the query.
  791. * @param o the query object
  792. * @return the object found, or <code>null</code> if no such object exists
  793. */
  794. def findOne(o: DBObject) = optWrap(underlying.findOne(o).asInstanceOf[T])
  795. /**
  796. * Returns a single object from this collection matching the query.
  797. * @param o the query object
  798. * @param fields fields to return
  799. * @return the object found, or <code>null</code> if no such object exists
  800. * @dochub find
  801. */
  802. def findOne(o: DBObject, fields: DBObject) = optWrap(underlying.findOne(o, fields).asInstanceOf[T])
  803. /**
  804. * Finds an object by its id.
  805. * This compares the passed in value to the _id field of the document
  806. *
  807. * @param obj any valid object
  808. * @return the object, if found, otherwise <code>null</code>
  809. */
  810. def findOne(obj: Object) = optWrap(underlying.findOne(obj).asInstanceOf[T])
  811. /**
  812. * Finds an object by its id.
  813. * This compares the passed in value to the _id field of the document
  814. *
  815. * @param obj any valid object
  816. * @param fields fields to return
  817. * @return the object, if found, otherwise <code>null</code>
  818. * @dochub find
  819. */
  820. def findOne(obj: Object, fields: DBObject) = optWrap(underlying.findOne(obj, fields).asInstanceOf[T])
  821. /**
  822. * Finds the first document in the query (sorted) and updates it.
  823. * If remove is specified it will be removed. If new is specified then the updated
  824. * document will be returned, otherwise the old document is returned (or it would be lost forever).
  825. * You can also specify the fields to return in the document, optionally.
  826. * @return the found document (before, or after the update)
  827. */
  828. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest](query: A, update: B) = optWrap(underlying.findAndModify(query, update)).asInstanceOf[T]
  829. /**
  830. * Finds the first document in the query (sorted) and updates it.
  831. * @return the old document
  832. */
  833. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest](query: A, sort: B, update: C) = optWrap(underlying.findAndModify(query, sort, update)).asInstanceOf[T]
  834. /**
  835. * Finds the first document in the query and updates it.
  836. * @return the old document
  837. */
  838. def findAndModify[A <% DBObject : Manifest, B <% DBObject : Manifest, C <% DBObject : Manifest, D <% DBObject : Manifest](
  839. query: A, fields: B, sort: C, remove: Boolean, update: D, returnNew: Boolean, upsert: Boolean
  840. ) = optWrap(underlying.findAndModify(query, fields, sort, remove, update, returnNew, upsert)).asInstanceOf[T]
  841. /**
  842. * Finds the first document in the query and removes it.
  843. * @return the removed document
  844. */
  845. def findAndRemove[A <% DBObject : Manifest](query: A) = optWrap(underlying.findAndRemove(query)).asInstanceOf[T]
  846. override def head = findOne.get
  847. override def headOption = Some(findOne.get.asInstanceOf[T])
  848. override def tail = find.skip(1).map(_.asInstanceOf[T]).toList
  849. def setHintFields(lst: List[DBObject]) = underlying.setHintFields(lst.asJava)
  850. def setObjectClass[A <: DBObject : Manifest](c: Class[A]) = {
  851. underlying.setObjectClass(c)
  852. new MongoTypedCollection[A](underlying)
  853. }
  854. override def toString() = underlying.toString
  855. def update(q: DBObject, o: DBObject) = underlying.update(q, o)
  856. def update(q: DBObject, o: DBObject, upsert: Boolean, multi: Boolean) = underlying.update(q, o, upsert, multi)
  857. def updateMulti(q: DBObject, o: DBObject) = underlying.updateMulti(q, o)
  858. /** Checks if this collection is equal to another object.
  859. * @param o object with which to compare this collection
  860. * @return if the two collections are the same object
  861. */
  862. override def equals(obj: Any) = obj match {
  863. case other: MongoCollectionWrapper => underlying.equals(other.underlying)
  864. case _ => false
  865. }
  866. }