PageRenderTime 42ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/casbah-core/src/test/scala/BulkWriteOperationSpec.scala

http://github.com/mongodb/casbah
Scala | 406 lines | 309 code | 76 blank | 21 comment | 0 complexity | 5f48eb012552f0586d1074fb7da1e40e 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. package com.mongodb.casbah.test.core
  23. import com.mongodb.BulkWriteUpsert
  24. import com.mongodb.casbah.Imports._
  25. import scala.collection.mutable
  26. @SuppressWarnings(Array("deprecation"))
  27. class BulkWriteOperationSpec extends CasbahDBTestSpecification {
  28. def initializeBulkOperation(ordered: Boolean): BulkWriteOperation = {
  29. ordered match {
  30. case true => collection.initializeOrderedBulkOperation
  31. case false => collection.initializeUnorderedBulkOperation
  32. }
  33. }
  34. Seq(true, false) foreach {
  35. ordered =>
  36. val builderName = {
  37. ordered match {
  38. case true => "OrderedBulkOperation"
  39. case false => "UnorderedBulkOperation"
  40. }
  41. }
  42. "The " + builderName should {
  43. "when no document with the same id exists, should insert the document " in {
  44. collection.drop()
  45. val operation = initializeBulkOperation(ordered)
  46. operation.insert(MongoDBObject("_id" -> 1))
  47. val result = operation.execute()
  48. result.insertedCount must beEqualTo(1)
  49. result.upserts.size must beEqualTo(0)
  50. collection.findOne() must beSome(MongoDBObject("_id" -> 1))
  51. }
  52. "when a document contains a key with an illegal character, inserting it should throw IllegalArgumentException" in {
  53. collection.drop()
  54. val operation = initializeBulkOperation(ordered)
  55. operation.insert(MongoDBObject("$set" -> 1))
  56. operation.execute() should throwA[IllegalArgumentException]
  57. }
  58. "when a document with the same id exists, should throw an exception" in {
  59. collection.drop()
  60. val document = MongoDBObject("_id" -> 1)
  61. collection.insert(document)
  62. val operation = initializeBulkOperation(ordered)
  63. operation.insert(document)
  64. operation.execute() should throwA[BulkWriteException]
  65. }
  66. "when a document with no _id is inserted, the _id should be generated by the driver" in {
  67. collection.drop()
  68. val document = MongoDBObject()
  69. val operation = initializeBulkOperation(ordered)
  70. operation.insert(document)
  71. operation.execute()
  72. collection.findOne() must haveSomeField("_id")
  73. }
  74. "when documents match the query, a remove of one should remove one of them" in {
  75. collection.drop()
  76. collection += MongoDBObject("x" -> true)
  77. collection += MongoDBObject("x" -> true)
  78. val operation = initializeBulkOperation(ordered)
  79. operation.find(MongoDBObject("x" -> true)).removeOne()
  80. val result = operation.execute()
  81. result.removedCount must beEqualTo(1)
  82. result.upserts.size must beEqualTo(0)
  83. collection.count() must beEqualTo(1)
  84. }
  85. "when documents match the query, a remove should remove all of them" in {
  86. collection.drop()
  87. collection += MongoDBObject("x" -> true)
  88. collection += MongoDBObject("x" -> true)
  89. collection += MongoDBObject("x" -> false)
  90. val operation = initializeBulkOperation(ordered)
  91. operation.find(MongoDBObject("x" -> true)).remove()
  92. val result = operation.execute()
  93. result.removedCount must beEqualTo(2)
  94. result.upserts.size must beEqualTo(0)
  95. collection.count() must beEqualTo(1)
  96. }
  97. "when an update document contains a non $-prefixed key, update should throw IllegalArgumentException" in {
  98. collection.drop()
  99. val operation = initializeBulkOperation(ordered)
  100. operation.find(MongoDBObject()).update($set("x" -> 1) ++ ("y" -> 2))
  101. operation.execute() should throwA[IllegalArgumentException]
  102. }
  103. "when an update document contains a non $-prefixed key, updateOne should throw IllegalArgumentException" in {
  104. val operation = initializeBulkOperation(ordered)
  105. operation.find(MongoDBObject()).update($set("x" -> 1) ++ ("y" -> 2))
  106. operation.execute() should throwA[IllegalArgumentException]
  107. }
  108. "when multiple document match the query, updateOne should update only one of them" in {
  109. collection.drop()
  110. collection += MongoDBObject("x" -> true)
  111. collection += MongoDBObject("x" -> true)
  112. val operation = initializeBulkOperation(ordered)
  113. operation.find(MongoDBObject("x" -> true)).updateOne($set("y" -> 1))
  114. val result = operation.execute()
  115. result.matchedCount must beEqualTo(1)
  116. serverIsAtLeastVersion(2, 5) match {
  117. case true => result.modifiedCount.get must beEqualTo(1)
  118. case false => result.modifiedCount.get must throwA[UnsupportedOperationException]
  119. }
  120. collection.find(MongoDBObject("y" -> 1)).count() must beEqualTo(1)
  121. }
  122. "when multiple document match the query, update should update all of them" in {
  123. collection.drop()
  124. collection += MongoDBObject("x" -> true)
  125. collection += MongoDBObject("x" -> true)
  126. val operation = initializeBulkOperation(ordered)
  127. operation.find(MongoDBObject("x" -> true)).update($set("y" -> 1))
  128. val result = operation.execute()
  129. result.matchedCount must beEqualTo(2)
  130. serverIsAtLeastVersion(2, 5) match {
  131. case true => result.modifiedCount.get must beEqualTo(2)
  132. case false => result.modifiedCount.get must throwA[UnsupportedOperationException]
  133. }
  134. collection.find(MongoDBObject("y" -> 1)).count() must beEqualTo(2)
  135. }
  136. "when no document matches the query, update with upsert should insert a document" in {
  137. collection.drop()
  138. val id = new ObjectId()
  139. val operation = initializeBulkOperation(ordered)
  140. operation.find(MongoDBObject("_id" -> id)).upsert().update($set("x" -> 2))
  141. val result = operation.execute()
  142. result.getUpserts.size should beEqualTo(1)
  143. result.upserts must beEqualTo(mutable.Buffer(new BulkWriteUpsert(0, id)))
  144. collection.findOne() should beSome(MongoDBObject("_id" -> id, "x" -> 2))
  145. }
  146. "when no document matches the query, update with upsert should insert a document with custom _id" in {
  147. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  148. collection.drop()
  149. val query = MongoDBObject("_id" -> 101)
  150. val operation = initializeBulkOperation(ordered)
  151. operation.find(query).upsert().updateOne($set("x" -> 2))
  152. val result = operation.execute()
  153. result.upserts.size must beEqualTo(1)
  154. result.upserts must beEqualTo(mutable.Buffer(new BulkWriteUpsert(0, 101)))
  155. collection.findOne() must beSome(MongoDBObject("_id" -> 101, "x" -> 2))
  156. }
  157. "when documents matches the query, update with upsert should update all of them" in {
  158. collection.drop()
  159. collection.insert(MongoDBObject("x" -> true))
  160. collection.insert(MongoDBObject("x" -> true))
  161. collection.insert(MongoDBObject("x" -> false))
  162. val operation = initializeBulkOperation(ordered)
  163. operation.find(MongoDBObject("x" -> true)).upsert().update($set("y" -> 1))
  164. val result = operation.execute()
  165. result.matchedCount should beEqualTo(2)
  166. serverIsAtLeastVersion(2, 5) match {
  167. case true => result.modifiedCount.get must beEqualTo(2)
  168. case false => result.modifiedCount.get must throwA[UnsupportedOperationException]
  169. }
  170. collection.count(MongoDBObject("y" -> 1)) should beEqualTo(2)
  171. }
  172. "when a document contains a key with an illegal character, replacing a document with it should throw IllegalArgumentException" in {
  173. val operation = initializeBulkOperation(ordered)
  174. val query = MongoDBObject("_id" -> new ObjectId())
  175. operation.find(query).upsert().replaceOne($set("x" -> 1))
  176. operation.execute() should throwA[IllegalArgumentException]
  177. }
  178. "when no document matches the query, a replace with upsert should insert a document" in {
  179. collection.drop()
  180. collection += MongoDBObject("_id" -> 101)
  181. val operation = initializeBulkOperation(ordered)
  182. operation.find(MongoDBObject("_id" -> 101)).upsert().replaceOne(MongoDBObject("_id" -> 101, "x" -> 2))
  183. val result = operation.execute()
  184. result.matchedCount must beEqualTo(1)
  185. result.upserts.size must beEqualTo(0)
  186. collection.count() must beEqualTo(1)
  187. collection.findOne() must beSome(MongoDBObject("_id" -> 101, "x" -> 2))
  188. }
  189. "when multiple documents match the query, replaceOne should replace one of them" in {
  190. collection.drop()
  191. collection.insert(MongoDBObject("x" -> true))
  192. collection.insert(MongoDBObject("x" -> true))
  193. val operation = initializeBulkOperation(ordered)
  194. val replacement = MongoDBObject("y" -> 1, "x" -> false)
  195. operation.find(MongoDBObject("x" -> true)).replaceOne(replacement)
  196. operation.execute()
  197. collection.findOne(MongoDBObject("x" -> false), MongoDBObject("_id" -> 0)) should beSome(replacement)
  198. }
  199. "when a document matches the query, updateOne with upsert should update that document" in {
  200. collection.drop()
  201. val id = new ObjectId()
  202. collection.insert(MongoDBObject("_id" -> id))
  203. val operation = initializeBulkOperation(ordered)
  204. operation.find(MongoDBObject("_id" -> id)).upsert().updateOne($set("x" -> 2))
  205. operation.execute()
  206. collection.findOne() should beSome(MongoDBObject("_id" -> id, "x" -> 2))
  207. }
  208. "when a document matches the query, a replace with upsert should update that document" in {
  209. collection.drop()
  210. collection.insert(MongoDBObject("_id" -> 1))
  211. val operation = initializeBulkOperation(ordered)
  212. operation.find(MongoDBObject("_id" -> 1)).upsert().replaceOne(MongoDBObject("_id" -> 1, "x" -> 2))
  213. operation.execute()
  214. collection.findOne() should beSome(MongoDBObject("_id" -> 1, "x" -> 2))
  215. }
  216. }
  217. }
  218. "BulkWriteOperations" should {
  219. "handle multi-length runs of ordered insert, update, replace, and remove" in {
  220. collection.drop()
  221. collection.insert(testInserts: _*)
  222. val operation = collection.initializeOrderedBulkOperation
  223. addWritesTo(operation)
  224. operation.execute()
  225. collection.findOne(MongoDBObject("_id" -> 1)) must beSome(MongoDBObject("_id" -> 1, "x" -> 2))
  226. collection.findOne(MongoDBObject("_id" -> 2)) must beSome(MongoDBObject("_id" -> 2, "x" -> 3))
  227. collection.findOne(MongoDBObject("_id" -> 3)) must beNone
  228. collection.findOne(MongoDBObject("_id" -> 4)) must beNone
  229. collection.findOne(MongoDBObject("_id" -> 5)) must beSome(MongoDBObject("_id" -> 5, "x" -> 4))
  230. collection.findOne(MongoDBObject("_id" -> 6)) must beSome(MongoDBObject("_id" -> 6, "x" -> 5))
  231. collection.findOne(MongoDBObject("_id" -> 7)) must beSome(MongoDBObject("_id" -> 7))
  232. collection.findOne(MongoDBObject("_id" -> 8)) must beSome(MongoDBObject("_id" -> 8))
  233. }
  234. "error details should have correct index on ordered write failure" in {
  235. collection.drop()
  236. val operation = collection.initializeOrderedBulkOperation
  237. operation.insert(MongoDBObject("_id" -> 1))
  238. operation.find(MongoDBObject("_id" -> 1)).updateOne($set("x" -> 3))
  239. operation.insert(MongoDBObject("_id" -> 1))
  240. try {
  241. operation.execute()
  242. } catch {
  243. case ex: BulkWriteException =>
  244. ex.writeErrors.size must beEqualTo(1)
  245. ex.writeErrors(0).getIndex must beEqualTo(2)
  246. ex.writeErrors(0).getCode must beEqualTo(11000)
  247. ex.writeResult must beAnInstanceOf[BulkWriteResult]
  248. }
  249. true
  250. }
  251. "handle multi-length runs of unordered insert, update, replace, and remove" in {
  252. collection.drop()
  253. collection.insert(testInserts: _*)
  254. val operation = collection.initializeUnorderedBulkOperation
  255. addWritesTo(operation)
  256. val result = operation.execute()
  257. result.insertedCount must beEqualTo(2)
  258. result.matchedCount must beEqualTo(4)
  259. result.removedCount must beEqualTo(2)
  260. result.upserts.size must beEqualTo(0)
  261. serverIsAtLeastVersion(2, 5) match {
  262. case true => result.modifiedCount.get must beEqualTo(4)
  263. case false => result.modifiedCount.get must throwA[UnsupportedOperationException]
  264. }
  265. collection.findOne(MongoDBObject("_id" -> 1)) must beSome(MongoDBObject("_id" -> 1, "x" -> 2))
  266. collection.findOne(MongoDBObject("_id" -> 2)) must beSome(MongoDBObject("_id" -> 2, "x" -> 3))
  267. collection.findOne(MongoDBObject("_id" -> 3)) must beNone
  268. collection.findOne(MongoDBObject("_id" -> 4)) must beNone
  269. collection.findOne(MongoDBObject("_id" -> 5)) must beSome(MongoDBObject("_id" -> 5, "x" -> 4))
  270. collection.findOne(MongoDBObject("_id" -> 6)) must beSome(MongoDBObject("_id" -> 6, "x" -> 5))
  271. collection.findOne(MongoDBObject("_id" -> 7)) must beSome(MongoDBObject("_id" -> 7))
  272. collection.findOne(MongoDBObject("_id" -> 8)) must beSome(MongoDBObject("_id" -> 8))
  273. }
  274. "error details should have correct index on unordered write failure" in {
  275. collection.drop()
  276. collection.insert(testInserts: _*)
  277. val operation = collection.initializeUnorderedBulkOperation
  278. operation.insert(MongoDBObject("_id" -> 1))
  279. operation.find(MongoDBObject("_id" -> 2)).updateOne(MongoDBObject("$set" -> MongoDBObject("x" -> 3)))
  280. operation.insert(MongoDBObject("_id" -> 3))
  281. try {
  282. operation.execute()
  283. } catch {
  284. case ex: BulkWriteException =>
  285. ex.writeErrors.size must beEqualTo(2)
  286. ex.writeErrors(0).getIndex must beEqualTo(0)
  287. ex.writeErrors(0).getCode must beEqualTo(11000)
  288. ex.writeErrors(1).getIndex must beEqualTo(2)
  289. ex.writeErrors(1).getCode must beEqualTo(11000)
  290. ex.writeResult must beAnInstanceOf[BulkWriteResult]
  291. ex.getMessage must startWith("Bulk write operation error")
  292. }
  293. success
  294. }
  295. "test write concern exceptions" in {
  296. val mongoClient = MongoClient(List(new ServerAddress()))
  297. isReplicaSet must beTrue.orSkip("Testing writeConcern on ReplicaSet")
  298. try {
  299. val operation: BulkWriteOperation = collection.initializeUnorderedBulkOperation
  300. operation.insert(MongoDBObject())
  301. operation.execute(new WriteConcern(5, 1, false, false))
  302. failure("Execute should have failed")
  303. } catch {
  304. case e: BulkWriteException => (e.getWriteConcernError must not).beNull
  305. case _: Throwable => failure("Unexpected exception")
  306. }
  307. success
  308. }
  309. }
  310. def testInserts = {
  311. List(
  312. MongoDBObject("_id" -> 1),
  313. MongoDBObject("_id" -> 2),
  314. MongoDBObject("_id" -> 3),
  315. MongoDBObject("_id" -> 4),
  316. MongoDBObject("_id" -> 5),
  317. MongoDBObject("_id" -> 6)
  318. )
  319. }
  320. def addWritesTo(operation: BulkWriteOperation) {
  321. operation.find(MongoDBObject("_id" -> 1)).updateOne($set("x" -> 2))
  322. operation.find(MongoDBObject("_id" -> 2)).updateOne($set("x" -> 3))
  323. operation.find(MongoDBObject("_id" -> 3)).removeOne()
  324. operation.find(MongoDBObject("_id" -> 4)).removeOne()
  325. operation.find(MongoDBObject("_id" -> 5)).replaceOne(MongoDBObject("_id" -> 5, "x" -> 4))
  326. operation.find(MongoDBObject("_id" -> 6)).replaceOne(MongoDBObject("_id" -> 6, "x" -> 5))
  327. operation.insert(MongoDBObject("_id" -> 7))
  328. operation.insert(MongoDBObject("_id" -> 8))
  329. }
  330. }