How to use isDup method of regression Package

Best Keploy code snippet using regression.isDup

session_test.go

Source:session_test.go Github

copy

Full Screen

1// mgo - MongoDB driver for Go2//3// Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions are met:9//10// 1. Redistributions of source code must retain the above copyright notice, this11// list of conditions and the following disclaimer.12// 2. Redistributions in binary form must reproduce the above copyright notice,13// this list of conditions and the following disclaimer in the documentation14// and/or other materials provided with the distribution.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR20// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES21// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;22// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND23// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT24// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26package mgo_test27import (28 "flag"29 "fmt"30 "math"31 "os"32 "runtime"33 "sort"34 "strconv"35 "strings"36 "time"37 . "gopkg.in/check.v1"38 "gopkg.in/mgo.v2"39 "AutomatedRegression/gopkg.in/mgo.v2/bson"40)41func (s *S) TestRunString(c *C) {42 session, err := mgo.Dial("localhost:40001")43 c.Assert(err, IsNil)44 defer session.Close()45 result := struct{ Ok int }{}46 err = session.Run("ping", &result)47 c.Assert(err, IsNil)48 c.Assert(result.Ok, Equals, 1)49}50func (s *S) TestRunValue(c *C) {51 session, err := mgo.Dial("localhost:40001")52 c.Assert(err, IsNil)53 defer session.Close()54 result := struct{ Ok int }{}55 err = session.Run(M{"ping": 1}, &result)56 c.Assert(err, IsNil)57 c.Assert(result.Ok, Equals, 1)58}59func (s *S) TestPing(c *C) {60 session, err := mgo.Dial("localhost:40001")61 c.Assert(err, IsNil)62 defer session.Close()63 // Just ensure the nonce has been received.64 result := struct{}{}65 err = session.Run("ping", &result)66 mgo.ResetStats()67 err = session.Ping()68 c.Assert(err, IsNil)69 // Pretty boring.70 stats := mgo.GetStats()71 c.Assert(stats.SentOps, Equals, 1)72 c.Assert(stats.ReceivedOps, Equals, 1)73}74func (s *S) TestDialIPAddress(c *C) {75 session, err := mgo.Dial("127.0.0.1:40001")76 c.Assert(err, IsNil)77 defer session.Close()78 if os.Getenv("NOIPV6") != "1" {79 session, err = mgo.Dial("[::1%]:40001")80 c.Assert(err, IsNil)81 defer session.Close()82 }83}84func (s *S) TestURLSingle(c *C) {85 session, err := mgo.Dial("mongodb://localhost:40001/")86 c.Assert(err, IsNil)87 defer session.Close()88 result := struct{ Ok int }{}89 err = session.Run("ping", &result)90 c.Assert(err, IsNil)91 c.Assert(result.Ok, Equals, 1)92}93func (s *S) TestURLMany(c *C) {94 session, err := mgo.Dial("mongodb://localhost:40011,localhost:40012/")95 c.Assert(err, IsNil)96 defer session.Close()97 result := struct{ Ok int }{}98 err = session.Run("ping", &result)99 c.Assert(err, IsNil)100 c.Assert(result.Ok, Equals, 1)101}102func (s *S) TestURLParsing(c *C) {103 urls := []string{104 "localhost:40001?foo=1&bar=2",105 "localhost:40001?foo=1;bar=2",106 }107 for _, url := range urls {108 session, err := mgo.Dial(url)109 if session != nil {110 session.Close()111 }112 c.Assert(err, ErrorMatches, "unsupported connection URL option: (foo=1|bar=2)")113 }114}115func (s *S) TestInsertFindOne(c *C) {116 session, err := mgo.Dial("localhost:40001")117 c.Assert(err, IsNil)118 defer session.Close()119 coll := session.DB("mydb").C("mycoll")120 err = coll.Insert(M{"a": 1, "b": 2})121 c.Assert(err, IsNil)122 err = coll.Insert(M{"a": 1, "b": 3})123 c.Assert(err, IsNil)124 result := struct{ A, B int }{}125 err = coll.Find(M{"a": 1}).Sort("b").One(&result)126 c.Assert(err, IsNil)127 c.Assert(result.A, Equals, 1)128 c.Assert(result.B, Equals, 2)129 err = coll.Find(M{"a": 1}).Sort("-b").One(&result)130 c.Assert(err, IsNil)131 c.Assert(result.A, Equals, 1)132 c.Assert(result.B, Equals, 3)133}134func (s *S) TestInsertFindOneNil(c *C) {135 session, err := mgo.Dial("localhost:40002")136 c.Assert(err, IsNil)137 defer session.Close()138 coll := session.DB("mydb").C("mycoll")139 err = coll.Find(nil).One(nil)140 c.Assert(err, ErrorMatches, "unauthorized.*|not authorized.*")141}142func (s *S) TestInsertFindOneMap(c *C) {143 session, err := mgo.Dial("localhost:40001")144 c.Assert(err, IsNil)145 defer session.Close()146 coll := session.DB("mydb").C("mycoll")147 err = coll.Insert(M{"a": 1, "b": 2})148 c.Assert(err, IsNil)149 result := make(M)150 err = coll.Find(M{"a": 1}).One(result)151 c.Assert(err, IsNil)152 c.Assert(result["a"], Equals, 1)153 c.Assert(result["b"], Equals, 2)154}155func (s *S) TestInsertFindAll(c *C) {156 session, err := mgo.Dial("localhost:40001")157 c.Assert(err, IsNil)158 defer session.Close()159 coll := session.DB("mydb").C("mycoll")160 err = coll.Insert(M{"a": 1, "b": 2})161 c.Assert(err, IsNil)162 err = coll.Insert(M{"a": 3, "b": 4})163 c.Assert(err, IsNil)164 type R struct{ A, B int }165 var result []R166 assertResult := func() {167 c.Assert(len(result), Equals, 2)168 c.Assert(result[0].A, Equals, 1)169 c.Assert(result[0].B, Equals, 2)170 c.Assert(result[1].A, Equals, 3)171 c.Assert(result[1].B, Equals, 4)172 }173 // nil slice174 err = coll.Find(nil).Sort("a").All(&result)175 c.Assert(err, IsNil)176 assertResult()177 // Previously allocated slice178 allocd := make([]R, 5)179 result = allocd180 err = coll.Find(nil).Sort("a").All(&result)181 c.Assert(err, IsNil)182 assertResult()183 // Ensure result is backed by the originally allocated array184 c.Assert(&result[0], Equals, &allocd[0])185 // Non-pointer slice error186 f := func() { coll.Find(nil).All(result) }187 c.Assert(f, Panics, "result argument must be a slice address")188 // Non-slice error189 f = func() { coll.Find(nil).All(new(int)) }190 c.Assert(f, Panics, "result argument must be a slice address")191}192func (s *S) TestFindRef(c *C) {193 session, err := mgo.Dial("localhost:40001")194 c.Assert(err, IsNil)195 defer session.Close()196 db1 := session.DB("db1")197 db1col1 := db1.C("col1")198 db2 := session.DB("db2")199 db2col1 := db2.C("col1")200 err = db1col1.Insert(M{"_id": 1, "n": 1})201 c.Assert(err, IsNil)202 err = db1col1.Insert(M{"_id": 2, "n": 2})203 c.Assert(err, IsNil)204 err = db2col1.Insert(M{"_id": 2, "n": 3})205 c.Assert(err, IsNil)206 result := struct{ N int }{}207 ref1 := &mgo.DBRef{Collection: "col1", Id: 1}208 ref2 := &mgo.DBRef{Collection: "col1", Id: 2, Database: "db2"}209 err = db1.FindRef(ref1).One(&result)210 c.Assert(err, IsNil)211 c.Assert(result.N, Equals, 1)212 err = db1.FindRef(ref2).One(&result)213 c.Assert(err, IsNil)214 c.Assert(result.N, Equals, 3)215 err = db2.FindRef(ref1).One(&result)216 c.Assert(err, Equals, mgo.ErrNotFound)217 err = db2.FindRef(ref2).One(&result)218 c.Assert(err, IsNil)219 c.Assert(result.N, Equals, 3)220 err = session.FindRef(ref2).One(&result)221 c.Assert(err, IsNil)222 c.Assert(result.N, Equals, 3)223 f := func() { session.FindRef(ref1).One(&result) }224 c.Assert(f, PanicMatches, "Can't resolve database for &mgo.DBRef{Collection:\"col1\", Id:1, Database:\"\"}")225}226func (s *S) TestDatabaseAndCollectionNames(c *C) {227 session, err := mgo.Dial("localhost:40001")228 c.Assert(err, IsNil)229 defer session.Close()230 db1 := session.DB("db1")231 db1col1 := db1.C("col1")232 db1col2 := db1.C("col2")233 db2 := session.DB("db2")234 db2col1 := db2.C("col3")235 err = db1col1.Insert(M{"_id": 1})236 c.Assert(err, IsNil)237 err = db1col2.Insert(M{"_id": 1})238 c.Assert(err, IsNil)239 err = db2col1.Insert(M{"_id": 1})240 c.Assert(err, IsNil)241 names, err := session.DatabaseNames()242 c.Assert(err, IsNil)243 c.Assert(filterDBs(names), DeepEquals, []string{"db1", "db2"})244 // Try to exercise cursor logic. 2.8.0-rc3 still ignores this.245 session.SetBatch(2)246 names, err = db1.CollectionNames()247 c.Assert(err, IsNil)248 c.Assert(names, DeepEquals, []string{"col1", "col2", "system.indexes"})249 names, err = db2.CollectionNames()250 c.Assert(err, IsNil)251 c.Assert(names, DeepEquals, []string{"col3", "system.indexes"})252}253func (s *S) TestSelect(c *C) {254 session, err := mgo.Dial("localhost:40001")255 c.Assert(err, IsNil)256 defer session.Close()257 coll := session.DB("mydb").C("mycoll")258 coll.Insert(M{"a": 1, "b": 2})259 result := struct{ A, B int }{}260 err = coll.Find(M{"a": 1}).Select(M{"b": 1}).One(&result)261 c.Assert(err, IsNil)262 c.Assert(result.A, Equals, 0)263 c.Assert(result.B, Equals, 2)264}265func (s *S) TestInlineMap(c *C) {266 session, err := mgo.Dial("localhost:40001")267 c.Assert(err, IsNil)268 defer session.Close()269 coll := session.DB("mydb").C("mycoll")270 var v, result1 struct {271 A int272 M map[string]int ",inline"273 }274 v.A = 1275 v.M = map[string]int{"b": 2}276 err = coll.Insert(v)277 c.Assert(err, IsNil)278 noId := M{"_id": 0}279 err = coll.Find(nil).Select(noId).One(&result1)280 c.Assert(err, IsNil)281 c.Assert(result1.A, Equals, 1)282 c.Assert(result1.M, DeepEquals, map[string]int{"b": 2})283 var result2 M284 err = coll.Find(nil).Select(noId).One(&result2)285 c.Assert(err, IsNil)286 c.Assert(result2, DeepEquals, M{"a": 1, "b": 2})287}288func (s *S) TestUpdate(c *C) {289 session, err := mgo.Dial("localhost:40001")290 c.Assert(err, IsNil)291 defer session.Close()292 coll := session.DB("mydb").C("mycoll")293 ns := []int{40, 41, 42, 43, 44, 45, 46}294 for _, n := range ns {295 err := coll.Insert(M{"k": n, "n": n})296 c.Assert(err, IsNil)297 }298 // No changes is a no-op and shouldn't return an error.299 err = coll.Update(M{"k": 42}, M{"$set": M{"n": 42}})300 c.Assert(err, IsNil)301 err = coll.Update(M{"k": 42}, M{"$inc": M{"n": 1}})302 c.Assert(err, IsNil)303 result := make(M)304 err = coll.Find(M{"k": 42}).One(result)305 c.Assert(err, IsNil)306 c.Assert(result["n"], Equals, 43)307 err = coll.Update(M{"k": 47}, M{"k": 47, "n": 47})308 c.Assert(err, Equals, mgo.ErrNotFound)309 err = coll.Find(M{"k": 47}).One(result)310 c.Assert(err, Equals, mgo.ErrNotFound)311}312func (s *S) TestUpdateId(c *C) {313 session, err := mgo.Dial("localhost:40001")314 c.Assert(err, IsNil)315 defer session.Close()316 coll := session.DB("mydb").C("mycoll")317 ns := []int{40, 41, 42, 43, 44, 45, 46}318 for _, n := range ns {319 err := coll.Insert(M{"_id": n, "n": n})320 c.Assert(err, IsNil)321 }322 err = coll.UpdateId(42, M{"$inc": M{"n": 1}})323 c.Assert(err, IsNil)324 result := make(M)325 err = coll.FindId(42).One(result)326 c.Assert(err, IsNil)327 c.Assert(result["n"], Equals, 43)328 err = coll.UpdateId(47, M{"k": 47, "n": 47})329 c.Assert(err, Equals, mgo.ErrNotFound)330 err = coll.FindId(47).One(result)331 c.Assert(err, Equals, mgo.ErrNotFound)332}333func (s *S) TestUpdateNil(c *C) {334 session, err := mgo.Dial("localhost:40001")335 c.Assert(err, IsNil)336 defer session.Close()337 coll := session.DB("mydb").C("mycoll")338 err = coll.Insert(M{"k": 42, "n": 42})339 c.Assert(err, IsNil)340 err = coll.Update(nil, M{"$inc": M{"n": 1}})341 c.Assert(err, IsNil)342 result := make(M)343 err = coll.Find(M{"k": 42}).One(result)344 c.Assert(err, IsNil)345 c.Assert(result["n"], Equals, 43)346 err = coll.Insert(M{"k": 45, "n": 45})347 c.Assert(err, IsNil)348 _, err = coll.UpdateAll(nil, M{"$inc": M{"n": 1}})349 c.Assert(err, IsNil)350 err = coll.Find(M{"k": 42}).One(result)351 c.Assert(err, IsNil)352 c.Assert(result["n"], Equals, 44)353 err = coll.Find(M{"k": 45}).One(result)354 c.Assert(err, IsNil)355 c.Assert(result["n"], Equals, 46)356}357func (s *S) TestUpsert(c *C) {358 session, err := mgo.Dial("localhost:40001")359 c.Assert(err, IsNil)360 defer session.Close()361 coll := session.DB("mydb").C("mycoll")362 ns := []int{40, 41, 42, 43, 44, 45, 46}363 for _, n := range ns {364 err := coll.Insert(bson.D{{"k", n}, {"n", n}})365 c.Assert(err, IsNil)366 }367 info, err := coll.Upsert(M{"k": 42}, bson.D{{"k", 42}, {"n", 24}})368 c.Assert(err, IsNil)369 c.Assert(info.Updated, Equals, 1)370 c.Assert(info.Matched, Equals, 1)371 c.Assert(info.UpsertedId, IsNil)372 result := M{}373 err = coll.Find(M{"k": 42}).One(result)374 c.Assert(err, IsNil)375 c.Assert(result["n"], Equals, 24)376 // Match but do not change.377 info, err = coll.Upsert(M{"k": 42}, bson.D{{"k", 42}, {"n", 24}})378 c.Assert(err, IsNil)379 c.Assert(info.Updated, Equals, 1) // On 2.6+ this feels like a server mistake.380 c.Assert(info.Matched, Equals, 1)381 c.Assert(info.UpsertedId, IsNil)382 // Insert with internally created id.383 info, err = coll.Upsert(M{"k": 47}, M{"k": 47, "n": 47})384 c.Assert(err, IsNil)385 c.Assert(info.Updated, Equals, 0)386 c.Assert(info.Matched, Equals, 0)387 c.Assert(info.UpsertedId, NotNil)388 err = coll.Find(M{"k": 47}).One(result)389 c.Assert(err, IsNil)390 c.Assert(result["n"], Equals, 47)391 result = M{}392 err = coll.Find(M{"_id": info.UpsertedId}).One(result)393 c.Assert(err, IsNil)394 c.Assert(result["n"], Equals, 47)395 // Insert with provided id.396 info, err = coll.Upsert(M{"k": 48}, M{"k": 48, "n": 48, "_id": 48})397 c.Assert(err, IsNil)398 c.Assert(info.Updated, Equals, 0)399 c.Assert(info.Matched, Equals, 0)400 if s.versionAtLeast(2, 6) {401 c.Assert(info.UpsertedId, Equals, 48)402 } else {403 c.Assert(info.UpsertedId, IsNil) // Unfortunate, but that's what Mongo gave us.404 }405 err = coll.Find(M{"k": 48}).One(result)406 c.Assert(err, IsNil)407 c.Assert(result["n"], Equals, 48)408}409func (s *S) TestUpsertId(c *C) {410 session, err := mgo.Dial("localhost:40001")411 c.Assert(err, IsNil)412 defer session.Close()413 coll := session.DB("mydb").C("mycoll")414 ns := []int{40, 41, 42, 43, 44, 45, 46}415 for _, n := range ns {416 err := coll.Insert(M{"_id": n, "n": n})417 c.Assert(err, IsNil)418 }419 info, err := coll.UpsertId(42, M{"n": 24})420 c.Assert(err, IsNil)421 c.Assert(info.Updated, Equals, 1)422 c.Assert(info.UpsertedId, IsNil)423 result := M{}424 err = coll.FindId(42).One(result)425 c.Assert(err, IsNil)426 c.Assert(result["n"], Equals, 24)427 info, err = coll.UpsertId(47, M{"_id": 47, "n": 47})428 c.Assert(err, IsNil)429 c.Assert(info.Updated, Equals, 0)430 if s.versionAtLeast(2, 6) {431 c.Assert(info.UpsertedId, Equals, 47)432 } else {433 c.Assert(info.UpsertedId, IsNil)434 }435 err = coll.FindId(47).One(result)436 c.Assert(err, IsNil)437 c.Assert(result["n"], Equals, 47)438}439func (s *S) TestUpdateAll(c *C) {440 session, err := mgo.Dial("localhost:40001")441 c.Assert(err, IsNil)442 defer session.Close()443 coll := session.DB("mydb").C("mycoll")444 ns := []int{40, 41, 42, 43, 44, 45, 46}445 for _, n := range ns {446 err := coll.Insert(M{"k": n, "n": n})447 c.Assert(err, IsNil)448 }449 info, err := coll.UpdateAll(M{"k": M{"$gt": 42}}, M{"$unset": M{"missing": 1}})450 c.Assert(err, IsNil)451 if s.versionAtLeast(2, 6) {452 c.Assert(info.Updated, Equals, 0)453 c.Assert(info.Matched, Equals, 4)454 } else {455 c.Assert(info.Updated, Equals, 4)456 c.Assert(info.Matched, Equals, 4)457 }458 info, err = coll.UpdateAll(M{"k": M{"$gt": 42}}, M{"$inc": M{"n": 1}})459 c.Assert(err, IsNil)460 c.Assert(info.Updated, Equals, 4)461 c.Assert(info.Matched, Equals, 4)462 result := make(M)463 err = coll.Find(M{"k": 42}).One(result)464 c.Assert(err, IsNil)465 c.Assert(result["n"], Equals, 42)466 err = coll.Find(M{"k": 43}).One(result)467 c.Assert(err, IsNil)468 c.Assert(result["n"], Equals, 44)469 err = coll.Find(M{"k": 44}).One(result)470 c.Assert(err, IsNil)471 c.Assert(result["n"], Equals, 45)472 if !s.versionAtLeast(2, 6) {473 // 2.6 made this invalid.474 info, err = coll.UpdateAll(M{"k": 47}, M{"k": 47, "n": 47})475 c.Assert(err, Equals, nil)476 c.Assert(info.Updated, Equals, 0)477 }478}479func (s *S) TestRemove(c *C) {480 session, err := mgo.Dial("localhost:40001")481 c.Assert(err, IsNil)482 defer session.Close()483 coll := session.DB("mydb").C("mycoll")484 ns := []int{40, 41, 42, 43, 44, 45, 46}485 for _, n := range ns {486 err := coll.Insert(M{"n": n})487 c.Assert(err, IsNil)488 }489 err = coll.Remove(M{"n": M{"$gt": 42}})490 c.Assert(err, IsNil)491 result := &struct{ N int }{}492 err = coll.Find(M{"n": 42}).One(result)493 c.Assert(err, IsNil)494 c.Assert(result.N, Equals, 42)495 err = coll.Find(M{"n": 43}).One(result)496 c.Assert(err, Equals, mgo.ErrNotFound)497 err = coll.Find(M{"n": 44}).One(result)498 c.Assert(err, IsNil)499 c.Assert(result.N, Equals, 44)500}501func (s *S) TestRemoveId(c *C) {502 session, err := mgo.Dial("localhost:40001")503 c.Assert(err, IsNil)504 defer session.Close()505 coll := session.DB("mydb").C("mycoll")506 err = coll.Insert(M{"_id": 40}, M{"_id": 41}, M{"_id": 42})507 c.Assert(err, IsNil)508 err = coll.RemoveId(41)509 c.Assert(err, IsNil)510 c.Assert(coll.FindId(40).One(nil), IsNil)511 c.Assert(coll.FindId(41).One(nil), Equals, mgo.ErrNotFound)512 c.Assert(coll.FindId(42).One(nil), IsNil)513}514func (s *S) TestRemoveUnsafe(c *C) {515 session, err := mgo.Dial("localhost:40001")516 c.Assert(err, IsNil)517 defer session.Close()518 session.SetSafe(nil)519 coll := session.DB("mydb").C("mycoll")520 err = coll.Insert(M{"_id": 40}, M{"_id": 41}, M{"_id": 42})521 c.Assert(err, IsNil)522 err = coll.RemoveId(41)523 c.Assert(err, IsNil)524 c.Assert(coll.FindId(40).One(nil), IsNil)525 c.Assert(coll.FindId(41).One(nil), Equals, mgo.ErrNotFound)526 c.Assert(coll.FindId(42).One(nil), IsNil)527}528func (s *S) TestRemoveAll(c *C) {529 session, err := mgo.Dial("localhost:40001")530 c.Assert(err, IsNil)531 defer session.Close()532 coll := session.DB("mydb").C("mycoll")533 ns := []int{40, 41, 42, 43, 44, 45, 46}534 for _, n := range ns {535 err := coll.Insert(M{"n": n})536 c.Assert(err, IsNil)537 }538 info, err := coll.RemoveAll(M{"n": M{"$gt": 42}})539 c.Assert(err, IsNil)540 c.Assert(info.Updated, Equals, 0)541 c.Assert(info.Removed, Equals, 4)542 c.Assert(info.Matched, Equals, 4)543 c.Assert(info.UpsertedId, IsNil)544 result := &struct{ N int }{}545 err = coll.Find(M{"n": 42}).One(result)546 c.Assert(err, IsNil)547 c.Assert(result.N, Equals, 42)548 err = coll.Find(M{"n": 43}).One(result)549 c.Assert(err, Equals, mgo.ErrNotFound)550 err = coll.Find(M{"n": 44}).One(result)551 c.Assert(err, Equals, mgo.ErrNotFound)552 info, err = coll.RemoveAll(nil)553 c.Assert(err, IsNil)554 c.Assert(info.Updated, Equals, 0)555 c.Assert(info.Removed, Equals, 3)556 c.Assert(info.Matched, Equals, 3)557 c.Assert(info.UpsertedId, IsNil)558 n, err := coll.Find(nil).Count()559 c.Assert(err, IsNil)560 c.Assert(n, Equals, 0)561}562func (s *S) TestDropDatabase(c *C) {563 session, err := mgo.Dial("localhost:40001")564 c.Assert(err, IsNil)565 defer session.Close()566 db1 := session.DB("db1")567 db1.C("col").Insert(M{"_id": 1})568 db2 := session.DB("db2")569 db2.C("col").Insert(M{"_id": 1})570 err = db1.DropDatabase()571 c.Assert(err, IsNil)572 names, err := session.DatabaseNames()573 c.Assert(err, IsNil)574 c.Assert(filterDBs(names), DeepEquals, []string{"db2"})575 err = db2.DropDatabase()576 c.Assert(err, IsNil)577 names, err = session.DatabaseNames()578 c.Assert(err, IsNil)579 c.Assert(filterDBs(names), DeepEquals, []string{})580}581func filterDBs(dbs []string) []string {582 var i int583 for _, name := range dbs {584 switch name {585 case "admin", "local":586 default:587 dbs[i] = name588 i++589 }590 }591 if len(dbs) == 0 {592 return []string{}593 }594 return dbs[:i]595}596func (s *S) TestDropCollection(c *C) {597 session, err := mgo.Dial("localhost:40001")598 c.Assert(err, IsNil)599 defer session.Close()600 db := session.DB("db1")601 db.C("col1").Insert(M{"_id": 1})602 db.C("col2").Insert(M{"_id": 1})603 err = db.C("col1").DropCollection()604 c.Assert(err, IsNil)605 names, err := db.CollectionNames()606 c.Assert(err, IsNil)607 c.Assert(names, DeepEquals, []string{"col2", "system.indexes"})608 err = db.C("col2").DropCollection()609 c.Assert(err, IsNil)610 names, err = db.CollectionNames()611 c.Assert(err, IsNil)612 c.Assert(names, DeepEquals, []string{"system.indexes"})613}614func (s *S) TestCreateCollectionCapped(c *C) {615 session, err := mgo.Dial("localhost:40001")616 c.Assert(err, IsNil)617 defer session.Close()618 coll := session.DB("mydb").C("mycoll")619 info := &mgo.CollectionInfo{620 Capped: true,621 MaxBytes: 1024,622 MaxDocs: 3,623 }624 err = coll.Create(info)625 c.Assert(err, IsNil)626 ns := []int{1, 2, 3, 4, 5}627 for _, n := range ns {628 err := coll.Insert(M{"n": n})629 c.Assert(err, IsNil)630 }631 n, err := coll.Find(nil).Count()632 c.Assert(err, IsNil)633 c.Assert(n, Equals, 3)634}635func (s *S) TestCreateCollectionNoIndex(c *C) {636 session, err := mgo.Dial("localhost:40001")637 c.Assert(err, IsNil)638 defer session.Close()639 coll := session.DB("mydb").C("mycoll")640 info := &mgo.CollectionInfo{641 DisableIdIndex: true,642 }643 err = coll.Create(info)644 c.Assert(err, IsNil)645 err = coll.Insert(M{"n": 1})646 c.Assert(err, IsNil)647 indexes, err := coll.Indexes()648 c.Assert(indexes, HasLen, 0)649}650func (s *S) TestCreateCollectionForceIndex(c *C) {651 session, err := mgo.Dial("localhost:40001")652 c.Assert(err, IsNil)653 defer session.Close()654 coll := session.DB("mydb").C("mycoll")655 info := &mgo.CollectionInfo{656 ForceIdIndex: true,657 Capped: true,658 MaxBytes: 1024,659 }660 err = coll.Create(info)661 c.Assert(err, IsNil)662 err = coll.Insert(M{"n": 1})663 c.Assert(err, IsNil)664 indexes, err := coll.Indexes()665 c.Assert(indexes, HasLen, 1)666}667func (s *S) TestCreateCollectionValidator(c *C) {668 if !s.versionAtLeast(3, 2) {669 c.Skip("validation depends on MongoDB 3.2+")670 }671 session, err := mgo.Dial("localhost:40001")672 c.Assert(err, IsNil)673 defer session.Close()674 db := session.DB("mydb")675 coll := db.C("mycoll")676 // Test Validator.677 info := &mgo.CollectionInfo{678 Validator: M{"b": M{"$exists": true}},679 }680 err = coll.Create(info)681 c.Assert(err, IsNil)682 err = coll.Insert(M{"a": 1})683 c.Assert(err, ErrorMatches, "Document failed validation")684 err = coll.DropCollection()685 c.Assert(err, IsNil)686 // Test ValidatorAction.687 info = &mgo.CollectionInfo{688 Validator: M{"b": M{"$exists": true}},689 ValidationAction: "warn",690 }691 err = coll.Create(info)692 c.Assert(err, IsNil)693 err = coll.Insert(M{"a": 1})694 c.Assert(err, IsNil)695 err = coll.DropCollection()696 c.Assert(err, IsNil)697 // Test ValidationLevel.698 info = &mgo.CollectionInfo{699 Validator: M{"a": M{"$exists": true}},700 ValidationLevel: "moderate",701 }702 err = coll.Create(info)703 err = coll.Insert(M{"a": 1})704 c.Assert(err, IsNil)705 err = db.Run(bson.D{{"collMod", "mycoll"}, {"validator", M{"b": M{"$exists": true}}}}, nil)706 c.Assert(err, IsNil)707 err = coll.Insert(M{"a": 2})708 c.Assert(err, ErrorMatches, "Document failed validation")709 err = coll.Update(M{"a": 1}, M{"c": 1})710 c.Assert(err, IsNil)711 err = coll.DropCollection()712 c.Assert(err, IsNil)713}714func (s *S) TestCreateCollectionStorageEngine(c *C) {715 if !s.versionAtLeast(3, 0) {716 c.Skip("storageEngine option depends on MongoDB 3.0+")717 }718 session, err := mgo.Dial("localhost:40001")719 c.Assert(err, IsNil)720 defer session.Close()721 db := session.DB("mydb")722 coll := db.C("mycoll")723 info := &mgo.CollectionInfo{724 StorageEngine: M{"test": M{}},725 }726 err = coll.Create(info)727 c.Assert(err, ErrorMatches, "test is not a registered storage engine for this server")728}729func (s *S) TestIsDupValues(c *C) {730 c.Assert(mgo.IsDup(nil), Equals, false)731 c.Assert(mgo.IsDup(&mgo.LastError{Code: 1}), Equals, false)732 c.Assert(mgo.IsDup(&mgo.QueryError{Code: 1}), Equals, false)733 c.Assert(mgo.IsDup(&mgo.LastError{Code: 11000}), Equals, true)734 c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11000}), Equals, true)735 c.Assert(mgo.IsDup(&mgo.LastError{Code: 11001}), Equals, true)736 c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11001}), Equals, true)737 c.Assert(mgo.IsDup(&mgo.LastError{Code: 12582}), Equals, true)738 c.Assert(mgo.IsDup(&mgo.QueryError{Code: 12582}), Equals, true)739 lerr := &mgo.LastError{Code: 16460, Err: "error inserting 1 documents to shard ... caused by :: E11000 duplicate key error index: ..."}740 c.Assert(mgo.IsDup(lerr), Equals, true)741}742func (s *S) TestIsDupPrimary(c *C) {743 session, err := mgo.Dial("localhost:40001")744 c.Assert(err, IsNil)745 defer session.Close()746 coll := session.DB("mydb").C("mycoll")747 err = coll.Insert(M{"_id": 1})748 c.Assert(err, IsNil)749 err = coll.Insert(M{"_id": 1})750 c.Assert(err, ErrorMatches, ".*duplicate key error.*")751 c.Assert(mgo.IsDup(err), Equals, true)752}753func (s *S) TestIsDupUnique(c *C) {754 session, err := mgo.Dial("localhost:40001")755 c.Assert(err, IsNil)756 defer session.Close()757 index := mgo.Index{758 Key: []string{"a", "b"},759 Unique: true,760 }761 coll := session.DB("mydb").C("mycoll")762 err = coll.EnsureIndex(index)763 c.Assert(err, IsNil)764 err = coll.Insert(M{"a": 1, "b": 1})765 c.Assert(err, IsNil)766 err = coll.Insert(M{"a": 1, "b": 1})767 c.Assert(err, ErrorMatches, ".*duplicate key error.*")768 c.Assert(mgo.IsDup(err), Equals, true)769}770func (s *S) TestIsDupCapped(c *C) {771 session, err := mgo.Dial("localhost:40001")772 c.Assert(err, IsNil)773 defer session.Close()774 coll := session.DB("mydb").C("mycoll")775 info := &mgo.CollectionInfo{776 ForceIdIndex: true,777 Capped: true,778 MaxBytes: 1024,779 }780 err = coll.Create(info)781 c.Assert(err, IsNil)782 err = coll.Insert(M{"_id": 1})783 c.Assert(err, IsNil)784 err = coll.Insert(M{"_id": 1})785 // The error was different for capped collections before 2.6.786 c.Assert(err, ErrorMatches, ".*duplicate key.*")787 // The issue is reduced by using IsDup.788 c.Assert(mgo.IsDup(err), Equals, true)789}790func (s *S) TestIsDupFindAndModify(c *C) {791 session, err := mgo.Dial("localhost:40001")792 c.Assert(err, IsNil)793 defer session.Close()794 coll := session.DB("mydb").C("mycoll")795 err = coll.EnsureIndex(mgo.Index{Key: []string{"n"}, Unique: true})796 c.Assert(err, IsNil)797 err = coll.Insert(M{"n": 1})798 c.Assert(err, IsNil)799 err = coll.Insert(M{"n": 2})800 c.Assert(err, IsNil)801 _, err = coll.Find(M{"n": 1}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, bson.M{})802 c.Assert(err, ErrorMatches, ".*duplicate key error.*")803 c.Assert(mgo.IsDup(err), Equals, true)804}805func (s *S) TestIsDupRetryUpsert(c *C) {806 session, err := mgo.Dial("localhost:40001")807 c.Assert(err, IsNil)808 defer session.Close()809 coll := session.DB("mydb").C("mycoll")810 err = coll.Insert(bson.M{"_id": 1, "x": 1})811 c.Assert(err, IsNil)812 _, err = coll.Upsert(bson.M{"_id": 1, "x": 2}, bson.M{"$set": bson.M{"x": 3}})813 c.Assert(mgo.IsDup(err), Equals, true)814 _, err = coll.Find(bson.M{"_id": 1, "x": 2}).Apply(mgo.Change{815 Update: bson.M{"$set": bson.M{"x": 3}},816 Upsert: true,817 }, nil)818 c.Assert(mgo.IsDup(err), Equals, true)819}820func (s *S) TestFindAndModify(c *C) {821 session, err := mgo.Dial("localhost:40011")822 c.Assert(err, IsNil)823 defer session.Close()824 coll := session.DB("mydb").C("mycoll")825 err = coll.Insert(M{"n": 42})826 session.SetMode(mgo.Monotonic, true)827 result := M{}828 info, err := coll.Find(M{"n": 42}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result)829 c.Assert(err, IsNil)830 c.Assert(result["n"], Equals, 42)831 c.Assert(info.Updated, Equals, 1)832 c.Assert(info.Matched, Equals, 1)833 c.Assert(info.Removed, Equals, 0)834 c.Assert(info.UpsertedId, IsNil)835 // A nil result parameter should be acceptable.836 info, err = coll.Find(M{"n": 43}).Apply(mgo.Change{Update: M{"$unset": M{"missing": 1}}}, nil)837 c.Assert(err, IsNil)838 c.Assert(info.Updated, Equals, 1) // On 2.6+ this feels like a server mistake.839 c.Assert(info.Matched, Equals, 1)840 c.Assert(info.Removed, Equals, 0)841 c.Assert(info.UpsertedId, IsNil)842 result = M{}843 info, err = coll.Find(M{"n": 43}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result)844 c.Assert(err, IsNil)845 c.Assert(result["n"], Equals, 44)846 c.Assert(info.Updated, Equals, 1)847 c.Assert(info.Removed, Equals, 0)848 c.Assert(info.UpsertedId, IsNil)849 result = M{}850 info, err = coll.Find(M{"n": 50}).Apply(mgo.Change{Upsert: true, Update: M{"n": 51, "o": 52}}, result)851 c.Assert(err, IsNil)852 c.Assert(result["n"], IsNil)853 c.Assert(info.Updated, Equals, 0)854 c.Assert(info.Removed, Equals, 0)855 c.Assert(info.UpsertedId, NotNil)856 result = M{}857 info, err = coll.Find(nil).Sort("-n").Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result)858 c.Assert(err, IsNil)859 c.Assert(result["n"], Equals, 52)860 c.Assert(info.Updated, Equals, 1)861 c.Assert(info.Removed, Equals, 0)862 c.Assert(info.UpsertedId, IsNil)863 result = M{}864 info, err = coll.Find(M{"n": 52}).Select(M{"o": 1}).Apply(mgo.Change{Remove: true}, result)865 c.Assert(err, IsNil)866 c.Assert(result["n"], IsNil)867 c.Assert(result["o"], Equals, 52)868 c.Assert(info.Updated, Equals, 0)869 c.Assert(info.Removed, Equals, 1)870 c.Assert(info.UpsertedId, IsNil)871 result = M{}872 info, err = coll.Find(M{"n": 60}).Apply(mgo.Change{Remove: true}, result)873 c.Assert(err, Equals, mgo.ErrNotFound)874 c.Assert(len(result), Equals, 0)875 c.Assert(info, IsNil)876}877func (s *S) TestFindAndModifyBug997828(c *C) {878 session, err := mgo.Dial("localhost:40001")879 c.Assert(err, IsNil)880 defer session.Close()881 coll := session.DB("mydb").C("mycoll")882 err = coll.Insert(M{"n": "not-a-number"})883 result := make(M)884 _, err = coll.Find(M{"n": "not-a-number"}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result)885 c.Assert(err, ErrorMatches, `(exception: )?Cannot apply \$inc .*`)886 if s.versionAtLeast(2, 1) {887 qerr, _ := err.(*mgo.QueryError)888 c.Assert(qerr, NotNil, Commentf("err: %#v", err))889 if s.versionAtLeast(2, 6) {890 // Oh, the dance of error codes. :-(891 c.Assert(qerr.Code, Equals, 16837)892 } else {893 c.Assert(qerr.Code, Equals, 10140)894 }895 } else {896 lerr, _ := err.(*mgo.LastError)897 c.Assert(lerr, NotNil, Commentf("err: %#v", err))898 c.Assert(lerr.Code, Equals, 10140)899 }900}901func (s *S) TestFindAndModifyErrmsgDoc(c *C) {902 session, err := mgo.Dial("localhost:40001")903 c.Assert(err, IsNil)904 defer session.Close()905 coll := session.DB("mydb").C("mycoll")906 err = coll.Insert(M{"errmsg": "an error"})907 var result M908 _, err = coll.Find(M{}).Apply(mgo.Change{Update: M{"$set": M{"n": 1}}}, &result)909 c.Assert(err, IsNil)910}911func (s *S) TestCountCollection(c *C) {912 session, err := mgo.Dial("localhost:40001")913 c.Assert(err, IsNil)914 defer session.Close()915 coll := session.DB("mydb").C("mycoll")916 ns := []int{40, 41, 42}917 for _, n := range ns {918 err := coll.Insert(M{"n": n})919 c.Assert(err, IsNil)920 }921 n, err := coll.Count()922 c.Assert(err, IsNil)923 c.Assert(n, Equals, 3)924}925func (s *S) TestCountQuery(c *C) {926 session, err := mgo.Dial("localhost:40001")927 c.Assert(err, IsNil)928 defer session.Close()929 coll := session.DB("mydb").C("mycoll")930 ns := []int{40, 41, 42}931 for _, n := range ns {932 err := coll.Insert(M{"n": n})933 c.Assert(err, IsNil)934 }935 n, err := coll.Find(M{"n": M{"$gt": 40}}).Count()936 c.Assert(err, IsNil)937 c.Assert(n, Equals, 2)938}939func (s *S) TestCountQuerySorted(c *C) {940 session, err := mgo.Dial("localhost:40001")941 c.Assert(err, IsNil)942 defer session.Close()943 coll := session.DB("mydb").C("mycoll")944 ns := []int{40, 41, 42}945 for _, n := range ns {946 err := coll.Insert(M{"n": n})947 c.Assert(err, IsNil)948 }949 n, err := coll.Find(M{"n": M{"$gt": 40}}).Sort("n").Count()950 c.Assert(err, IsNil)951 c.Assert(n, Equals, 2)952}953func (s *S) TestCountSkipLimit(c *C) {954 session, err := mgo.Dial("localhost:40001")955 c.Assert(err, IsNil)956 defer session.Close()957 coll := session.DB("mydb").C("mycoll")958 ns := []int{40, 41, 42, 43, 44}959 for _, n := range ns {960 err := coll.Insert(M{"n": n})961 c.Assert(err, IsNil)962 }963 n, err := coll.Find(nil).Skip(1).Limit(3).Count()964 c.Assert(err, IsNil)965 c.Assert(n, Equals, 3)966 n, err = coll.Find(nil).Skip(1).Limit(5).Count()967 c.Assert(err, IsNil)968 c.Assert(n, Equals, 4)969}970func (s *S) TestQueryExplain(c *C) {971 session, err := mgo.Dial("localhost:40001")972 c.Assert(err, IsNil)973 defer session.Close()974 coll := session.DB("mydb").C("mycoll")975 ns := []int{40, 41, 42}976 for _, n := range ns {977 err := coll.Insert(M{"n": n})978 c.Assert(err, IsNil)979 }980 m := M{}981 query := coll.Find(nil).Limit(2)982 err = query.Explain(m)983 c.Assert(err, IsNil)984 if m["queryPlanner"] != nil {985 c.Assert(m["executionStats"].(M)["totalDocsExamined"], Equals, 2)986 } else {987 c.Assert(m["cursor"], Equals, "BasicCursor")988 c.Assert(m["nscanned"], Equals, 2)989 c.Assert(m["n"], Equals, 2)990 }991 n := 0992 var result M993 iter := query.Iter()994 for iter.Next(&result) {995 n++996 }997 c.Assert(iter.Close(), IsNil)998 c.Assert(n, Equals, 2)999}1000func (s *S) TestQuerySetMaxScan(c *C) {1001 session, err := mgo.Dial("localhost:40001")1002 c.Assert(err, IsNil)1003 defer session.Close()1004 coll := session.DB("mydb").C("mycoll")1005 ns := []int{40, 41, 42}1006 for _, n := range ns {1007 err := coll.Insert(M{"n": n})1008 c.Assert(err, IsNil)1009 }1010 query := coll.Find(nil).SetMaxScan(2)1011 var result []M1012 err = query.All(&result)1013 c.Assert(err, IsNil)1014 c.Assert(result, HasLen, 2)1015}1016func (s *S) TestQuerySetMaxTime(c *C) {1017 if !s.versionAtLeast(2, 6) {1018 c.Skip("SetMaxTime only supported in 2.6+")1019 }1020 session, err := mgo.Dial("localhost:40001")1021 c.Assert(err, IsNil)1022 defer session.Close()1023 coll := session.DB("mydb").C("mycoll")1024 for i := 0; i < 1000; i++ {1025 err := coll.Insert(M{"n": i})1026 c.Assert(err, IsNil)1027 }1028 query := coll.Find(nil)1029 query.SetMaxTime(1 * time.Millisecond)1030 query.Batch(2)1031 var result []M1032 err = query.All(&result)1033 c.Assert(err, ErrorMatches, "operation exceeded time limit")1034}1035func (s *S) TestQueryHint(c *C) {1036 session, err := mgo.Dial("localhost:40001")1037 c.Assert(err, IsNil)1038 defer session.Close()1039 coll := session.DB("mydb").C("mycoll")1040 coll.EnsureIndexKey("a")1041 m := M{}1042 err = coll.Find(nil).Hint("a").Explain(m)1043 c.Assert(err, IsNil)1044 if m["queryPlanner"] != nil {1045 m = m["queryPlanner"].(M)1046 m = m["winningPlan"].(M)1047 m = m["inputStage"].(M)1048 c.Assert(m["indexName"], Equals, "a_1")1049 } else {1050 c.Assert(m["indexBounds"], NotNil)1051 c.Assert(m["indexBounds"].(M)["a"], NotNil)1052 }1053}1054func (s *S) TestQueryComment(c *C) {1055 session, err := mgo.Dial("localhost:40001")1056 c.Assert(err, IsNil)1057 defer session.Close()1058 db := session.DB("mydb")1059 coll := db.C("mycoll")1060 err = db.Run(bson.M{"profile": 2}, nil)1061 c.Assert(err, IsNil)1062 ns := []int{40, 41, 42}1063 for _, n := range ns {1064 err := coll.Insert(M{"n": n})1065 c.Assert(err, IsNil)1066 }1067 query := coll.Find(bson.M{"n": 41})1068 query.Comment("some comment")1069 err = query.One(nil)1070 c.Assert(err, IsNil)1071 query = coll.Find(bson.M{"n": 41})1072 query.Comment("another comment")1073 err = query.One(nil)1074 c.Assert(err, IsNil)1075 commentField := "query.$comment"1076 nField := "query.$query.n"1077 if s.versionAtLeast(3, 2) {1078 commentField = "query.comment"1079 nField = "query.filter.n"1080 }1081 n, err := session.DB("mydb").C("system.profile").Find(bson.M{nField: 41, commentField: "some comment"}).Count()1082 c.Assert(err, IsNil)1083 c.Assert(n, Equals, 1)1084}1085func (s *S) TestFindOneNotFound(c *C) {1086 session, err := mgo.Dial("localhost:40001")1087 c.Assert(err, IsNil)1088 defer session.Close()1089 coll := session.DB("mydb").C("mycoll")1090 result := struct{ A, B int }{}1091 err = coll.Find(M{"a": 1}).One(&result)1092 c.Assert(err, Equals, mgo.ErrNotFound)1093 c.Assert(err, ErrorMatches, "not found")1094 c.Assert(err == mgo.ErrNotFound, Equals, true)1095}1096func (s *S) TestFindIterNotFound(c *C) {1097 session, err := mgo.Dial("localhost:40001")1098 c.Assert(err, IsNil)1099 defer session.Close()1100 coll := session.DB("mydb").C("mycoll")1101 result := struct{ A, B int }{}1102 iter := coll.Find(M{"a": 1}).Iter()1103 ok := iter.Next(&result)1104 c.Assert(ok, Equals, false)1105 c.Assert(iter.Err(), IsNil)1106}1107func (s *S) TestFindNil(c *C) {1108 session, err := mgo.Dial("localhost:40001")1109 c.Assert(err, IsNil)1110 defer session.Close()1111 coll := session.DB("mydb").C("mycoll")1112 err = coll.Insert(M{"n": 1})1113 c.Assert(err, IsNil)1114 result := struct{ N int }{}1115 err = coll.Find(nil).One(&result)1116 c.Assert(err, IsNil)1117 c.Assert(result.N, Equals, 1)1118}1119func (s *S) TestFindId(c *C) {1120 session, err := mgo.Dial("localhost:40001")1121 c.Assert(err, IsNil)1122 defer session.Close()1123 coll := session.DB("mydb").C("mycoll")1124 err = coll.Insert(M{"_id": 41, "n": 41})1125 c.Assert(err, IsNil)1126 err = coll.Insert(M{"_id": 42, "n": 42})1127 c.Assert(err, IsNil)1128 result := struct{ N int }{}1129 err = coll.FindId(42).One(&result)1130 c.Assert(err, IsNil)1131 c.Assert(result.N, Equals, 42)1132}1133func (s *S) TestFindIterAll(c *C) {1134 session, err := mgo.Dial("localhost:40001")1135 c.Assert(err, IsNil)1136 defer session.Close()1137 coll := session.DB("mydb").C("mycoll")1138 ns := []int{40, 41, 42, 43, 44, 45, 46}1139 for _, n := range ns {1140 coll.Insert(M{"n": n})1141 }1142 session.Refresh() // Release socket.1143 mgo.ResetStats()1144 iter := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2).Iter()1145 result := struct{ N int }{}1146 for i := 2; i < 7; i++ {1147 ok := iter.Next(&result)1148 c.Assert(ok, Equals, true, Commentf("err=%v", err))1149 c.Assert(result.N, Equals, ns[i])1150 if i == 1 {1151 stats := mgo.GetStats()1152 c.Assert(stats.ReceivedDocs, Equals, 2)1153 }1154 }1155 ok := iter.Next(&result)1156 c.Assert(ok, Equals, false)1157 c.Assert(iter.Close(), IsNil)1158 session.Refresh() // Release socket.1159 stats := mgo.GetStats()1160 c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP1161 c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.1162 if s.versionAtLeast(3, 2) {1163 // In 3.2+ responses come in batches inside the op reply docs.1164 c.Assert(stats.ReceivedDocs, Equals, 3)1165 } else {1166 c.Assert(stats.ReceivedDocs, Equals, 5)1167 }1168 c.Assert(stats.SocketsInUse, Equals, 0)1169}1170func (s *S) TestFindIterTwiceWithSameQuery(c *C) {1171 session, err := mgo.Dial("localhost:40001")1172 c.Assert(err, IsNil)1173 defer session.Close()1174 coll := session.DB("mydb").C("mycoll")1175 for i := 40; i != 47; i++ {1176 err := coll.Insert(M{"n": i})1177 c.Assert(err, IsNil)1178 }1179 query := coll.Find(M{}).Sort("n")1180 iter1 := query.Skip(1).Iter()1181 iter2 := query.Skip(2).Iter()1182 var result struct{ N int }1183 ok := iter2.Next(&result)1184 c.Assert(ok, Equals, true)1185 c.Assert(result.N, Equals, 42)1186 ok = iter1.Next(&result)1187 c.Assert(ok, Equals, true)1188 c.Assert(result.N, Equals, 41)1189}1190func (s *S) TestFindIterWithoutResults(c *C) {1191 session, err := mgo.Dial("localhost:40001")1192 c.Assert(err, IsNil)1193 defer session.Close()1194 coll := session.DB("mydb").C("mycoll")1195 coll.Insert(M{"n": 42})1196 iter := coll.Find(M{"n": 0}).Iter()1197 result := struct{ N int }{}1198 ok := iter.Next(&result)1199 c.Assert(ok, Equals, false)1200 c.Assert(iter.Close(), IsNil)1201 c.Assert(result.N, Equals, 0)1202}1203func (s *S) TestFindIterLimit(c *C) {1204 session, err := mgo.Dial("localhost:40001")1205 c.Assert(err, IsNil)1206 defer session.Close()1207 coll := session.DB("mydb").C("mycoll")1208 ns := []int{40, 41, 42, 43, 44, 45, 46}1209 for _, n := range ns {1210 err := coll.Insert(M{"n": n})1211 c.Assert(err, IsNil)1212 }1213 session.Refresh() // Release socket.1214 mgo.ResetStats()1215 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3)1216 iter := query.Iter()1217 result := struct{ N int }{}1218 for i := 2; i < 5; i++ {1219 ok := iter.Next(&result)1220 c.Assert(ok, Equals, true)1221 c.Assert(result.N, Equals, ns[i])1222 }1223 ok := iter.Next(&result)1224 c.Assert(ok, Equals, false)1225 c.Assert(iter.Close(), IsNil)1226 session.Refresh() // Release socket.1227 stats := mgo.GetStats()1228 if s.versionAtLeast(3, 2) {1229 // Limit works properly in 3.2+, and results are batched in single doc.1230 c.Assert(stats.SentOps, Equals, 1) // 1*QUERY_OP1231 c.Assert(stats.ReceivedOps, Equals, 1) // and its REPLY_OP1232 c.Assert(stats.ReceivedDocs, Equals, 1)1233 } else {1234 c.Assert(stats.SentOps, Equals, 2) // 1*QUERY_OP + 1*KILL_CURSORS_OP1235 c.Assert(stats.ReceivedOps, Equals, 1) // and its REPLY_OP1236 c.Assert(stats.ReceivedDocs, Equals, 3)1237 }1238 c.Assert(stats.SocketsInUse, Equals, 0)1239}1240var cursorTimeout = flag.Bool("cursor-timeout", false, "Enable cursor timeout test")1241func (s *S) TestFindIterCursorTimeout(c *C) {1242 if !*cursorTimeout {1243 c.Skip("-cursor-timeout")1244 }1245 session, err := mgo.Dial("localhost:40001")1246 c.Assert(err, IsNil)1247 defer session.Close()1248 type Doc struct {1249 Id int "_id"1250 }1251 coll := session.DB("test").C("test")1252 coll.Remove(nil)1253 for i := 0; i < 100; i++ {1254 err = coll.Insert(Doc{i})1255 c.Assert(err, IsNil)1256 }1257 session.SetBatch(1)1258 iter := coll.Find(nil).Iter()1259 var doc Doc1260 if !iter.Next(&doc) {1261 c.Fatalf("iterator failed to return any documents")1262 }1263 for i := 10; i > 0; i-- {1264 c.Logf("Sleeping... %d minutes to go...", i)1265 time.Sleep(1*time.Minute + 2*time.Second)1266 }1267 // Drain any existing documents that were fetched.1268 if !iter.Next(&doc) {1269 c.Fatalf("iterator with timed out cursor failed to return previously cached document")1270 }1271 if iter.Next(&doc) {1272 c.Fatalf("timed out cursor returned document")1273 }1274 c.Assert(iter.Err(), Equals, mgo.ErrCursor)1275}1276func (s *S) TestTooManyItemsLimitBug(c *C) {1277 if *fast {1278 c.Skip("-fast")1279 }1280 session, err := mgo.Dial("localhost:40001")1281 c.Assert(err, IsNil)1282 defer session.Close()1283 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))1284 mgo.SetDebug(false)1285 coll := session.DB("mydb").C("mycoll")1286 words := strings.Split("foo bar baz", " ")1287 for i := 0; i < 5; i++ {1288 words = append(words, words...)1289 }1290 doc := bson.D{{"words", words}}1291 inserts := 100001292 limit := 50001293 iters := 01294 c.Assert(inserts > limit, Equals, true)1295 for i := 0; i < inserts; i++ {1296 err := coll.Insert(&doc)1297 c.Assert(err, IsNil)1298 }1299 iter := coll.Find(nil).Limit(limit).Iter()1300 for iter.Next(&doc) {1301 if iters%100 == 0 {1302 c.Logf("Seen %d docments", iters)1303 }1304 iters++1305 }1306 c.Assert(iter.Close(), IsNil)1307 c.Assert(iters, Equals, limit)1308}1309func (s *S) TestBatchSizeZeroGetMore(c *C) {1310 if *fast {1311 c.Skip("-fast")1312 }1313 session, err := mgo.Dial("localhost:40001")1314 c.Assert(err, IsNil)1315 defer session.Close()1316 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))1317 mgo.SetDebug(false)1318 coll := session.DB("mydb").C("mycoll")1319 words := strings.Split("foo bar baz", " ")1320 for i := 0; i < 5; i++ {1321 words = append(words, words...)1322 }1323 doc := bson.D{{"words", words}}1324 inserts := 100001325 iters := 01326 for i := 0; i < inserts; i++ {1327 err := coll.Insert(&doc)1328 c.Assert(err, IsNil)1329 }1330 iter := coll.Find(nil).Iter()1331 for iter.Next(&doc) {1332 if iters%100 == 0 {1333 c.Logf("Seen %d docments", iters)1334 }1335 iters++1336 }1337 c.Assert(iter.Close(), IsNil)1338}1339func serverCursorsOpen(session *mgo.Session) int {1340 var result struct {1341 Cursors struct {1342 TotalOpen int `bson:"totalOpen"`1343 TimedOut int `bson:"timedOut"`1344 }1345 }1346 err := session.Run("serverStatus", &result)1347 if err != nil {1348 panic(err)1349 }1350 return result.Cursors.TotalOpen1351}1352func (s *S) TestFindIterLimitWithMore(c *C) {1353 session, err := mgo.Dial("localhost:40001")1354 c.Assert(err, IsNil)1355 defer session.Close()1356 coll := session.DB("mydb").C("mycoll")1357 // Insane amounts of logging otherwise due to the1358 // amount of data being shuffled.1359 mgo.SetDebug(false)1360 defer mgo.SetDebug(true)1361 // Should amount to more than 4MB bson payload,1362 // the default limit per result chunk.1363 const total = 40961364 var d struct{ A [1024]byte }1365 docs := make([]interface{}, total)1366 for i := 0; i < total; i++ {1367 docs[i] = &d1368 }1369 err = coll.Insert(docs...)1370 c.Assert(err, IsNil)1371 n, err := coll.Count()1372 c.Assert(err, IsNil)1373 c.Assert(n, Equals, total)1374 // First, try restricting to a single chunk with a negative limit.1375 nresults := 01376 iter := coll.Find(nil).Limit(-total).Iter()1377 var discard struct{}1378 for iter.Next(&discard) {1379 nresults++1380 }1381 if nresults < total/2 || nresults >= total {1382 c.Fatalf("Bad result size with negative limit: %d", nresults)1383 }1384 cursorsOpen := serverCursorsOpen(session)1385 // Try again, with a positive limit. Should reach the end now,1386 // using multiple chunks.1387 nresults = 01388 iter = coll.Find(nil).Limit(total).Iter()1389 for iter.Next(&discard) {1390 nresults++1391 }1392 c.Assert(nresults, Equals, total)1393 // Ensure the cursor used is properly killed.1394 c.Assert(serverCursorsOpen(session), Equals, cursorsOpen)1395 // Edge case, -MinInt == -MinInt.1396 nresults = 01397 iter = coll.Find(nil).Limit(math.MinInt32).Iter()1398 for iter.Next(&discard) {1399 nresults++1400 }1401 if nresults < total/2 || nresults >= total {1402 c.Fatalf("Bad result size with MinInt32 limit: %d", nresults)1403 }1404}1405func (s *S) TestFindIterLimitWithBatch(c *C) {1406 session, err := mgo.Dial("localhost:40001")1407 c.Assert(err, IsNil)1408 defer session.Close()1409 coll := session.DB("mydb").C("mycoll")1410 ns := []int{40, 41, 42, 43, 44, 45, 46}1411 for _, n := range ns {1412 coll.Insert(M{"n": n})1413 }1414 // Ping the database to ensure the nonce has been received already.1415 c.Assert(session.Ping(), IsNil)1416 session.Refresh() // Release socket.1417 mgo.ResetStats()1418 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3).Batch(2)1419 iter := query.Iter()1420 result := struct{ N int }{}1421 for i := 2; i < 5; i++ {1422 ok := iter.Next(&result)1423 c.Assert(ok, Equals, true)1424 c.Assert(result.N, Equals, ns[i])1425 if i == 3 {1426 stats := mgo.GetStats()1427 if s.versionAtLeast(3, 2) {1428 // In 3.2+ responses come in batches inside the op reply docs.1429 c.Assert(stats.ReceivedDocs, Equals, 1)1430 } else {1431 c.Assert(stats.ReceivedDocs, Equals, 2)1432 }1433 }1434 }1435 ok := iter.Next(&result)1436 c.Assert(ok, Equals, false)1437 c.Assert(iter.Close(), IsNil)1438 session.Refresh() // Release socket.1439 stats := mgo.GetStats()1440 if s.versionAtLeast(3, 2) {1441 // In 3.2+ limit works properly even with multiple batches..1442 c.Assert(stats.SentOps, Equals, 2) // 1*QUERY_OP + 1*GET_MORE_OP1443 c.Assert(stats.ReceivedOps, Equals, 2) // and its REPLY_OPs1444 // In 3.2+ responses come in batches inside the op reply docs.1445 c.Assert(stats.ReceivedDocs, Equals, 2)1446 } else {1447 c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 1*GET_MORE_OP + 1*KILL_CURSORS_OP1448 c.Assert(stats.ReceivedOps, Equals, 2) // and its REPLY_OPs1449 c.Assert(stats.ReceivedDocs, Equals, 3)1450 }1451 c.Assert(stats.SocketsInUse, Equals, 0)1452}1453func (s *S) TestFindIterSortWithBatch(c *C) {1454 session, err := mgo.Dial("localhost:40001")1455 c.Assert(err, IsNil)1456 defer session.Close()1457 coll := session.DB("mydb").C("mycoll")1458 ns := []int{40, 41, 42, 43, 44, 45, 46}1459 for _, n := range ns {1460 coll.Insert(M{"n": n})1461 }1462 // Without this, the logic above breaks because Mongo refuses to1463 // return a cursor with an in-memory sort.1464 coll.EnsureIndexKey("n")1465 // Ping the database to ensure the nonce has been received already.1466 c.Assert(session.Ping(), IsNil)1467 session.Refresh() // Release socket.1468 mgo.ResetStats()1469 query := coll.Find(M{"n": M{"$lte": 44}}).Sort("-n").Batch(2)1470 iter := query.Iter()1471 ns = []int{46, 45, 44, 43, 42, 41, 40}1472 result := struct{ N int }{}1473 for i := 2; i < len(ns); i++ {1474 c.Logf("i=%d", i)1475 ok := iter.Next(&result)1476 c.Assert(ok, Equals, true)1477 c.Assert(result.N, Equals, ns[i])1478 if i == 3 {1479 stats := mgo.GetStats()1480 if s.versionAtLeast(3, 2) {1481 // Find command in 3.2+ bundles batches in a single document.1482 c.Assert(stats.ReceivedDocs, Equals, 1)1483 } else {1484 c.Assert(stats.ReceivedDocs, Equals, 2)1485 }1486 }1487 }1488 ok := iter.Next(&result)1489 c.Assert(ok, Equals, false)1490 c.Assert(iter.Close(), IsNil)1491 session.Refresh() // Release socket.1492 stats := mgo.GetStats()1493 c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP1494 c.Assert(stats.ReceivedOps, Equals, 3) // and its REPLY_OPs1495 if s.versionAtLeast(3, 2) {1496 // Find command in 3.2+ bundles batches in a single document.1497 c.Assert(stats.ReceivedDocs, Equals, 3)1498 } else {1499 c.Assert(stats.ReceivedDocs, Equals, 5)1500 }1501 c.Assert(stats.SocketsInUse, Equals, 0)1502}1503// Test tailable cursors in a situation where Next has to sleep to1504// respect the timeout requested on Tail.1505func (s *S) TestFindTailTimeoutWithSleep(c *C) {1506 if *fast {1507 c.Skip("-fast")1508 }1509 session, err := mgo.Dial("localhost:40001")1510 c.Assert(err, IsNil)1511 defer session.Close()1512 cresult := struct{ ErrMsg string }{}1513 db := session.DB("mydb")1514 err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)1515 c.Assert(err, IsNil)1516 c.Assert(cresult.ErrMsg, Equals, "")1517 coll := db.C("mycoll")1518 ns := []int{40, 41, 42, 43, 44, 45, 46}1519 for _, n := range ns {1520 coll.Insert(M{"n": n})1521 }1522 session.Refresh() // Release socket.1523 mgo.ResetStats()1524 timeout := 5 * time.Second1525 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)1526 iter := query.Tail(timeout)1527 n := len(ns)1528 result := struct{ N int }{}1529 for i := 2; i != n; i++ {1530 ok := iter.Next(&result)1531 c.Assert(ok, Equals, true)1532 c.Assert(iter.Err(), IsNil)1533 c.Assert(iter.Timeout(), Equals, false)1534 c.Assert(result.N, Equals, ns[i])1535 if i == 3 { // The batch boundary.1536 stats := mgo.GetStats()1537 c.Assert(stats.ReceivedDocs, Equals, 2)1538 }1539 }1540 mgo.ResetStats()1541 // The following call to Next will block.1542 done := make(chan bool)1543 defer func() { <-done }()1544 go func() {1545 // The internal AwaitData timing of MongoDB is around 2 seconds,1546 // so this should force mgo to sleep at least once by itself to1547 // respect the requested timeout.1548 c.Logf("[GOROUTINE] Starting and sleeping...")1549 time.Sleep(timeout - 2*time.Second)1550 c.Logf("[GOROUTINE] Woke up...")1551 session := session.New()1552 c.Logf("[GOROUTINE] Session created and will insert...")1553 err := coll.Insert(M{"n": 47})1554 c.Logf("[GOROUTINE] Insert attempted, err=%v...", err)1555 session.Close()1556 c.Logf("[GOROUTINE] Session closed.")1557 c.Check(err, IsNil)1558 done <- true1559 }()1560 c.Log("Will wait for Next with N=47...")1561 ok := iter.Next(&result)1562 c.Log("Next unblocked...")1563 c.Assert(ok, Equals, true)1564 c.Assert(iter.Err(), IsNil)1565 c.Assert(iter.Timeout(), Equals, false)1566 c.Assert(result.N, Equals, 47)1567 c.Log("Got Next with N=47!")1568 c.Log("Will wait for a result which will never come...")1569 started := time.Now()1570 ok = iter.Next(&result)1571 c.Assert(ok, Equals, false)1572 c.Assert(iter.Err(), IsNil)1573 c.Assert(iter.Timeout(), Equals, true)1574 c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)1575 c.Log("Will now reuse the timed out tail cursor...")1576 coll.Insert(M{"n": 48})1577 ok = iter.Next(&result)1578 c.Assert(ok, Equals, true)1579 c.Assert(iter.Close(), IsNil)1580 c.Assert(iter.Timeout(), Equals, false)1581 c.Assert(result.N, Equals, 48)1582}1583// Test tailable cursors in a situation where Next never gets to sleep once1584// to respect the timeout requested on Tail.1585func (s *S) TestFindTailTimeoutNoSleep(c *C) {1586 session, err := mgo.Dial("localhost:40001")1587 c.Assert(err, IsNil)1588 defer session.Close()1589 cresult := struct{ ErrMsg string }{}1590 db := session.DB("mydb")1591 err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)1592 c.Assert(err, IsNil)1593 c.Assert(cresult.ErrMsg, Equals, "")1594 coll := db.C("mycoll")1595 ns := []int{40, 41, 42, 43, 44, 45, 46}1596 for _, n := range ns {1597 coll.Insert(M{"n": n})1598 }1599 session.Refresh() // Release socket.1600 mgo.ResetStats()1601 timeout := 1 * time.Second1602 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)1603 iter := query.Tail(timeout)1604 n := len(ns)1605 result := struct{ N int }{}1606 for i := 2; i != n; i++ {1607 ok := iter.Next(&result)1608 c.Assert(ok, Equals, true)1609 c.Assert(iter.Err(), IsNil)1610 c.Assert(iter.Timeout(), Equals, false)1611 c.Assert(result.N, Equals, ns[i])1612 if i == 3 { // The batch boundary.1613 stats := mgo.GetStats()1614 c.Assert(stats.ReceivedDocs, Equals, 2)1615 }1616 }1617 // The following call to Next will block.1618 go func() {1619 // The internal AwaitData timing of MongoDB is around 2 seconds,1620 // so this item should arrive within the AwaitData threshold.1621 time.Sleep(500 * time.Millisecond)1622 session := session.New()1623 defer session.Close()1624 coll := session.DB("mydb").C("mycoll")1625 coll.Insert(M{"n": 47})1626 }()1627 c.Log("Will wait for Next with N=47...")1628 ok := iter.Next(&result)1629 c.Assert(ok, Equals, true)1630 c.Assert(iter.Err(), IsNil)1631 c.Assert(iter.Timeout(), Equals, false)1632 c.Assert(result.N, Equals, 47)1633 c.Log("Got Next with N=47!")1634 c.Log("Will wait for a result which will never come...")1635 started := time.Now()1636 ok = iter.Next(&result)1637 c.Assert(ok, Equals, false)1638 c.Assert(iter.Err(), IsNil)1639 c.Assert(iter.Timeout(), Equals, true)1640 c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true)1641 c.Log("Will now reuse the timed out tail cursor...")1642 coll.Insert(M{"n": 48})1643 ok = iter.Next(&result)1644 c.Assert(ok, Equals, true)1645 c.Assert(iter.Close(), IsNil)1646 c.Assert(iter.Timeout(), Equals, false)1647 c.Assert(result.N, Equals, 48)1648}1649// Test tailable cursors in a situation where Next never gets to sleep once1650// to respect the timeout requested on Tail.1651func (s *S) TestFindTailNoTimeout(c *C) {1652 if *fast {1653 c.Skip("-fast")1654 }1655 session, err := mgo.Dial("localhost:40001")1656 c.Assert(err, IsNil)1657 defer session.Close()1658 cresult := struct{ ErrMsg string }{}1659 db := session.DB("mydb")1660 err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult)1661 c.Assert(err, IsNil)1662 c.Assert(cresult.ErrMsg, Equals, "")1663 coll := db.C("mycoll")1664 ns := []int{40, 41, 42, 43, 44, 45, 46}1665 for _, n := range ns {1666 coll.Insert(M{"n": n})1667 }1668 session.Refresh() // Release socket.1669 mgo.ResetStats()1670 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)1671 iter := query.Tail(-1)1672 c.Assert(err, IsNil)1673 n := len(ns)1674 result := struct{ N int }{}1675 for i := 2; i != n; i++ {1676 ok := iter.Next(&result)1677 c.Assert(ok, Equals, true)1678 c.Assert(result.N, Equals, ns[i])1679 if i == 3 { // The batch boundary.1680 stats := mgo.GetStats()1681 c.Assert(stats.ReceivedDocs, Equals, 2)1682 }1683 }1684 mgo.ResetStats()1685 // The following call to Next will block.1686 go func() {1687 time.Sleep(5e8)1688 session := session.New()1689 defer session.Close()1690 coll := session.DB("mydb").C("mycoll")1691 coll.Insert(M{"n": 47})1692 }()1693 c.Log("Will wait for Next with N=47...")1694 ok := iter.Next(&result)1695 c.Assert(ok, Equals, true)1696 c.Assert(iter.Err(), IsNil)1697 c.Assert(iter.Timeout(), Equals, false)1698 c.Assert(result.N, Equals, 47)1699 c.Log("Got Next with N=47!")1700 c.Log("Will wait for a result which will never come...")1701 gotNext := make(chan bool)1702 go func() {1703 ok := iter.Next(&result)1704 gotNext <- ok1705 }()1706 select {1707 case ok := <-gotNext:1708 c.Fatalf("Next returned: %v", ok)1709 case <-time.After(3e9):1710 // Good. Should still be sleeping at that point.1711 }1712 // Closing the session should cause Next to return.1713 session.Close()1714 select {1715 case ok := <-gotNext:1716 c.Assert(ok, Equals, false)1717 c.Assert(iter.Err(), ErrorMatches, "Closed explicitly")1718 c.Assert(iter.Timeout(), Equals, false)1719 case <-time.After(1e9):1720 c.Fatal("Closing the session did not unblock Next")1721 }1722}1723func (s *S) TestIterNextResetsResult(c *C) {1724 session, err := mgo.Dial("localhost:40001")1725 c.Assert(err, IsNil)1726 defer session.Close()1727 coll := session.DB("mydb").C("mycoll")1728 ns := []int{1, 2, 3}1729 for _, n := range ns {1730 coll.Insert(M{"n" + strconv.Itoa(n): n})1731 }1732 query := coll.Find(nil).Sort("$natural")1733 i := 01734 var sresult *struct{ N1, N2, N3 int }1735 iter := query.Iter()1736 for iter.Next(&sresult) {1737 switch i {1738 case 0:1739 c.Assert(sresult.N1, Equals, 1)1740 c.Assert(sresult.N2+sresult.N3, Equals, 0)1741 case 1:1742 c.Assert(sresult.N2, Equals, 2)1743 c.Assert(sresult.N1+sresult.N3, Equals, 0)1744 case 2:1745 c.Assert(sresult.N3, Equals, 3)1746 c.Assert(sresult.N1+sresult.N2, Equals, 0)1747 }1748 i++1749 }1750 c.Assert(iter.Close(), IsNil)1751 i = 01752 var mresult M1753 iter = query.Iter()1754 for iter.Next(&mresult) {1755 delete(mresult, "_id")1756 switch i {1757 case 0:1758 c.Assert(mresult, DeepEquals, M{"n1": 1})1759 case 1:1760 c.Assert(mresult, DeepEquals, M{"n2": 2})1761 case 2:1762 c.Assert(mresult, DeepEquals, M{"n3": 3})1763 }1764 i++1765 }1766 c.Assert(iter.Close(), IsNil)1767 i = 01768 var iresult interface{}1769 iter = query.Iter()1770 for iter.Next(&iresult) {1771 mresult, ok := iresult.(bson.M)1772 c.Assert(ok, Equals, true, Commentf("%#v", iresult))1773 delete(mresult, "_id")1774 switch i {1775 case 0:1776 c.Assert(mresult, DeepEquals, bson.M{"n1": 1})1777 case 1:1778 c.Assert(mresult, DeepEquals, bson.M{"n2": 2})1779 case 2:1780 c.Assert(mresult, DeepEquals, bson.M{"n3": 3})1781 }1782 i++1783 }1784 c.Assert(iter.Close(), IsNil)1785}1786func (s *S) TestFindForOnIter(c *C) {1787 session, err := mgo.Dial("localhost:40001")1788 c.Assert(err, IsNil)1789 defer session.Close()1790 coll := session.DB("mydb").C("mycoll")1791 ns := []int{40, 41, 42, 43, 44, 45, 46}1792 for _, n := range ns {1793 coll.Insert(M{"n": n})1794 }1795 session.Refresh() // Release socket.1796 mgo.ResetStats()1797 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)1798 iter := query.Iter()1799 i := 21800 var result *struct{ N int }1801 err = iter.For(&result, func() error {1802 c.Assert(i < 7, Equals, true)1803 c.Assert(result.N, Equals, ns[i])1804 if i == 1 {1805 stats := mgo.GetStats()1806 if s.versionAtLeast(3, 2) {1807 // Find command in 3.2+ bundles batches in a single document.1808 c.Assert(stats.ReceivedDocs, Equals, 1)1809 } else {1810 c.Assert(stats.ReceivedDocs, Equals, 2)1811 }1812 }1813 i++1814 return nil1815 })1816 c.Assert(err, IsNil)1817 session.Refresh() // Release socket.1818 stats := mgo.GetStats()1819 c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP1820 c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.1821 if s.versionAtLeast(3, 2) {1822 // Find command in 3.2+ bundles batches in a single document.1823 c.Assert(stats.ReceivedDocs, Equals, 3)1824 } else {1825 c.Assert(stats.ReceivedDocs, Equals, 5)1826 }1827 c.Assert(stats.SocketsInUse, Equals, 0)1828}1829func (s *S) TestFindFor(c *C) {1830 session, err := mgo.Dial("localhost:40001")1831 c.Assert(err, IsNil)1832 defer session.Close()1833 coll := session.DB("mydb").C("mycoll")1834 ns := []int{40, 41, 42, 43, 44, 45, 46}1835 for _, n := range ns {1836 coll.Insert(M{"n": n})1837 }1838 session.Refresh() // Release socket.1839 mgo.ResetStats()1840 query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2)1841 i := 21842 var result *struct{ N int }1843 err = query.For(&result, func() error {1844 c.Assert(i < 7, Equals, true)1845 c.Assert(result.N, Equals, ns[i])1846 if i == 1 {1847 stats := mgo.GetStats()1848 c.Assert(stats.ReceivedDocs, Equals, 2)1849 if s.versionAtLeast(3, 2) {1850 // Find command in 3.2+ bundles batches in a single document.1851 c.Assert(stats.ReceivedDocs, Equals, 1)1852 } else {1853 c.Assert(stats.ReceivedDocs, Equals, 2)1854 }1855 }1856 i++1857 return nil1858 })1859 c.Assert(err, IsNil)1860 session.Refresh() // Release socket.1861 stats := mgo.GetStats()1862 c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP1863 c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs.1864 if s.versionAtLeast(3, 2) {1865 // Find command in 3.2+ bundles batches in a single document.1866 c.Assert(stats.ReceivedDocs, Equals, 3)1867 } else {1868 c.Assert(stats.ReceivedDocs, Equals, 5)1869 }1870 c.Assert(stats.SocketsInUse, Equals, 0)1871}1872func (s *S) TestFindForStopOnError(c *C) {1873 session, err := mgo.Dial("localhost:40001")1874 c.Assert(err, IsNil)1875 defer session.Close()1876 coll := session.DB("mydb").C("mycoll")1877 ns := []int{40, 41, 42, 43, 44, 45, 46}1878 for _, n := range ns {1879 coll.Insert(M{"n": n})1880 }1881 query := coll.Find(M{"n": M{"$gte": 42}})1882 i := 21883 var result *struct{ N int }1884 err = query.For(&result, func() error {1885 c.Assert(i < 4, Equals, true)1886 c.Assert(result.N, Equals, ns[i])1887 if i == 3 {1888 return fmt.Errorf("stop!")1889 }1890 i++1891 return nil1892 })1893 c.Assert(err, ErrorMatches, "stop!")1894}1895func (s *S) TestFindForResetsResult(c *C) {1896 session, err := mgo.Dial("localhost:40001")1897 c.Assert(err, IsNil)1898 defer session.Close()1899 coll := session.DB("mydb").C("mycoll")1900 ns := []int{1, 2, 3}1901 for _, n := range ns {1902 coll.Insert(M{"n" + strconv.Itoa(n): n})1903 }1904 query := coll.Find(nil).Sort("$natural")1905 i := 01906 var sresult *struct{ N1, N2, N3 int }1907 err = query.For(&sresult, func() error {1908 switch i {1909 case 0:1910 c.Assert(sresult.N1, Equals, 1)1911 c.Assert(sresult.N2+sresult.N3, Equals, 0)1912 case 1:1913 c.Assert(sresult.N2, Equals, 2)1914 c.Assert(sresult.N1+sresult.N3, Equals, 0)1915 case 2:1916 c.Assert(sresult.N3, Equals, 3)1917 c.Assert(sresult.N1+sresult.N2, Equals, 0)1918 }1919 i++1920 return nil1921 })1922 c.Assert(err, IsNil)1923 i = 01924 var mresult M1925 err = query.For(&mresult, func() error {1926 delete(mresult, "_id")1927 switch i {1928 case 0:1929 c.Assert(mresult, DeepEquals, M{"n1": 1})1930 case 1:1931 c.Assert(mresult, DeepEquals, M{"n2": 2})1932 case 2:1933 c.Assert(mresult, DeepEquals, M{"n3": 3})1934 }1935 i++1936 return nil1937 })1938 c.Assert(err, IsNil)1939 i = 01940 var iresult interface{}1941 err = query.For(&iresult, func() error {1942 mresult, ok := iresult.(bson.M)1943 c.Assert(ok, Equals, true, Commentf("%#v", iresult))1944 delete(mresult, "_id")1945 switch i {1946 case 0:1947 c.Assert(mresult, DeepEquals, bson.M{"n1": 1})1948 case 1:1949 c.Assert(mresult, DeepEquals, bson.M{"n2": 2})1950 case 2:1951 c.Assert(mresult, DeepEquals, bson.M{"n3": 3})1952 }1953 i++1954 return nil1955 })1956 c.Assert(err, IsNil)1957}1958func (s *S) TestFindIterSnapshot(c *C) {1959 if s.versionAtLeast(3, 2) {1960 c.Skip("Broken in 3.2: https://jira.mongodb.org/browse/SERVER-21403")1961 }1962 session, err := mgo.Dial("localhost:40001")1963 c.Assert(err, IsNil)1964 defer session.Close()1965 // Insane amounts of logging otherwise due to the1966 // amount of data being shuffled.1967 mgo.SetDebug(false)1968 defer mgo.SetDebug(true)1969 coll := session.DB("mydb").C("mycoll")1970 var a [1024000]byte1971 for n := 0; n < 10; n++ {1972 err := coll.Insert(M{"_id": n, "n": n, "a1": &a})1973 c.Assert(err, IsNil)1974 }1975 query := coll.Find(M{"n": M{"$gt": -1}}).Batch(2).Prefetch(0)1976 query.Snapshot()1977 iter := query.Iter()1978 seen := map[int]bool{}1979 result := struct {1980 Id int "_id"1981 }{}1982 for iter.Next(&result) {1983 if len(seen) == 2 {1984 // Grow all entries so that they have to move.1985 // Backwards so that the order is inverted.1986 for n := 10; n >= 0; n-- {1987 _, err := coll.Upsert(M{"_id": n}, M{"$set": M{"a2": &a}})1988 c.Assert(err, IsNil)1989 }1990 }1991 if seen[result.Id] {1992 c.Fatalf("seen duplicated key: %d", result.Id)1993 }1994 seen[result.Id] = true1995 }1996 c.Assert(iter.Close(), IsNil)1997}1998func (s *S) TestSort(c *C) {1999 session, err := mgo.Dial("localhost:40001")2000 c.Assert(err, IsNil)2001 defer session.Close()2002 coll := session.DB("mydb").C("mycoll")2003 coll.Insert(M{"a": 1, "b": 1})2004 coll.Insert(M{"a": 2, "b": 2})2005 coll.Insert(M{"a": 2, "b": 1})2006 coll.Insert(M{"a": 0, "b": 1})2007 coll.Insert(M{"a": 2, "b": 0})2008 coll.Insert(M{"a": 0, "b": 2})2009 coll.Insert(M{"a": 1, "b": 2})2010 coll.Insert(M{"a": 0, "b": 0})2011 coll.Insert(M{"a": 1, "b": 0})2012 query := coll.Find(M{})2013 query.Sort("-a") // Should be ignored.2014 query.Sort("-b", "a")2015 iter := query.Iter()2016 l := make([]int, 18)2017 r := struct{ A, B int }{}2018 for i := 0; i != len(l); i += 2 {2019 ok := iter.Next(&r)2020 c.Assert(ok, Equals, true)2021 c.Assert(err, IsNil)2022 l[i] = r.A2023 l[i+1] = r.B2024 }2025 c.Assert(l, DeepEquals, []int{0, 2, 1, 2, 2, 2, 0, 1, 1, 1, 2, 1, 0, 0, 1, 0, 2, 0})2026}2027func (s *S) TestSortWithBadArgs(c *C) {2028 session, err := mgo.Dial("localhost:40001")2029 c.Assert(err, IsNil)2030 defer session.Close()2031 coll := session.DB("mydb").C("mycoll")2032 f1 := func() { coll.Find(nil).Sort("") }2033 f2 := func() { coll.Find(nil).Sort("+") }2034 f3 := func() { coll.Find(nil).Sort("foo", "-") }2035 for _, f := range []func(){f1, f2, f3} {2036 c.Assert(f, PanicMatches, "Sort: empty field name")2037 }2038}2039func (s *S) TestSortScoreText(c *C) {2040 session, err := mgo.Dial("localhost:40001")2041 c.Assert(err, IsNil)2042 defer session.Close()2043 if !s.versionAtLeast(2, 4) {2044 c.Skip("Text search depends on 2.4+")2045 }2046 coll := session.DB("mydb").C("mycoll")2047 err = coll.EnsureIndex(mgo.Index{2048 Key: []string{"$text:a", "$text:b"},2049 })2050 msg := "text search not enabled"2051 if err != nil && strings.Contains(err.Error(), msg) {2052 c.Skip(msg)2053 }2054 c.Assert(err, IsNil)2055 err = coll.Insert(M{2056 "a": "none",2057 "b": "twice: foo foo",2058 })2059 c.Assert(err, IsNil)2060 err = coll.Insert(M{2061 "a": "just once: foo",2062 "b": "none",2063 })2064 c.Assert(err, IsNil)2065 err = coll.Insert(M{2066 "a": "many: foo foo foo",2067 "b": "none",2068 })2069 c.Assert(err, IsNil)2070 err = coll.Insert(M{2071 "a": "none",2072 "b": "none",2073 "c": "ignore: foo",2074 })2075 c.Assert(err, IsNil)2076 query := coll.Find(M{"$text": M{"$search": "foo"}})2077 query.Select(M{"score": M{"$meta": "textScore"}})2078 query.Sort("$textScore:score")2079 iter := query.Iter()2080 var r struct{ A, B string }2081 var results []string2082 for iter.Next(&r) {2083 results = append(results, r.A, r.B)2084 }2085 c.Assert(results, DeepEquals, []string{2086 "many: foo foo foo", "none",2087 "none", "twice: foo foo",2088 "just once: foo", "none",2089 })2090}2091func (s *S) TestPrefetching(c *C) {2092 session, err := mgo.Dial("localhost:40001")2093 c.Assert(err, IsNil)2094 defer session.Close()2095 coll := session.DB("mydb").C("mycoll")2096 const total = 6002097 const batch = 1002098 mgo.SetDebug(false)2099 docs := make([]interface{}, total)2100 for i := 0; i != total; i++ {2101 docs[i] = bson.D{{"n", i}}2102 }2103 err = coll.Insert(docs...)2104 c.Assert(err, IsNil)2105 for testi := 0; testi < 5; testi++ {2106 mgo.ResetStats()2107 var iter *mgo.Iter2108 var beforeMore int2109 switch testi {2110 case 0: // The default session value.2111 session.SetBatch(batch)2112 iter = coll.Find(M{}).Iter()2113 beforeMore = 752114 case 2: // Changing the session value.2115 session.SetBatch(batch)2116 session.SetPrefetch(0.27)2117 iter = coll.Find(M{}).Iter()2118 beforeMore = 732119 case 1: // Changing via query methods.2120 iter = coll.Find(M{}).Prefetch(0.27).Batch(batch).Iter()2121 beforeMore = 732122 case 3: // With prefetch on first document.2123 iter = coll.Find(M{}).Prefetch(1.0).Batch(batch).Iter()2124 beforeMore = 02125 case 4: // Without prefetch.2126 iter = coll.Find(M{}).Prefetch(0).Batch(batch).Iter()2127 beforeMore = 1002128 }2129 pings := 02130 for batchi := 0; batchi < len(docs)/batch-1; batchi++ {2131 c.Logf("Iterating over %d documents on batch %d", beforeMore, batchi)2132 var result struct{ N int }2133 for i := 0; i < beforeMore; i++ {2134 ok := iter.Next(&result)2135 c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err()))2136 }2137 beforeMore = 992138 c.Logf("Done iterating.")2139 session.Run("ping", nil) // Roundtrip to settle down.2140 pings++2141 stats := mgo.GetStats()2142 if s.versionAtLeast(3, 2) {2143 // Find command in 3.2+ bundles batches in a single document.2144 c.Assert(stats.ReceivedDocs, Equals, (batchi+1)+pings)2145 } else {2146 c.Assert(stats.ReceivedDocs, Equals, (batchi+1)*batch+pings)2147 }2148 c.Logf("Iterating over one more document on batch %d", batchi)2149 ok := iter.Next(&result)2150 c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err()))2151 c.Logf("Done iterating.")2152 session.Run("ping", nil) // Roundtrip to settle down.2153 pings++2154 stats = mgo.GetStats()2155 if s.versionAtLeast(3, 2) {2156 // Find command in 3.2+ bundles batches in a single document.2157 c.Assert(stats.ReceivedDocs, Equals, (batchi+2)+pings)2158 } else {2159 c.Assert(stats.ReceivedDocs, Equals, (batchi+2)*batch+pings)2160 }2161 }2162 }2163}2164func (s *S) TestSafeSetting(c *C) {2165 session, err := mgo.Dial("localhost:40001")2166 c.Assert(err, IsNil)2167 defer session.Close()2168 // Check the default2169 safe := session.Safe()2170 c.Assert(safe.W, Equals, 0)2171 c.Assert(safe.WMode, Equals, "")2172 c.Assert(safe.WTimeout, Equals, 0)2173 c.Assert(safe.FSync, Equals, false)2174 c.Assert(safe.J, Equals, false)2175 // Tweak it2176 session.SetSafe(&mgo.Safe{W: 1, WTimeout: 2, FSync: true})2177 safe = session.Safe()2178 c.Assert(safe.W, Equals, 1)2179 c.Assert(safe.WMode, Equals, "")2180 c.Assert(safe.WTimeout, Equals, 2)2181 c.Assert(safe.FSync, Equals, true)2182 c.Assert(safe.J, Equals, false)2183 // Reset it again.2184 session.SetSafe(&mgo.Safe{})2185 safe = session.Safe()2186 c.Assert(safe.W, Equals, 0)2187 c.Assert(safe.WMode, Equals, "")2188 c.Assert(safe.WTimeout, Equals, 0)2189 c.Assert(safe.FSync, Equals, false)2190 c.Assert(safe.J, Equals, false)2191 // Ensure safety to something more conservative.2192 session.SetSafe(&mgo.Safe{W: 5, WTimeout: 6, J: true})2193 safe = session.Safe()2194 c.Assert(safe.W, Equals, 5)2195 c.Assert(safe.WMode, Equals, "")2196 c.Assert(safe.WTimeout, Equals, 6)2197 c.Assert(safe.FSync, Equals, false)2198 c.Assert(safe.J, Equals, true)2199 // Ensure safety to something less conservative won't change it.2200 session.EnsureSafe(&mgo.Safe{W: 4, WTimeout: 7})2201 safe = session.Safe()2202 c.Assert(safe.W, Equals, 5)2203 c.Assert(safe.WMode, Equals, "")2204 c.Assert(safe.WTimeout, Equals, 6)2205 c.Assert(safe.FSync, Equals, false)2206 c.Assert(safe.J, Equals, true)2207 // But to something more conservative will.2208 session.EnsureSafe(&mgo.Safe{W: 6, WTimeout: 4, FSync: true})2209 safe = session.Safe()2210 c.Assert(safe.W, Equals, 6)2211 c.Assert(safe.WMode, Equals, "")2212 c.Assert(safe.WTimeout, Equals, 4)2213 c.Assert(safe.FSync, Equals, true)2214 c.Assert(safe.J, Equals, false)2215 // Even more conservative.2216 session.EnsureSafe(&mgo.Safe{WMode: "majority", WTimeout: 2})2217 safe = session.Safe()2218 c.Assert(safe.W, Equals, 0)2219 c.Assert(safe.WMode, Equals, "majority")2220 c.Assert(safe.WTimeout, Equals, 2)2221 c.Assert(safe.FSync, Equals, true)2222 c.Assert(safe.J, Equals, false)2223 // WMode always overrides, whatever it is, but J doesn't.2224 session.EnsureSafe(&mgo.Safe{WMode: "something", J: true})2225 safe = session.Safe()2226 c.Assert(safe.W, Equals, 0)2227 c.Assert(safe.WMode, Equals, "something")2228 c.Assert(safe.WTimeout, Equals, 2)2229 c.Assert(safe.FSync, Equals, true)2230 c.Assert(safe.J, Equals, false)2231 // EnsureSafe with nil does nothing.2232 session.EnsureSafe(nil)2233 safe = session.Safe()2234 c.Assert(safe.W, Equals, 0)2235 c.Assert(safe.WMode, Equals, "something")2236 c.Assert(safe.WTimeout, Equals, 2)2237 c.Assert(safe.FSync, Equals, true)2238 c.Assert(safe.J, Equals, false)2239 // Changing the safety of a cloned session doesn't touch the original.2240 clone := session.Clone()2241 defer clone.Close()2242 clone.EnsureSafe(&mgo.Safe{WMode: "foo"})2243 safe = session.Safe()2244 c.Assert(safe.WMode, Equals, "something")2245}2246func (s *S) TestSafeInsert(c *C) {2247 session, err := mgo.Dial("localhost:40001")2248 c.Assert(err, IsNil)2249 defer session.Close()2250 coll := session.DB("mydb").C("mycoll")2251 // Insert an element with a predefined key.2252 err = coll.Insert(M{"_id": 1})2253 c.Assert(err, IsNil)2254 mgo.ResetStats()2255 // Session should be safe by default, so inserting it again must fail.2256 err = coll.Insert(M{"_id": 1})2257 c.Assert(err, ErrorMatches, ".*E11000 duplicate.*")2258 c.Assert(err.(*mgo.LastError).Code, Equals, 11000)2259 // It must have sent two operations (INSERT_OP + getLastError QUERY_OP)2260 stats := mgo.GetStats()2261 if s.versionAtLeast(2, 6) {2262 c.Assert(stats.SentOps, Equals, 1)2263 } else {2264 c.Assert(stats.SentOps, Equals, 2)2265 }2266 mgo.ResetStats()2267 // If we disable safety, though, it won't complain.2268 session.SetSafe(nil)2269 err = coll.Insert(M{"_id": 1})2270 c.Assert(err, IsNil)2271 // Must have sent a single operation this time (just the INSERT_OP)2272 stats = mgo.GetStats()2273 c.Assert(stats.SentOps, Equals, 1)2274}2275func (s *S) TestSafeParameters(c *C) {2276 session, err := mgo.Dial("localhost:40011")2277 c.Assert(err, IsNil)2278 defer session.Close()2279 coll := session.DB("mydb").C("mycoll")2280 // Tweak the safety parameters to something unachievable.2281 session.SetSafe(&mgo.Safe{W: 4, WTimeout: 100})2282 err = coll.Insert(M{"_id": 1})2283 c.Assert(err, ErrorMatches, "timeout|timed out waiting for slaves|Not enough data-bearing nodes|waiting for replication timed out") // :-(2284 if !s.versionAtLeast(2, 6) {2285 // 2.6 turned it into a query error.2286 c.Assert(err.(*mgo.LastError).WTimeout, Equals, true)2287 }2288}2289func (s *S) TestQueryErrorOne(c *C) {2290 session, err := mgo.Dial("localhost:40001")2291 c.Assert(err, IsNil)2292 defer session.Close()2293 coll := session.DB("mydb").C("mycoll")2294 err = coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).One(nil)2295 c.Assert(err, ErrorMatches, ".*Unsupported projection option:.*")2296 c.Assert(err.(*mgo.QueryError).Message, Matches, ".*Unsupported projection option:.*")2297 // Oh, the dance of error codes. :-(2298 if s.versionAtLeast(3, 2) {2299 c.Assert(err.(*mgo.QueryError).Code, Equals, 2)2300 } else if s.versionAtLeast(2, 6) {2301 c.Assert(err.(*mgo.QueryError).Code, Equals, 17287)2302 } else {2303 c.Assert(err.(*mgo.QueryError).Code, Equals, 13097)2304 }2305}2306func (s *S) TestQueryErrorNext(c *C) {2307 session, err := mgo.Dial("localhost:40001")2308 c.Assert(err, IsNil)2309 defer session.Close()2310 coll := session.DB("mydb").C("mycoll")2311 iter := coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).Iter()2312 var result struct{}2313 ok := iter.Next(&result)2314 c.Assert(ok, Equals, false)2315 err = iter.Close()2316 c.Assert(err, ErrorMatches, ".*Unsupported projection option:.*")2317 c.Assert(err.(*mgo.QueryError).Message, Matches, ".*Unsupported projection option:.*")2318 // Oh, the dance of error codes. :-(2319 if s.versionAtLeast(3, 2) {2320 c.Assert(err.(*mgo.QueryError).Code, Equals, 2)2321 } else if s.versionAtLeast(2, 6) {2322 c.Assert(err.(*mgo.QueryError).Code, Equals, 17287)2323 } else {2324 c.Assert(err.(*mgo.QueryError).Code, Equals, 13097)2325 }2326 c.Assert(iter.Err(), Equals, err)2327}2328var indexTests = []struct {2329 index mgo.Index2330 expected M2331}{{2332 mgo.Index{2333 Key: []string{"a"},2334 Background: true,2335 },2336 M{2337 "name": "a_1",2338 "key": M{"a": 1},2339 "ns": "mydb.mycoll",2340 "background": true,2341 },2342}, {2343 mgo.Index{2344 Key: []string{"a", "-b"},2345 Unique: true,2346 DropDups: true,2347 },2348 M{2349 "name": "a_1_b_-1",2350 "key": M{"a": 1, "b": -1},2351 "ns": "mydb.mycoll",2352 "unique": true,2353 "dropDups": true,2354 },2355}, {2356 mgo.Index{2357 Key: []string{"@loc_old"}, // Obsolete2358 Min: -500,2359 Max: 500,2360 Bits: 32,2361 },2362 M{2363 "name": "loc_old_2d",2364 "key": M{"loc_old": "2d"},2365 "ns": "mydb.mycoll",2366 "min": -500.0,2367 "max": 500.0,2368 "bits": 32,2369 },2370}, {2371 mgo.Index{2372 Key: []string{"$2d:loc"},2373 Min: -500,2374 Max: 500,2375 Bits: 32,2376 },2377 M{2378 "name": "loc_2d",2379 "key": M{"loc": "2d"},2380 "ns": "mydb.mycoll",2381 "min": -500.0,2382 "max": 500.0,2383 "bits": 32,2384 },2385}, {2386 mgo.Index{2387 Key: []string{"$2d:loc"},2388 Minf: -500.1,2389 Maxf: 500.1,2390 Min: 1, // Should be ignored2391 Max: 2,2392 Bits: 32,2393 },2394 M{2395 "name": "loc_2d",2396 "key": M{"loc": "2d"},2397 "ns": "mydb.mycoll",2398 "min": -500.1,2399 "max": 500.1,2400 "bits": 32,2401 },2402}, {2403 mgo.Index{2404 Key: []string{"$geoHaystack:loc", "type"},2405 BucketSize: 1,2406 },2407 M{2408 "name": "loc_geoHaystack_type_1",2409 "key": M{"loc": "geoHaystack", "type": 1},2410 "ns": "mydb.mycoll",2411 "bucketSize": 1.0,2412 },2413}, {2414 mgo.Index{2415 Key: []string{"$text:a", "$text:b"},2416 Weights: map[string]int{"b": 42},2417 },2418 M{2419 "name": "a_text_b_text",2420 "key": M{"_fts": "text", "_ftsx": 1},2421 "ns": "mydb.mycoll",2422 "weights": M{"a": 1, "b": 42},2423 "default_language": "english",2424 "language_override": "language",2425 "textIndexVersion": 2,2426 },2427}, {2428 mgo.Index{2429 Key: []string{"$text:a"},2430 DefaultLanguage: "portuguese",2431 LanguageOverride: "idioma",2432 },2433 M{2434 "name": "a_text",2435 "key": M{"_fts": "text", "_ftsx": 1},2436 "ns": "mydb.mycoll",2437 "weights": M{"a": 1},2438 "default_language": "portuguese",2439 "language_override": "idioma",2440 "textIndexVersion": 2,2441 },2442}, {2443 mgo.Index{2444 Key: []string{"$text:$**"},2445 },2446 M{2447 "name": "$**_text",2448 "key": M{"_fts": "text", "_ftsx": 1},2449 "ns": "mydb.mycoll",2450 "weights": M{"$**": 1},2451 "default_language": "english",2452 "language_override": "language",2453 "textIndexVersion": 2,2454 },2455}, {2456 mgo.Index{2457 Key: []string{"cn"},2458 Name: "CustomName",2459 },2460 M{2461 "name": "CustomName",2462 "key": M{"cn": 1},2463 "ns": "mydb.mycoll",2464 },2465}}2466func (s *S) TestEnsureIndex(c *C) {2467 session, err := mgo.Dial("localhost:40001")2468 c.Assert(err, IsNil)2469 defer session.Close()2470 coll := session.DB("mydb").C("mycoll")2471 idxs := session.DB("mydb").C("system.indexes")2472 for _, test := range indexTests {2473 if !s.versionAtLeast(2, 4) && test.expected["textIndexVersion"] != nil {2474 continue2475 }2476 err = coll.EnsureIndex(test.index)2477 msg := "text search not enabled"2478 if err != nil && strings.Contains(err.Error(), msg) {2479 continue2480 }2481 c.Assert(err, IsNil)2482 expectedName := test.index.Name2483 if expectedName == "" {2484 expectedName, _ = test.expected["name"].(string)2485 }2486 obtained := M{}2487 err = idxs.Find(M{"name": expectedName}).One(obtained)2488 c.Assert(err, IsNil)2489 delete(obtained, "v")2490 if s.versionAtLeast(2, 7) {2491 // Was deprecated in 2.6, and not being reported by 2.7+.2492 delete(test.expected, "dropDups")2493 test.index.DropDups = false2494 }2495 if s.versionAtLeast(3, 2) && test.expected["textIndexVersion"] != nil {2496 test.expected["textIndexVersion"] = 32497 }2498 c.Assert(obtained, DeepEquals, test.expected)2499 // The result of Indexes must match closely what was used to create the index.2500 indexes, err := coll.Indexes()2501 c.Assert(err, IsNil)2502 c.Assert(indexes, HasLen, 2)2503 gotIndex := indexes[0]2504 if gotIndex.Name == "_id_" {2505 gotIndex = indexes[1]2506 }2507 wantIndex := test.index2508 if wantIndex.Name == "" {2509 wantIndex.Name = gotIndex.Name2510 }2511 if strings.HasPrefix(wantIndex.Key[0], "@") {2512 wantIndex.Key[0] = "$2d:" + wantIndex.Key[0][1:]2513 }2514 if wantIndex.Minf == 0 && wantIndex.Maxf == 0 {2515 wantIndex.Minf = float64(wantIndex.Min)2516 wantIndex.Maxf = float64(wantIndex.Max)2517 } else {2518 wantIndex.Min = gotIndex.Min2519 wantIndex.Max = gotIndex.Max2520 }2521 if wantIndex.DefaultLanguage == "" {2522 wantIndex.DefaultLanguage = gotIndex.DefaultLanguage2523 }2524 if wantIndex.LanguageOverride == "" {2525 wantIndex.LanguageOverride = gotIndex.LanguageOverride2526 }2527 for name, _ := range gotIndex.Weights {2528 if _, ok := wantIndex.Weights[name]; !ok {2529 if wantIndex.Weights == nil {2530 wantIndex.Weights = make(map[string]int)2531 }2532 wantIndex.Weights[name] = 12533 }2534 }2535 c.Assert(gotIndex, DeepEquals, wantIndex)2536 // Drop created index by key or by name if a custom name was used.2537 if test.index.Name == "" {2538 err = coll.DropIndex(test.index.Key...)2539 c.Assert(err, IsNil)2540 } else {2541 err = coll.DropIndexName(test.index.Name)2542 c.Assert(err, IsNil)2543 }2544 }2545}2546func (s *S) TestEnsureIndexWithBadInfo(c *C) {2547 session, err := mgo.Dial("localhost:40001")2548 c.Assert(err, IsNil)2549 defer session.Close()2550 coll := session.DB("mydb").C("mycoll")2551 err = coll.EnsureIndex(mgo.Index{})2552 c.Assert(err, ErrorMatches, "invalid index key:.*")2553 err = coll.EnsureIndex(mgo.Index{Key: []string{""}})2554 c.Assert(err, ErrorMatches, "invalid index key:.*")2555}2556func (s *S) TestEnsureIndexWithUnsafeSession(c *C) {2557 session, err := mgo.Dial("localhost:40001")2558 c.Assert(err, IsNil)2559 defer session.Close()2560 session.SetSafe(nil)2561 coll := session.DB("mydb").C("mycoll")2562 err = coll.Insert(M{"a": 1})2563 c.Assert(err, IsNil)2564 err = coll.Insert(M{"a": 1})2565 c.Assert(err, IsNil)2566 // Should fail since there are duplicated entries.2567 index := mgo.Index{2568 Key: []string{"a"},2569 Unique: true,2570 }2571 err = coll.EnsureIndex(index)2572 c.Assert(err, ErrorMatches, ".*duplicate key error.*")2573}2574func (s *S) TestEnsureIndexKey(c *C) {2575 session, err := mgo.Dial("localhost:40001")2576 c.Assert(err, IsNil)2577 defer session.Close()2578 coll := session.DB("mydb").C("mycoll")2579 err = coll.EnsureIndexKey("a")2580 c.Assert(err, IsNil)2581 err = coll.EnsureIndexKey("a", "-b")2582 c.Assert(err, IsNil)2583 sysidx := session.DB("mydb").C("system.indexes")2584 result1 := M{}2585 err = sysidx.Find(M{"name": "a_1"}).One(result1)2586 c.Assert(err, IsNil)2587 result2 := M{}2588 err = sysidx.Find(M{"name": "a_1_b_-1"}).One(result2)2589 c.Assert(err, IsNil)2590 delete(result1, "v")2591 expected1 := M{2592 "name": "a_1",2593 "key": M{"a": 1},2594 "ns": "mydb.mycoll",2595 }2596 c.Assert(result1, DeepEquals, expected1)2597 delete(result2, "v")2598 expected2 := M{2599 "name": "a_1_b_-1",2600 "key": M{"a": 1, "b": -1},2601 "ns": "mydb.mycoll",2602 }2603 c.Assert(result2, DeepEquals, expected2)2604}2605func (s *S) TestEnsureIndexDropIndex(c *C) {2606 session, err := mgo.Dial("localhost:40001")2607 c.Assert(err, IsNil)2608 defer session.Close()2609 coll := session.DB("mydb").C("mycoll")2610 err = coll.EnsureIndexKey("a")2611 c.Assert(err, IsNil)2612 err = coll.EnsureIndexKey("-b")2613 c.Assert(err, IsNil)2614 err = coll.DropIndex("-b")2615 c.Assert(err, IsNil)2616 sysidx := session.DB("mydb").C("system.indexes")2617 err = sysidx.Find(M{"name": "a_1"}).One(nil)2618 c.Assert(err, IsNil)2619 err = sysidx.Find(M{"name": "b_1"}).One(nil)2620 c.Assert(err, Equals, mgo.ErrNotFound)2621 err = coll.DropIndex("a")2622 c.Assert(err, IsNil)2623 err = sysidx.Find(M{"name": "a_1"}).One(nil)2624 c.Assert(err, Equals, mgo.ErrNotFound)2625 err = coll.DropIndex("a")2626 c.Assert(err, ErrorMatches, "index not found.*")2627}2628func (s *S) TestEnsureIndexDropIndexName(c *C) {2629 session, err := mgo.Dial("localhost:40001")2630 c.Assert(err, IsNil)2631 defer session.Close()2632 coll := session.DB("mydb").C("mycoll")2633 err = coll.EnsureIndexKey("a")2634 c.Assert(err, IsNil)2635 err = coll.EnsureIndex(mgo.Index{Key: []string{"b"}, Name: "a"})2636 c.Assert(err, IsNil)2637 err = coll.DropIndexName("a")2638 c.Assert(err, IsNil)2639 sysidx := session.DB("mydb").C("system.indexes")2640 err = sysidx.Find(M{"name": "a_1"}).One(nil)2641 c.Assert(err, IsNil)2642 err = sysidx.Find(M{"name": "a"}).One(nil)2643 c.Assert(err, Equals, mgo.ErrNotFound)2644 err = coll.DropIndexName("a_1")2645 c.Assert(err, IsNil)2646 err = sysidx.Find(M{"name": "a_1"}).One(nil)2647 c.Assert(err, Equals, mgo.ErrNotFound)2648 err = coll.DropIndexName("a_1")2649 c.Assert(err, ErrorMatches, "index not found.*")2650}2651func (s *S) TestEnsureIndexCaching(c *C) {2652 session, err := mgo.Dial("localhost:40001")2653 c.Assert(err, IsNil)2654 defer session.Close()2655 coll := session.DB("mydb").C("mycoll")2656 err = coll.EnsureIndexKey("a")2657 c.Assert(err, IsNil)2658 mgo.ResetStats()2659 // Second EnsureIndex should be cached and do nothing.2660 err = coll.EnsureIndexKey("a")2661 c.Assert(err, IsNil)2662 stats := mgo.GetStats()2663 c.Assert(stats.SentOps, Equals, 0)2664 // Resetting the cache should make it contact the server again.2665 session.ResetIndexCache()2666 err = coll.EnsureIndexKey("a")2667 c.Assert(err, IsNil)2668 stats = mgo.GetStats()2669 c.Assert(stats.SentOps > 0, Equals, true)2670 // Dropping the index should also drop the cached index key.2671 err = coll.DropIndex("a")2672 c.Assert(err, IsNil)2673 mgo.ResetStats()2674 err = coll.EnsureIndexKey("a")2675 c.Assert(err, IsNil)2676 stats = mgo.GetStats()2677 c.Assert(stats.SentOps > 0, Equals, true)2678}2679func (s *S) TestEnsureIndexGetIndexes(c *C) {2680 session, err := mgo.Dial("localhost:40001")2681 c.Assert(err, IsNil)2682 defer session.Close()2683 coll := session.DB("mydb").C("mycoll")2684 err = coll.EnsureIndexKey("-b")2685 c.Assert(err, IsNil)2686 err = coll.EnsureIndexKey("a")2687 c.Assert(err, IsNil)2688 // Obsolete.2689 err = coll.EnsureIndexKey("@c")2690 c.Assert(err, IsNil)2691 err = coll.EnsureIndexKey("$2d:d")2692 c.Assert(err, IsNil)2693 // Try to exercise cursor logic. 2.8.0-rc3 still ignores this.2694 session.SetBatch(2)2695 indexes, err := coll.Indexes()2696 c.Assert(err, IsNil)2697 c.Assert(indexes[0].Name, Equals, "_id_")2698 c.Assert(indexes[1].Name, Equals, "a_1")2699 c.Assert(indexes[1].Key, DeepEquals, []string{"a"})2700 c.Assert(indexes[2].Name, Equals, "b_-1")2701 c.Assert(indexes[2].Key, DeepEquals, []string{"-b"})2702 c.Assert(indexes[3].Name, Equals, "c_2d")2703 c.Assert(indexes[3].Key, DeepEquals, []string{"$2d:c"})2704 c.Assert(indexes[4].Name, Equals, "d_2d")2705 c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})2706}2707func (s *S) TestEnsureIndexNameCaching(c *C) {2708 session, err := mgo.Dial("localhost:40001")2709 c.Assert(err, IsNil)2710 defer session.Close()2711 coll := session.DB("mydb").C("mycoll")2712 err = coll.EnsureIndex(mgo.Index{Key: []string{"a"}, Name: "custom"})2713 c.Assert(err, IsNil)2714 mgo.ResetStats()2715 // Second EnsureIndex should be cached and do nothing.2716 err = coll.EnsureIndexKey("a")2717 c.Assert(err, IsNil)2718 err = coll.EnsureIndex(mgo.Index{Key: []string{"a"}, Name: "custom"})2719 c.Assert(err, IsNil)2720 stats := mgo.GetStats()2721 c.Assert(stats.SentOps, Equals, 0)2722 // Resetting the cache should make it contact the server again.2723 session.ResetIndexCache()2724 err = coll.EnsureIndex(mgo.Index{Key: []string{"a"}, Name: "custom"})2725 c.Assert(err, IsNil)2726 stats = mgo.GetStats()2727 c.Assert(stats.SentOps > 0, Equals, true)2728 // Dropping the index should also drop the cached index key.2729 err = coll.DropIndexName("custom")2730 c.Assert(err, IsNil)2731 mgo.ResetStats()2732 err = coll.EnsureIndex(mgo.Index{Key: []string{"a"}, Name: "custom"})2733 c.Assert(err, IsNil)2734 stats = mgo.GetStats()2735 c.Assert(stats.SentOps > 0, Equals, true)2736}2737func (s *S) TestEnsureIndexEvalGetIndexes(c *C) {2738 session, err := mgo.Dial("localhost:40001")2739 c.Assert(err, IsNil)2740 defer session.Close()2741 coll := session.DB("mydb").C("mycoll")2742 err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({b: -1})"}}, nil)2743 c.Assert(err, IsNil)2744 err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({a: 1})"}}, nil)2745 c.Assert(err, IsNil)2746 err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({c: -1, e: 1})"}}, nil)2747 c.Assert(err, IsNil)2748 err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({d: '2d'})"}}, nil)2749 c.Assert(err, IsNil)2750 indexes, err := coll.Indexes()2751 c.Assert(err, IsNil)2752 c.Assert(indexes[0].Name, Equals, "_id_")2753 c.Assert(indexes[1].Name, Equals, "a_1")2754 c.Assert(indexes[1].Key, DeepEquals, []string{"a"})2755 c.Assert(indexes[2].Name, Equals, "b_-1")2756 c.Assert(indexes[2].Key, DeepEquals, []string{"-b"})2757 c.Assert(indexes[3].Name, Equals, "c_-1_e_1")2758 c.Assert(indexes[3].Key, DeepEquals, []string{"-c", "e"})2759 if s.versionAtLeast(2, 2) {2760 c.Assert(indexes[4].Name, Equals, "d_2d")2761 c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})2762 } else {2763 c.Assert(indexes[4].Name, Equals, "d_")2764 c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"})2765 }2766}2767var testTTL = flag.Bool("test-ttl", false, "test TTL collections (may take 1 minute)")2768func (s *S) TestEnsureIndexExpireAfter(c *C) {2769 session, err := mgo.Dial("localhost:40001")2770 c.Assert(err, IsNil)2771 defer session.Close()2772 session.SetSafe(nil)2773 coll := session.DB("mydb").C("mycoll")2774 err = coll.Insert(M{"n": 1, "t": time.Now().Add(-120 * time.Second)})2775 c.Assert(err, IsNil)2776 err = coll.Insert(M{"n": 2, "t": time.Now()})2777 c.Assert(err, IsNil)2778 // Should fail since there are duplicated entries.2779 index := mgo.Index{2780 Key: []string{"t"},2781 ExpireAfter: 1 * time.Minute,2782 }2783 err = coll.EnsureIndex(index)2784 c.Assert(err, IsNil)2785 indexes, err := coll.Indexes()2786 c.Assert(err, IsNil)2787 c.Assert(indexes[1].Name, Equals, "t_1")2788 c.Assert(indexes[1].ExpireAfter, Equals, 1*time.Minute)2789 if *testTTL {2790 worked := false2791 stop := time.Now().Add(70 * time.Second)2792 for time.Now().Before(stop) {2793 n, err := coll.Count()2794 c.Assert(err, IsNil)2795 if n == 1 {2796 worked = true2797 break2798 }2799 c.Assert(n, Equals, 2)2800 c.Logf("Still has 2 entries...")2801 time.Sleep(1 * time.Second)2802 }2803 if !worked {2804 c.Fatalf("TTL index didn't work")2805 }2806 }2807}2808func (s *S) TestDistinct(c *C) {2809 session, err := mgo.Dial("localhost:40001")2810 c.Assert(err, IsNil)2811 defer session.Close()2812 coll := session.DB("mydb").C("mycoll")2813 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2814 coll.Insert(M{"n": i})2815 }2816 var result []int2817 err = coll.Find(M{"n": M{"$gt": 2}}).Sort("n").Distinct("n", &result)2818 sort.IntSlice(result).Sort()2819 c.Assert(result, DeepEquals, []int{3, 4, 6})2820}2821func (s *S) TestMapReduce(c *C) {2822 session, err := mgo.Dial("localhost:40001")2823 c.Assert(err, IsNil)2824 defer session.Close()2825 coll := session.DB("mydb").C("mycoll")2826 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2827 coll.Insert(M{"n": i})2828 }2829 job := &mgo.MapReduce{2830 Map: "function() { emit(this.n, 1); }",2831 Reduce: "function(key, values) { return Array.sum(values); }",2832 }2833 var result []struct {2834 Id int "_id"2835 Value int2836 }2837 info, err := coll.Find(M{"n": M{"$gt": 2}}).MapReduce(job, &result)2838 c.Assert(err, IsNil)2839 c.Assert(info.InputCount, Equals, 4)2840 c.Assert(info.EmitCount, Equals, 4)2841 c.Assert(info.OutputCount, Equals, 3)2842 c.Assert(info.VerboseTime, IsNil)2843 expected := map[int]int{3: 1, 4: 2, 6: 1}2844 for _, item := range result {2845 c.Logf("Item: %#v", &item)2846 c.Assert(item.Value, Equals, expected[item.Id])2847 expected[item.Id] = -12848 }2849}2850func (s *S) TestMapReduceFinalize(c *C) {2851 session, err := mgo.Dial("localhost:40001")2852 c.Assert(err, IsNil)2853 defer session.Close()2854 coll := session.DB("mydb").C("mycoll")2855 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2856 coll.Insert(M{"n": i})2857 }2858 job := &mgo.MapReduce{2859 Map: "function() { emit(this.n, 1) }",2860 Reduce: "function(key, values) { return Array.sum(values) }",2861 Finalize: "function(key, count) { return {count: count} }",2862 }2863 var result []struct {2864 Id int "_id"2865 Value struct{ Count int }2866 }2867 _, err = coll.Find(nil).MapReduce(job, &result)2868 c.Assert(err, IsNil)2869 expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1}2870 for _, item := range result {2871 c.Logf("Item: %#v", &item)2872 c.Assert(item.Value.Count, Equals, expected[item.Id])2873 expected[item.Id] = -12874 }2875}2876func (s *S) TestMapReduceToCollection(c *C) {2877 session, err := mgo.Dial("localhost:40001")2878 c.Assert(err, IsNil)2879 defer session.Close()2880 coll := session.DB("mydb").C("mycoll")2881 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2882 coll.Insert(M{"n": i})2883 }2884 job := &mgo.MapReduce{2885 Map: "function() { emit(this.n, 1); }",2886 Reduce: "function(key, values) { return Array.sum(values); }",2887 Out: "mr",2888 }2889 info, err := coll.Find(nil).MapReduce(job, nil)2890 c.Assert(err, IsNil)2891 c.Assert(info.InputCount, Equals, 7)2892 c.Assert(info.EmitCount, Equals, 7)2893 c.Assert(info.OutputCount, Equals, 5)2894 c.Assert(info.Collection, Equals, "mr")2895 c.Assert(info.Database, Equals, "mydb")2896 expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1}2897 var item *struct {2898 Id int "_id"2899 Value int2900 }2901 mr := session.DB("mydb").C("mr")2902 iter := mr.Find(nil).Iter()2903 for iter.Next(&item) {2904 c.Logf("Item: %#v", &item)2905 c.Assert(item.Value, Equals, expected[item.Id])2906 expected[item.Id] = -12907 }2908 c.Assert(iter.Close(), IsNil)2909}2910func (s *S) TestMapReduceToOtherDb(c *C) {2911 session, err := mgo.Dial("localhost:40001")2912 c.Assert(err, IsNil)2913 defer session.Close()2914 coll := session.DB("mydb").C("mycoll")2915 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2916 coll.Insert(M{"n": i})2917 }2918 job := &mgo.MapReduce{2919 Map: "function() { emit(this.n, 1); }",2920 Reduce: "function(key, values) { return Array.sum(values); }",2921 Out: bson.D{{"replace", "mr"}, {"db", "otherdb"}},2922 }2923 info, err := coll.Find(nil).MapReduce(job, nil)2924 c.Assert(err, IsNil)2925 c.Assert(info.InputCount, Equals, 7)2926 c.Assert(info.EmitCount, Equals, 7)2927 c.Assert(info.OutputCount, Equals, 5)2928 c.Assert(info.Collection, Equals, "mr")2929 c.Assert(info.Database, Equals, "otherdb")2930 expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1}2931 var item *struct {2932 Id int "_id"2933 Value int2934 }2935 mr := session.DB("otherdb").C("mr")2936 iter := mr.Find(nil).Iter()2937 for iter.Next(&item) {2938 c.Logf("Item: %#v", &item)2939 c.Assert(item.Value, Equals, expected[item.Id])2940 expected[item.Id] = -12941 }2942 c.Assert(iter.Close(), IsNil)2943}2944func (s *S) TestMapReduceOutOfOrder(c *C) {2945 session, err := mgo.Dial("localhost:40001")2946 c.Assert(err, IsNil)2947 defer session.Close()2948 coll := session.DB("mydb").C("mycoll")2949 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {2950 coll.Insert(M{"n": i})2951 }2952 job := &mgo.MapReduce{2953 Map: "function() { emit(this.n, 1); }",2954 Reduce: "function(key, values) { return Array.sum(values); }",2955 Out: bson.M{"a": "a", "z": "z", "replace": "mr", "db": "otherdb", "b": "b", "y": "y"},2956 }2957 info, err := coll.Find(nil).MapReduce(job, nil)2958 c.Assert(err, IsNil)2959 c.Assert(info.Collection, Equals, "mr")2960 c.Assert(info.Database, Equals, "otherdb")2961}2962func (s *S) TestMapReduceScope(c *C) {2963 session, err := mgo.Dial("localhost:40001")2964 c.Assert(err, IsNil)2965 defer session.Close()2966 coll := session.DB("mydb").C("mycoll")2967 coll.Insert(M{"n": 1})2968 job := &mgo.MapReduce{2969 Map: "function() { emit(this.n, x); }",2970 Reduce: "function(key, values) { return Array.sum(values); }",2971 Scope: M{"x": 42},2972 }2973 var result []bson.M2974 _, err = coll.Find(nil).MapReduce(job, &result)2975 c.Assert(len(result), Equals, 1)2976 c.Assert(result[0]["value"], Equals, 42.0)2977}2978func (s *S) TestMapReduceVerbose(c *C) {2979 session, err := mgo.Dial("localhost:40001")2980 c.Assert(err, IsNil)2981 defer session.Close()2982 coll := session.DB("mydb").C("mycoll")2983 for i := 0; i < 100; i++ {2984 err = coll.Insert(M{"n": i})2985 c.Assert(err, IsNil)2986 }2987 job := &mgo.MapReduce{2988 Map: "function() { emit(this.n, 1); }",2989 Reduce: "function(key, values) { return Array.sum(values); }",2990 Verbose: true,2991 }2992 info, err := coll.Find(nil).MapReduce(job, nil)2993 c.Assert(err, IsNil)2994 c.Assert(info.VerboseTime, NotNil)2995}2996func (s *S) TestMapReduceLimit(c *C) {2997 session, err := mgo.Dial("localhost:40001")2998 c.Assert(err, IsNil)2999 defer session.Close()3000 coll := session.DB("mydb").C("mycoll")3001 for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {3002 coll.Insert(M{"n": i})3003 }3004 job := &mgo.MapReduce{3005 Map: "function() { emit(this.n, 1); }",3006 Reduce: "function(key, values) { return Array.sum(values); }",3007 }3008 var result []bson.M3009 _, err = coll.Find(nil).Limit(3).MapReduce(job, &result)3010 c.Assert(err, IsNil)3011 c.Assert(len(result), Equals, 3)3012}3013func (s *S) TestBuildInfo(c *C) {3014 session, err := mgo.Dial("localhost:40001")3015 c.Assert(err, IsNil)3016 defer session.Close()3017 info, err := session.BuildInfo()3018 c.Assert(err, IsNil)3019 var v []int3020 for i, a := range strings.Split(info.Version, ".") {3021 for _, token := range []string{"-rc", "-pre"} {3022 if i == 2 && strings.Contains(a, token) {3023 a = a[:strings.Index(a, token)]3024 info.VersionArray[len(info.VersionArray)-1] = 03025 }3026 }3027 n, err := strconv.Atoi(a)3028 c.Assert(err, IsNil)3029 v = append(v, n)3030 }3031 for len(v) < 4 {3032 v = append(v, 0)3033 }3034 c.Assert(info.VersionArray, DeepEquals, v)3035 c.Assert(info.GitVersion, Matches, "[a-z0-9]+")3036 if s.versionAtLeast(3, 2) {3037 // It was deprecated in 3.2.3038 c.Assert(info.SysInfo, Equals, "")3039 } else {3040 c.Assert(info.SysInfo, Matches, ".*[0-9:]+.*")3041 }3042 if info.Bits != 32 && info.Bits != 64 {3043 c.Fatalf("info.Bits is %d", info.Bits)3044 }3045 if info.MaxObjectSize < 8192 {3046 c.Fatalf("info.MaxObjectSize seems too small: %d", info.MaxObjectSize)3047 }3048}3049func (s *S) TestZeroTimeRoundtrip(c *C) {3050 session, err := mgo.Dial("localhost:40001")3051 c.Assert(err, IsNil)3052 defer session.Close()3053 var d struct{ T time.Time }3054 conn := session.DB("mydb").C("mycoll")3055 err = conn.Insert(d)3056 c.Assert(err, IsNil)3057 var result bson.M3058 err = conn.Find(nil).One(&result)3059 c.Assert(err, IsNil)3060 t, isTime := result["t"].(time.Time)3061 c.Assert(isTime, Equals, true)3062 c.Assert(t, Equals, time.Time{})3063}3064func (s *S) TestFsyncLock(c *C) {3065 session, err := mgo.Dial("localhost:40001")3066 c.Assert(err, IsNil)3067 defer session.Close()3068 clone := session.Clone()3069 defer clone.Close()3070 err = session.FsyncLock()3071 c.Assert(err, IsNil)3072 done := make(chan time.Time)3073 go func() {3074 time.Sleep(3 * time.Second)3075 now := time.Now()3076 err := session.FsyncUnlock()3077 c.Check(err, IsNil)3078 done <- now3079 }()3080 err = clone.DB("mydb").C("mycoll").Insert(bson.M{"n": 1})3081 unlocked := time.Now()3082 unlocking := <-done3083 c.Assert(err, IsNil)3084 c.Assert(unlocked.After(unlocking), Equals, true)3085}3086func (s *S) TestFsync(c *C) {3087 session, err := mgo.Dial("localhost:40001")3088 c.Assert(err, IsNil)3089 defer session.Close()3090 // Not much to do here. Just a smoke check.3091 err = session.Fsync(false)3092 c.Assert(err, IsNil)3093 err = session.Fsync(true)3094 c.Assert(err, IsNil)3095}3096func (s *S) TestRepairCursor(c *C) {3097 if !s.versionAtLeast(2, 7) {3098 c.Skip("RepairCursor only works on 2.7+")3099 }3100 session, err := mgo.Dial("localhost:40001")3101 c.Assert(err, IsNil)3102 defer session.Close()3103 session.SetBatch(2)3104 coll := session.DB("mydb").C("mycoll3")3105 err = coll.DropCollection()3106 ns := []int{0, 10, 20, 30, 40, 50}3107 for _, n := range ns {3108 coll.Insert(M{"n": n})3109 }3110 repairIter := coll.Repair()3111 c.Assert(repairIter.Err(), IsNil)3112 result := struct{ N int }{}3113 resultCounts := map[int]int{}3114 for repairIter.Next(&result) {3115 resultCounts[result.N]++3116 }3117 c.Assert(repairIter.Next(&result), Equals, false)3118 c.Assert(repairIter.Err(), IsNil)3119 c.Assert(repairIter.Close(), IsNil)3120 // Verify that the results of the repair cursor are valid.3121 // The repair cursor can return multiple copies3122 // of the same document, so to check correctness we only3123 // need to verify that at least 1 of each document was returned.3124 for _, key := range ns {3125 c.Assert(resultCounts[key] > 0, Equals, true)3126 }3127}3128func (s *S) TestPipeIter(c *C) {3129 if !s.versionAtLeast(2, 1) {3130 c.Skip("Pipe only works on 2.1+")3131 }3132 session, err := mgo.Dial("localhost:40001")3133 c.Assert(err, IsNil)3134 defer session.Close()3135 coll := session.DB("mydb").C("mycoll")3136 ns := []int{40, 41, 42, 43, 44, 45, 46}3137 for _, n := range ns {3138 coll.Insert(M{"n": n})3139 }3140 pipe := coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}})3141 // Ensure cursor logic is working by forcing a small batch.3142 pipe.Batch(2)3143 // Smoke test for AllowDiskUse.3144 pipe.AllowDiskUse()3145 iter := pipe.Iter()3146 result := struct{ N int }{}3147 for i := 2; i < 7; i++ {3148 ok := iter.Next(&result)3149 c.Assert(ok, Equals, true)3150 c.Assert(result.N, Equals, ns[i])3151 }3152 c.Assert(iter.Next(&result), Equals, false)3153 c.Assert(iter.Close(), IsNil)3154}3155func (s *S) TestPipeAll(c *C) {3156 if !s.versionAtLeast(2, 1) {3157 c.Skip("Pipe only works on 2.1+")3158 }3159 session, err := mgo.Dial("localhost:40001")3160 c.Assert(err, IsNil)3161 defer session.Close()3162 coll := session.DB("mydb").C("mycoll")3163 ns := []int{40, 41, 42, 43, 44, 45, 46}3164 for _, n := range ns {3165 err := coll.Insert(M{"n": n})3166 c.Assert(err, IsNil)3167 }3168 var result []struct{ N int }3169 err = coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}}).All(&result)3170 c.Assert(err, IsNil)3171 for i := 2; i < 7; i++ {3172 c.Assert(result[i-2].N, Equals, ns[i])3173 }3174}3175func (s *S) TestPipeOne(c *C) {3176 if !s.versionAtLeast(2, 1) {3177 c.Skip("Pipe only works on 2.1+")3178 }3179 session, err := mgo.Dial("localhost:40001")3180 c.Assert(err, IsNil)3181 defer session.Close()3182 coll := session.DB("mydb").C("mycoll")3183 coll.Insert(M{"a": 1, "b": 2})3184 result := struct{ A, B int }{}3185 pipe := coll.Pipe([]M{{"$project": M{"a": 1, "b": M{"$add": []interface{}{"$b", 1}}}}})3186 err = pipe.One(&result)3187 c.Assert(err, IsNil)3188 c.Assert(result.A, Equals, 1)3189 c.Assert(result.B, Equals, 3)3190 pipe = coll.Pipe([]M{{"$match": M{"a": 2}}})3191 err = pipe.One(&result)3192 c.Assert(err, Equals, mgo.ErrNotFound)3193}3194func (s *S) TestPipeExplain(c *C) {3195 if !s.versionAtLeast(2, 1) {3196 c.Skip("Pipe only works on 2.1+")3197 }3198 session, err := mgo.Dial("localhost:40001")3199 c.Assert(err, IsNil)3200 defer session.Close()3201 coll := session.DB("mydb").C("mycoll")3202 coll.Insert(M{"a": 1, "b": 2})3203 pipe := coll.Pipe([]M{{"$project": M{"a": 1, "b": M{"$add": []interface{}{"$b", 1}}}}})3204 // The explain command result changes across versions.3205 var result struct{ Ok int }3206 err = pipe.Explain(&result)3207 c.Assert(err, IsNil)3208 c.Assert(result.Ok, Equals, 1)3209}3210func (s *S) TestBatch1Bug(c *C) {3211 session, err := mgo.Dial("localhost:40001")3212 c.Assert(err, IsNil)3213 defer session.Close()3214 coll := session.DB("mydb").C("mycoll")3215 for i := 0; i < 3; i++ {3216 err := coll.Insert(M{"n": i})3217 c.Assert(err, IsNil)3218 }3219 var ns []struct{ N int }3220 err = coll.Find(nil).Batch(1).All(&ns)3221 c.Assert(err, IsNil)3222 c.Assert(len(ns), Equals, 3)3223 session.SetBatch(1)3224 err = coll.Find(nil).All(&ns)3225 c.Assert(err, IsNil)3226 c.Assert(len(ns), Equals, 3)3227}3228func (s *S) TestInterfaceIterBug(c *C) {3229 session, err := mgo.Dial("localhost:40001")3230 c.Assert(err, IsNil)3231 defer session.Close()3232 coll := session.DB("mydb").C("mycoll")3233 for i := 0; i < 3; i++ {3234 err := coll.Insert(M{"n": i})3235 c.Assert(err, IsNil)3236 }3237 var result interface{}3238 i := 03239 iter := coll.Find(nil).Sort("n").Iter()3240 for iter.Next(&result) {3241 c.Assert(result.(bson.M)["n"], Equals, i)3242 i++3243 }3244 c.Assert(iter.Close(), IsNil)3245}3246func (s *S) TestFindIterCloseKillsCursor(c *C) {3247 session, err := mgo.Dial("localhost:40001")3248 c.Assert(err, IsNil)3249 defer session.Close()3250 cursors := serverCursorsOpen(session)3251 coll := session.DB("mydb").C("mycoll")3252 ns := []int{40, 41, 42, 43, 44, 45, 46}3253 for _, n := range ns {3254 err = coll.Insert(M{"n": n})3255 c.Assert(err, IsNil)3256 }3257 iter := coll.Find(nil).Batch(2).Iter()3258 c.Assert(iter.Next(bson.M{}), Equals, true)3259 c.Assert(iter.Close(), IsNil)3260 c.Assert(serverCursorsOpen(session), Equals, cursors)3261}3262func (s *S) TestFindIterDoneWithBatches(c *C) {3263 session, err := mgo.Dial("localhost:40001")3264 c.Assert(err, IsNil)3265 defer session.Close()3266 coll := session.DB("mydb").C("mycoll")3267 ns := []int{40, 41, 42, 43, 44, 45, 46}3268 for _, n := range ns {3269 coll.Insert(M{"n": n})3270 }3271 iter := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2).Iter()3272 result := struct{ N int }{}3273 for i := 2; i < 7; i++ {3274 // first check will be with pending local record;3275 // second will be with open cursor ID but no local3276 // records3277 c.Assert(iter.Done(), Equals, false)3278 ok := iter.Next(&result)3279 c.Assert(ok, Equals, true, Commentf("err=%v", err))3280 }3281 c.Assert(iter.Done(), Equals, true)3282 ok := iter.Next(&result)3283 c.Assert(ok, Equals, false)3284 c.Assert(iter.Close(), IsNil)3285}3286func (s *S) TestFindIterDoneErr(c *C) {3287 session, err := mgo.Dial("localhost:40002")3288 c.Assert(err, IsNil)3289 defer session.Close()3290 coll := session.DB("mydb").C("mycoll")3291 iter := coll.Find(nil).Iter()3292 result := struct{}{}3293 ok := iter.Next(&result)3294 c.Assert(iter.Done(), Equals, true)3295 c.Assert(ok, Equals, false)3296 c.Assert(iter.Err(), ErrorMatches, "unauthorized.*|not authorized.*")3297}3298func (s *S) TestFindIterDoneNotFound(c *C) {3299 session, err := mgo.Dial("localhost:40001")3300 c.Assert(err, IsNil)3301 defer session.Close()3302 coll := session.DB("mydb").C("mycoll")3303 result := struct{ A, B int }{}3304 iter := coll.Find(M{"a": 1}).Iter()3305 ok := iter.Next(&result)3306 c.Assert(ok, Equals, false)3307 c.Assert(iter.Done(), Equals, true)3308}3309func (s *S) TestLogReplay(c *C) {3310 session, err := mgo.Dial("localhost:40001")3311 c.Assert(err, IsNil)3312 defer session.Close()3313 coll := session.DB("mydb").C("mycoll")3314 for i := 0; i < 5; i++ {3315 err = coll.Insert(M{"ts": time.Now()})3316 c.Assert(err, IsNil)3317 }3318 iter := coll.Find(nil).LogReplay().Iter()3319 if s.versionAtLeast(2, 6) {3320 // This used to fail in 2.4. Now it's just a smoke test.3321 c.Assert(iter.Err(), IsNil)3322 } else {3323 c.Assert(iter.Next(bson.M{}), Equals, false)3324 c.Assert(iter.Err(), ErrorMatches, "no ts field in query")3325 }3326}3327func (s *S) TestSetCursorTimeout(c *C) {3328 session, err := mgo.Dial("localhost:40001")3329 c.Assert(err, IsNil)3330 defer session.Close()3331 coll := session.DB("mydb").C("mycoll")3332 err = coll.Insert(M{"n": 42})3333 // This is just a smoke test. Won't wait 10 minutes for an actual timeout.3334 session.SetCursorTimeout(0)3335 var result struct{ N int }3336 iter := coll.Find(nil).Iter()3337 c.Assert(iter.Next(&result), Equals, true)3338 c.Assert(result.N, Equals, 42)3339 c.Assert(iter.Next(&result), Equals, false)3340}3341func (s *S) TestNewIterNoServer(c *C) {3342 session, err := mgo.Dial("localhost:40001")3343 c.Assert(err, IsNil)3344 defer session.Close()3345 data, err := bson.Marshal(bson.M{"a": 1})3346 coll := session.DB("mydb").C("mycoll")3347 iter := coll.NewIter(nil, []bson.Raw{{3, data}}, 42, nil)3348 var result struct{ A int }3349 ok := iter.Next(&result)3350 c.Assert(ok, Equals, true)3351 c.Assert(result.A, Equals, 1)3352 ok = iter.Next(&result)3353 c.Assert(ok, Equals, false)3354 c.Assert(iter.Err(), ErrorMatches, "server not available")3355}3356func (s *S) TestNewIterNoServerPresetErr(c *C) {3357 session, err := mgo.Dial("localhost:40001")3358 c.Assert(err, IsNil)3359 defer session.Close()3360 data, err := bson.Marshal(bson.M{"a": 1})3361 coll := session.DB("mydb").C("mycoll")3362 iter := coll.NewIter(nil, []bson.Raw{{3, data}}, 42, fmt.Errorf("my error"))3363 var result struct{ A int }3364 ok := iter.Next(&result)3365 c.Assert(ok, Equals, true)3366 c.Assert(result.A, Equals, 1)3367 ok = iter.Next(&result)3368 c.Assert(ok, Equals, false)3369 c.Assert(iter.Err(), ErrorMatches, "my error")3370}3371func (s *S) TestBypassValidation(c *C) {3372 if !s.versionAtLeast(3, 2) {3373 c.Skip("validation supported on 3.2+")3374 }3375 session, err := mgo.Dial("localhost:40001")3376 c.Assert(err, IsNil)3377 defer session.Close()3378 coll := session.DB("mydb").C("mycoll")3379 err = coll.Insert(M{"n": 1})3380 c.Assert(err, IsNil)3381 err = coll.Database.Run(bson.D{3382 {"collMod", "mycoll"},3383 {"validator", M{"s": M{"$type": "string"}}},3384 }, nil)3385 c.Assert(err, IsNil)3386 err = coll.Insert(M{"n": 2})3387 c.Assert(err, ErrorMatches, "Document failed validation")3388 err = coll.Update(M{"n": 1}, M{"n": 10})3389 c.Assert(err, ErrorMatches, "Document failed validation")3390 session.SetBypassValidation(true)3391 err = coll.Insert(M{"n": 3})3392 c.Assert(err, IsNil)3393 err = coll.Update(M{"n": 3}, M{"n": 4})3394 c.Assert(err, IsNil)3395 // Ensure this still works. Shouldn't be affected.3396 err = coll.Remove(M{"n": 1})3397 c.Assert(err, IsNil)3398 var result struct{ N int }3399 var ns []int3400 iter := coll.Find(nil).Iter()3401 for iter.Next(&result) {3402 ns = append(ns, result.N)3403 }3404 c.Assert(iter.Err(), IsNil)3405 sort.Ints(ns)3406 c.Assert(ns, DeepEquals, []int{4})3407}3408func (s *S) TestVersionAtLeast(c *C) {3409 tests := [][][]int{3410 {{3, 2, 1}, {3, 2, 0}},3411 {{3, 2, 1}, {3, 2}},3412 {{3, 2, 1}, {2, 5, 5, 5}},3413 {{3, 2, 1}, {2, 5, 5}},3414 {{3, 2, 1}, {2, 5}},3415 }3416 for _, pair := range tests {3417 bi := mgo.BuildInfo{VersionArray: pair[0]}3418 c.Assert(bi.VersionAtLeast(pair[1]...), Equals, true)3419 bi = mgo.BuildInfo{VersionArray: pair[0]}3420 c.Assert(bi.VersionAtLeast(pair[0]...), Equals, true)3421 bi = mgo.BuildInfo{VersionArray: pair[1]}3422 c.Assert(bi.VersionAtLeast(pair[1]...), Equals, true)3423 bi = mgo.BuildInfo{VersionArray: pair[1]}3424 c.Assert(bi.VersionAtLeast(pair[0]...), Equals, false)3425 }3426}3427// --------------------------------------------------------------------------3428// Some benchmarks that require a running database.3429func (s *S) BenchmarkFindIterRaw(c *C) {3430 session, err := mgo.Dial("localhost:40001")3431 c.Assert(err, IsNil)3432 defer session.Close()3433 coll := session.DB("mydb").C("mycoll")3434 doc := bson.D{3435 {"f2", "a short string"},3436 {"f3", bson.D{{"1", "one"}, {"2", 2.0}}},3437 {"f4", []string{"a", "b", "c", "d", "e", "f", "g"}},3438 }3439 for i := 0; i < c.N+1; i++ {3440 err := coll.Insert(doc)3441 c.Assert(err, IsNil)3442 }3443 session.SetBatch(c.N)3444 var raw bson.Raw3445 iter := coll.Find(nil).Iter()3446 iter.Next(&raw)3447 c.ResetTimer()3448 i := 03449 for iter.Next(&raw) {3450 i++3451 }3452 c.StopTimer()3453 c.Assert(iter.Err(), IsNil)3454 c.Assert(i, Equals, c.N)3455}...

Full Screen

Full Screen

flusher.go

Source:flusher.go Github

copy

Full Screen

1package txn2import (3 "fmt"4 "gopkg.in/mgo.v2"5 "AutomatedRegression/gopkg.in/mgo.v2/bson"6)7func flush(r *Runner, t *transaction) error {8 f := &flusher{9 Runner: r,10 goal: t,11 goalKeys: make(map[docKey]bool),12 queue: make(map[docKey][]token),13 debugId: debugPrefix(),14 }15 for _, dkey := range f.goal.docKeys() {16 f.goalKeys[dkey] = true17 }18 return f.run()19}20type flusher struct {21 *Runner22 goal *transaction23 goalKeys map[docKey]bool24 queue map[docKey][]token25 debugId string26}27func (f *flusher) run() (err error) {28 if chaosEnabled {29 defer f.handleChaos(&err)30 }31 f.debugf("Processing %s", f.goal)32 seen := make(map[bson.ObjectId]*transaction)33 if err := f.recurse(f.goal, seen); err != nil {34 return err35 }36 if f.goal.done() {37 return nil38 }39 // Sparse workloads will generally be managed entirely by recurse.40 // Getting here means one or more transactions have dependencies41 // and perhaps cycles.42 // Build successors data for Tarjan's sort. Must consider43 // that entries in txn-queue are not necessarily valid.44 successors := make(map[bson.ObjectId][]bson.ObjectId)45 ready := true46 for _, dqueue := range f.queue {47 NextPair:48 for i := 0; i < len(dqueue); i++ {49 pred := dqueue[i]50 predid := pred.id()51 predt := seen[predid]52 if predt == nil || predt.Nonce != pred.nonce() {53 continue54 }55 predsuccids, ok := successors[predid]56 if !ok {57 successors[predid] = nil58 }59 for j := i + 1; j < len(dqueue); j++ {60 succ := dqueue[j]61 succid := succ.id()62 succt := seen[succid]63 if succt == nil || succt.Nonce != succ.nonce() {64 continue65 }66 if _, ok := successors[succid]; !ok {67 successors[succid] = nil68 }69 // Found a valid pred/succ pair.70 i = j - 171 for _, predsuccid := range predsuccids {72 if predsuccid == succid {73 continue NextPair74 }75 }76 successors[predid] = append(predsuccids, succid)77 if succid == f.goal.Id {78 // There are still pre-requisites to handle.79 ready = false80 }81 continue NextPair82 }83 }84 }85 f.debugf("Queues: %v", f.queue)86 f.debugf("Successors: %v", successors)87 if ready {88 f.debugf("Goal %s has no real pre-requisites", f.goal)89 return f.advance(f.goal, nil, true)90 }91 // Robert Tarjan's algorithm for detecting strongly-connected92 // components is used for topological sorting and detecting93 // cycles at once. The order in which transactions are applied94 // in commonly affected documents must be a global agreement.95 sorted := tarjanSort(successors)96 if debugEnabled {97 f.debugf("Tarjan output: %v", sorted)98 }99 pull := make(map[bson.ObjectId]*transaction)100 for i := len(sorted) - 1; i >= 0; i-- {101 scc := sorted[i]102 f.debugf("Flushing %v", scc)103 if len(scc) == 1 {104 pull[scc[0]] = seen[scc[0]]105 }106 for _, id := range scc {107 if err := f.advance(seen[id], pull, true); err != nil {108 return err109 }110 }111 if len(scc) > 1 {112 for _, id := range scc {113 pull[id] = seen[id]114 }115 }116 }117 return nil118}119func (f *flusher) recurse(t *transaction, seen map[bson.ObjectId]*transaction) error {120 seen[t.Id] = t121 err := f.advance(t, nil, false)122 if err != errPreReqs {123 return err124 }125 for _, dkey := range t.docKeys() {126 for _, dtt := range f.queue[dkey] {127 id := dtt.id()128 if seen[id] != nil {129 continue130 }131 qt, err := f.load(id)132 if err != nil {133 return err134 }135 err = f.recurse(qt, seen)136 if err != nil {137 return err138 }139 }140 }141 return nil142}143func (f *flusher) advance(t *transaction, pull map[bson.ObjectId]*transaction, force bool) error {144 for {145 switch t.State {146 case tpreparing, tprepared:147 revnos, err := f.prepare(t, force)148 if err != nil {149 return err150 }151 if t.State != tprepared {152 continue153 }154 if err = f.assert(t, revnos, pull); err != nil {155 return err156 }157 if t.State != tprepared {158 continue159 }160 if err = f.checkpoint(t, revnos); err != nil {161 return err162 }163 case tapplying:164 return f.apply(t, pull)165 case taborting:166 return f.abortOrReload(t, nil, pull)167 case tapplied, taborted:168 return nil169 default:170 panic(fmt.Errorf("transaction in unknown state: %q", t.State))171 }172 }173 panic("unreachable")174}175type stash string176const (177 stashStable stash = ""178 stashInsert stash = "insert"179 stashRemove stash = "remove"180)181type txnInfo struct {182 Queue []token `bson:"txn-queue"`183 Revno int64 `bson:"txn-revno,omitempty"`184 Insert bson.ObjectId `bson:"txn-insert,omitempty"`185 Remove bson.ObjectId `bson:"txn-remove,omitempty"`186}187type stashState string188const (189 stashNew stashState = ""190 stashInserting stashState = "inserting"191)192var txnFields = bson.D{{"txn-queue", 1}, {"txn-revno", 1}, {"txn-remove", 1}, {"txn-insert", 1}}193var errPreReqs = fmt.Errorf("transaction has pre-requisites and force is false")194// prepare injects t's id onto txn-queue for all affected documents195// and collects the current txn-queue and txn-revno values during196// the process. If the prepared txn-queue indicates that there are197// pre-requisite transactions to be applied and the force parameter198// is false, errPreReqs will be returned. Otherwise, the current199// tip revision numbers for all the documents are returned.200func (f *flusher) prepare(t *transaction, force bool) (revnos []int64, err error) {201 if t.State != tpreparing {202 return f.rescan(t, force)203 }204 f.debugf("Preparing %s", t)205 // dkeys being sorted means stable iteration across all runners. This206 // isn't strictly required, but reduces the chances of cycles.207 dkeys := t.docKeys()208 revno := make(map[docKey]int64)209 info := txnInfo{}210 tt := tokenFor(t)211NextDoc:212 for _, dkey := range dkeys {213 change := mgo.Change{214 Update: bson.D{{"$addToSet", bson.D{{"txn-queue", tt}}}},215 ReturnNew: true,216 }217 c := f.tc.Database.C(dkey.C)218 cquery := c.FindId(dkey.Id).Select(txnFields)219 RetryDoc:220 change.Upsert = false221 chaos("")222 if _, err := cquery.Apply(change, &info); err == nil {223 if info.Remove == "" {224 // Fast path, unless workload is insert/remove heavy.225 revno[dkey] = info.Revno226 f.queue[dkey] = info.Queue227 f.debugf("[A] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue)228 continue NextDoc229 } else {230 // Handle remove in progress before preparing it.231 if err := f.loadAndApply(info.Remove); err != nil {232 return nil, err233 }234 goto RetryDoc235 }236 } else if err != mgo.ErrNotFound {237 return nil, err238 }239 // Document missing. Use stash collection.240 change.Upsert = true241 chaos("")242 _, err := f.sc.FindId(dkey).Apply(change, &info)243 if err != nil {244 return nil, err245 }246 if info.Insert != "" {247 // Handle insert in progress before preparing it.248 if err := f.loadAndApply(info.Insert); err != nil {249 return nil, err250 }251 goto RetryDoc252 }253 // Must confirm stash is still in use and is the same one254 // prepared, since applying a remove overwrites the stash.255 docFound := false256 stashFound := false257 if err = c.FindId(dkey.Id).Select(txnFields).One(&info); err == nil {258 docFound = true259 } else if err != mgo.ErrNotFound {260 return nil, err261 } else if err = f.sc.FindId(dkey).One(&info); err == nil {262 stashFound = true263 if info.Revno == 0 {264 // Missing revno in the stash only happens when it265 // has been upserted, in which case it defaults to -1.266 // Txn-inserted documents get revno -1 while in the stash267 // for the first time, and -revno-1 == 2 when they go live.268 info.Revno = -1269 }270 } else if err != mgo.ErrNotFound {271 return nil, err272 }273 if docFound && info.Remove == "" || stashFound && info.Insert == "" {274 for _, dtt := range info.Queue {275 if dtt != tt {276 continue277 }278 // Found tt properly prepared.279 if stashFound {280 f.debugf("[B] Prepared document %v on stash with revno %d and queue: %v", dkey, info.Revno, info.Queue)281 } else {282 f.debugf("[B] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue)283 }284 revno[dkey] = info.Revno285 f.queue[dkey] = info.Queue286 continue NextDoc287 }288 }289 // The stash wasn't valid and tt got overwritten. Try again.290 f.unstashToken(tt, dkey)291 goto RetryDoc292 }293 // Save the prepared nonce onto t.294 nonce := tt.nonce()295 qdoc := bson.D{{"_id", t.Id}, {"s", tpreparing}}296 udoc := bson.D{{"$set", bson.D{{"s", tprepared}, {"n", nonce}}}}297 chaos("set-prepared")298 err = f.tc.Update(qdoc, udoc)299 if err == nil {300 t.State = tprepared301 t.Nonce = nonce302 } else if err == mgo.ErrNotFound {303 f.debugf("Can't save nonce of %s: LOST RACE", tt)304 if err := f.reload(t); err != nil {305 return nil, err306 } else if t.State == tpreparing {307 panic("can't save nonce yet transaction is still preparing")308 } else if t.State != tprepared {309 return t.Revnos, nil310 }311 tt = t.token()312 } else if err != nil {313 return nil, err314 }315 prereqs, found := f.hasPreReqs(tt, dkeys)316 if !found {317 // Must only happen when reloading above.318 return f.rescan(t, force)319 } else if prereqs && !force {320 f.debugf("Prepared queue with %s [has prereqs & not forced].", tt)321 return nil, errPreReqs322 }323 revnos = assembledRevnos(t.Ops, revno)324 if !prereqs {325 f.debugf("Prepared queue with %s [no prereqs]. Revnos: %v", tt, revnos)326 } else {327 f.debugf("Prepared queue with %s [forced] Revnos: %v", tt, revnos)328 }329 return revnos, nil330}331func (f *flusher) unstashToken(tt token, dkey docKey) error {332 qdoc := bson.D{{"_id", dkey}, {"txn-queue", tt}}333 udoc := bson.D{{"$pull", bson.D{{"txn-queue", tt}}}}334 chaos("")335 if err := f.sc.Update(qdoc, udoc); err == nil {336 chaos("")337 err = f.sc.Remove(bson.D{{"_id", dkey}, {"txn-queue", bson.D{}}})338 } else if err != mgo.ErrNotFound {339 return err340 }341 return nil342}343func (f *flusher) rescan(t *transaction, force bool) (revnos []int64, err error) {344 f.debugf("Rescanning %s", t)345 if t.State != tprepared {346 panic(fmt.Errorf("rescanning transaction in invalid state: %q", t.State))347 }348 // dkeys being sorted means stable iteration across all349 // runners. This isn't strictly required, but reduces the chances350 // of cycles.351 dkeys := t.docKeys()352 tt := t.token()353 if !force {354 prereqs, found := f.hasPreReqs(tt, dkeys)355 if found && prereqs {356 // Its state is already known.357 return nil, errPreReqs358 }359 }360 revno := make(map[docKey]int64)361 info := txnInfo{}362 for _, dkey := range dkeys {363 const retries = 3364 retry := -1365 RetryDoc:366 retry++367 c := f.tc.Database.C(dkey.C)368 if err := c.FindId(dkey.Id).Select(txnFields).One(&info); err == mgo.ErrNotFound {369 // Document is missing. Look in stash.370 chaos("")371 if err := f.sc.FindId(dkey).One(&info); err == mgo.ErrNotFound {372 // Stash also doesn't exist. Maybe someone applied it.373 if err := f.reload(t); err != nil {374 return nil, err375 } else if t.State != tprepared {376 return t.Revnos, err377 }378 // Not applying either.379 if retry < retries {380 // Retry since there might be an insert/remove race.381 goto RetryDoc382 }383 // Neither the doc nor the stash seem to exist.384 return nil, fmt.Errorf("cannot find document %v for applying transaction %s", dkey, t)385 } else if err != nil {386 return nil, err387 }388 // Stash found.389 if info.Insert != "" {390 // Handle insert in progress before assuming ordering is good.391 if err := f.loadAndApply(info.Insert); err != nil {392 return nil, err393 }394 goto RetryDoc395 }396 if info.Revno == 0 {397 // Missing revno in the stash means -1.398 info.Revno = -1399 }400 } else if err != nil {401 return nil, err402 } else if info.Remove != "" {403 // Handle remove in progress before assuming ordering is good.404 if err := f.loadAndApply(info.Remove); err != nil {405 return nil, err406 }407 goto RetryDoc408 }409 revno[dkey] = info.Revno410 found := false411 for _, id := range info.Queue {412 if id == tt {413 found = true414 break415 }416 }417 f.queue[dkey] = info.Queue418 if !found {419 // Rescanned transaction id was not in the queue. This could mean one420 // of three things:421 // 1) The transaction was applied and popped by someone else. This is422 // the common case.423 // 2) We've read an out-of-date queue from the stash. This can happen424 // when someone else was paused for a long while preparing another425 // transaction for this document, and improperly upserted to the426 // stash when unpaused (after someone else inserted the document).427 // This is rare but possible.428 // 3) There's an actual bug somewhere, or outside interference. Worst429 // possible case.430 f.debugf("Rescanned document %v misses %s in queue: %v", dkey, tt, info.Queue)431 err := f.reload(t)432 if t.State == tpreparing || t.State == tprepared {433 if retry < retries {434 // Case 2.435 goto RetryDoc436 }437 // Case 3.438 return nil, fmt.Errorf("cannot find transaction %s in queue for document %v", t, dkey)439 }440 // Case 1.441 return t.Revnos, err442 }443 }444 prereqs, found := f.hasPreReqs(tt, dkeys)445 if !found {446 panic("rescanning loop guarantees that this can't happen")447 } else if prereqs && !force {448 f.debugf("Rescanned queue with %s: has prereqs, not forced", tt)449 return nil, errPreReqs450 }451 revnos = assembledRevnos(t.Ops, revno)452 if !prereqs {453 f.debugf("Rescanned queue with %s: no prereqs, revnos: %v", tt, revnos)454 } else {455 f.debugf("Rescanned queue with %s: has prereqs, forced, revnos: %v", tt, revnos)456 }457 return revnos, nil458}459func assembledRevnos(ops []Op, revno map[docKey]int64) []int64 {460 revnos := make([]int64, len(ops))461 for i, op := range ops {462 dkey := op.docKey()463 revnos[i] = revno[dkey]464 drevno := revno[dkey]465 switch {466 case op.Insert != nil && drevno < 0:467 revno[dkey] = -drevno + 1468 case op.Update != nil && drevno >= 0:469 revno[dkey] = drevno + 1470 case op.Remove && drevno >= 0:471 revno[dkey] = -drevno - 1472 }473 }474 return revnos475}476func (f *flusher) hasPreReqs(tt token, dkeys docKeys) (prereqs, found bool) {477 found = true478NextDoc:479 for _, dkey := range dkeys {480 for _, dtt := range f.queue[dkey] {481 if dtt == tt {482 continue NextDoc483 } else if dtt.id() != tt.id() {484 prereqs = true485 }486 }487 found = false488 }489 return490}491func (f *flusher) reload(t *transaction) error {492 var newt transaction493 query := f.tc.FindId(t.Id)494 query.Select(bson.D{{"s", 1}, {"n", 1}, {"r", 1}})495 if err := query.One(&newt); err != nil {496 return fmt.Errorf("failed to reload transaction: %v", err)497 }498 t.State = newt.State499 t.Nonce = newt.Nonce500 t.Revnos = newt.Revnos501 f.debugf("Reloaded %s: %q", t, t.State)502 return nil503}504func (f *flusher) loadAndApply(id bson.ObjectId) error {505 t, err := f.load(id)506 if err != nil {507 return err508 }509 return f.advance(t, nil, true)510}511// assert verifies that all assertions in t match the content that t512// will be applied upon. If an assertion fails, the transaction state513// is changed to aborted.514func (f *flusher) assert(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) error {515 f.debugf("Asserting %s with revnos %v", t, revnos)516 if t.State != tprepared {517 panic(fmt.Errorf("asserting transaction in invalid state: %q", t.State))518 }519 qdoc := make(bson.D, 3)520 revno := make(map[docKey]int64)521 for i, op := range t.Ops {522 dkey := op.docKey()523 if _, ok := revno[dkey]; !ok {524 revno[dkey] = revnos[i]525 }526 if op.Assert == nil {527 continue528 }529 if op.Assert == DocMissing {530 if revnos[i] >= 0 {531 return f.abortOrReload(t, revnos, pull)532 }533 continue534 }535 if op.Insert != nil {536 return fmt.Errorf("Insert can only Assert txn.DocMissing", op.Assert)537 }538 // if revnos[i] < 0 { abort }?539 qdoc = append(qdoc[:0], bson.DocElem{"_id", op.Id})540 if op.Assert != DocMissing {541 var revnoq interface{}542 if n := revno[dkey]; n == 0 {543 revnoq = bson.D{{"$exists", false}}544 } else {545 revnoq = n546 }547 // XXX Add tt to the query here, once we're sure it's all working.548 // Not having it increases the chances of breaking on bad logic.549 qdoc = append(qdoc, bson.DocElem{"txn-revno", revnoq})550 if op.Assert != DocExists {551 qdoc = append(qdoc, bson.DocElem{"$or", []interface{}{op.Assert}})552 }553 }554 c := f.tc.Database.C(op.C)555 if err := c.Find(qdoc).Select(bson.D{{"_id", 1}}).One(nil); err == mgo.ErrNotFound {556 // Assertion failed or someone else started applying.557 return f.abortOrReload(t, revnos, pull)558 } else if err != nil {559 return err560 }561 }562 f.debugf("Asserting %s succeeded", t)563 return nil564}565func (f *flusher) abortOrReload(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) (err error) {566 f.debugf("Aborting or reloading %s (was %q)", t, t.State)567 if t.State == tprepared {568 qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}}569 udoc := bson.D{{"$set", bson.D{{"s", taborting}}}}570 chaos("set-aborting")571 if err = f.tc.Update(qdoc, udoc); err == nil {572 t.State = taborting573 } else if err == mgo.ErrNotFound {574 if err = f.reload(t); err != nil || t.State != taborting {575 f.debugf("Won't abort %s. Reloaded state: %q", t, t.State)576 return err577 }578 } else {579 return err580 }581 } else if t.State != taborting {582 panic(fmt.Errorf("aborting transaction in invalid state: %q", t.State))583 }584 if len(revnos) > 0 {585 if pull == nil {586 pull = map[bson.ObjectId]*transaction{t.Id: t}587 }588 seen := make(map[docKey]bool)589 for i, op := range t.Ops {590 dkey := op.docKey()591 if seen[op.docKey()] {592 continue593 }594 seen[dkey] = true595 pullAll := tokensToPull(f.queue[dkey], pull, "")596 if len(pullAll) == 0 {597 continue598 }599 udoc := bson.D{{"$pullAll", bson.D{{"txn-queue", pullAll}}}}600 chaos("")601 if revnos[i] < 0 {602 err = f.sc.UpdateId(dkey, udoc)603 } else {604 c := f.tc.Database.C(dkey.C)605 err = c.UpdateId(dkey.Id, udoc)606 }607 if err != nil && err != mgo.ErrNotFound {608 return err609 }610 }611 }612 udoc := bson.D{{"$set", bson.D{{"s", taborted}}}}613 chaos("set-aborted")614 if err := f.tc.UpdateId(t.Id, udoc); err != nil && err != mgo.ErrNotFound {615 return err616 }617 t.State = taborted618 f.debugf("Aborted %s", t)619 return nil620}621func (f *flusher) checkpoint(t *transaction, revnos []int64) error {622 var debugRevnos map[docKey][]int64623 if debugEnabled {624 debugRevnos = make(map[docKey][]int64)625 for i, op := range t.Ops {626 dkey := op.docKey()627 debugRevnos[dkey] = append(debugRevnos[dkey], revnos[i])628 }629 f.debugf("Ready to apply %s. Saving revnos %v", t, debugRevnos)630 }631 // Save in t the txn-revno values the transaction must run on.632 qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}}633 udoc := bson.D{{"$set", bson.D{{"s", tapplying}, {"r", revnos}}}}634 chaos("set-applying")635 err := f.tc.Update(qdoc, udoc)636 if err == nil {637 t.State = tapplying638 t.Revnos = revnos639 f.debugf("Ready to apply %s. Saving revnos %v: DONE", t, debugRevnos)640 } else if err == mgo.ErrNotFound {641 f.debugf("Ready to apply %s. Saving revnos %v: LOST RACE", t, debugRevnos)642 return f.reload(t)643 }644 return nil645}646func (f *flusher) apply(t *transaction, pull map[bson.ObjectId]*transaction) error {647 f.debugf("Applying transaction %s", t)648 if t.State != tapplying {649 panic(fmt.Errorf("applying transaction in invalid state: %q", t.State))650 }651 if pull == nil {652 pull = map[bson.ObjectId]*transaction{t.Id: t}653 }654 logRevnos := append([]int64(nil), t.Revnos...)655 logDoc := bson.D{{"_id", t.Id}}656 tt := tokenFor(t)657 for i := range t.Ops {658 op := &t.Ops[i]659 dkey := op.docKey()660 dqueue := f.queue[dkey]661 revno := t.Revnos[i]662 var opName string663 if debugEnabled {664 opName = op.name()665 f.debugf("Applying %s op %d (%s) on %v with txn-revno %d", t, i, opName, dkey, revno)666 }667 c := f.tc.Database.C(op.C)668 qdoc := bson.D{{"_id", dkey.Id}, {"txn-revno", revno}, {"txn-queue", tt}}669 if op.Insert != nil {670 qdoc[0].Value = dkey671 if revno == -1 {672 qdoc[1].Value = bson.D{{"$exists", false}}673 }674 } else if revno == 0 {675 // There's no document with revno 0. The only way to see it is676 // when an existent document participates in a transaction the677 // first time. Txn-inserted documents get revno -1 while in the678 // stash for the first time, and -revno-1 == 2 when they go live.679 qdoc[1].Value = bson.D{{"$exists", false}}680 }681 pullAll := tokensToPull(dqueue, pull, tt)682 var d bson.D683 var outcome string684 var err error685 switch {686 case op.Update != nil:687 if revno < 0 {688 err = mgo.ErrNotFound689 f.debugf("Won't try to apply update op; negative revision means the document is missing or stashed")690 } else {691 newRevno := revno + 1692 logRevnos[i] = newRevno693 if d, err = objToDoc(op.Update); err != nil {694 return err695 }696 if d, err = addToDoc(d, "$pullAll", bson.D{{"txn-queue", pullAll}}); err != nil {697 return err698 }699 if d, err = addToDoc(d, "$set", bson.D{{"txn-revno", newRevno}}); err != nil {700 return err701 }702 chaos("")703 err = c.Update(qdoc, d)704 }705 case op.Remove:706 if revno < 0 {707 err = mgo.ErrNotFound708 } else {709 newRevno := -revno - 1710 logRevnos[i] = newRevno711 nonce := newNonce()712 stash := txnInfo{}713 change := mgo.Change{714 Update: bson.D{{"$push", bson.D{{"n", nonce}}}},715 Upsert: true,716 ReturnNew: true,717 }718 if _, err = f.sc.FindId(dkey).Apply(change, &stash); err != nil {719 return err720 }721 change = mgo.Change{722 Update: bson.D{{"$set", bson.D{{"txn-remove", t.Id}}}},723 ReturnNew: true,724 }725 var info txnInfo726 if _, err = c.Find(qdoc).Apply(change, &info); err == nil {727 // The document still exists so the stash previously728 // observed was either out of date or necessarily729 // contained the token being applied.730 f.debugf("Marked document %v to be removed on revno %d with queue: %v", dkey, info.Revno, info.Queue)731 updated := false732 if !hasToken(stash.Queue, tt) {733 var set, unset bson.D734 if revno == 0 {735 // Missing revno in stash means -1.736 set = bson.D{{"txn-queue", info.Queue}}737 unset = bson.D{{"n", 1}, {"txn-revno", 1}}738 } else {739 set = bson.D{{"txn-queue", info.Queue}, {"txn-revno", newRevno}}740 unset = bson.D{{"n", 1}}741 }742 qdoc := bson.D{{"_id", dkey}, {"n", nonce}}743 udoc := bson.D{{"$set", set}, {"$unset", unset}}744 if err = f.sc.Update(qdoc, udoc); err == nil {745 updated = true746 } else if err != mgo.ErrNotFound {747 return err748 }749 }750 if updated {751 f.debugf("Updated stash for document %v with revno %d and queue: %v", dkey, newRevno, info.Queue)752 } else {753 f.debugf("Stash for document %v was up-to-date", dkey)754 }755 err = c.Remove(qdoc)756 }757 }758 case op.Insert != nil:759 if revno >= 0 {760 err = mgo.ErrNotFound761 } else {762 newRevno := -revno + 1763 logRevnos[i] = newRevno764 if d, err = objToDoc(op.Insert); err != nil {765 return err766 }767 change := mgo.Change{768 Update: bson.D{{"$set", bson.D{{"txn-insert", t.Id}}}},769 ReturnNew: true,770 }771 chaos("")772 var info txnInfo773 if _, err = f.sc.Find(qdoc).Apply(change, &info); err == nil {774 f.debugf("Stash for document %v has revno %d and queue: %v", dkey, info.Revno, info.Queue)775 d = setInDoc(d, bson.D{{"_id", op.Id}, {"txn-revno", newRevno}, {"txn-queue", info.Queue}})776 // Unlikely yet unfortunate race in here if this gets seriously777 // delayed. If someone inserts+removes meanwhile, this will778 // reinsert, and there's no way to avoid that while keeping the779 // collection clean or compromising sharding. applyOps can solve780 // the former, but it can't shard (SERVER-1439).781 chaos("insert")782 err = c.Insert(d)783 if err == nil || mgo.IsDup(err) {784 if err == nil {785 f.debugf("New document %v inserted with revno %d and queue: %v", dkey, info.Revno, info.Queue)786 } else {787 f.debugf("Document %v already existed", dkey)788 }789 chaos("")790 if err = f.sc.Remove(qdoc); err == nil {791 f.debugf("Stash for document %v removed", dkey)792 }793 }794 }795 }796 case op.Assert != nil:797 // Pure assertion. No changes to apply.798 }799 if err == nil {800 outcome = "DONE"801 } else if err == mgo.ErrNotFound || mgo.IsDup(err) {802 outcome = "MISS"803 err = nil804 } else {805 outcome = err.Error()806 }807 if debugEnabled {808 f.debugf("Applying %s op %d (%s) on %v with txn-revno %d: %s", t, i, opName, dkey, revno, outcome)809 }810 if err != nil {811 return err812 }813 if f.lc != nil && op.isChange() {814 // Add change to the log document.815 var dr bson.D816 for li := range logDoc {817 elem := &logDoc[li]818 if elem.Name == op.C {819 dr = elem.Value.(bson.D)820 break821 }822 }823 if dr == nil {824 logDoc = append(logDoc, bson.DocElem{op.C, bson.D{{"d", []interface{}{}}, {"r", []int64{}}}})825 dr = logDoc[len(logDoc)-1].Value.(bson.D)826 }827 dr[0].Value = append(dr[0].Value.([]interface{}), op.Id)828 dr[1].Value = append(dr[1].Value.([]int64), logRevnos[i])829 }830 }831 t.State = tapplied832 if f.lc != nil {833 // Insert log document into the changelog collection.834 f.debugf("Inserting %s into change log", t)835 err := f.lc.Insert(logDoc)836 if err != nil && !mgo.IsDup(err) {837 return err838 }839 }840 // It's been applied, so errors are ignored here. It's fine for someone841 // else to win the race and mark it as applied, and it's also fine for842 // it to remain pending until a later point when someone will perceive843 // it has been applied and mark it at such.844 f.debugf("Marking %s as applied", t)845 chaos("set-applied")846 f.tc.Update(bson.D{{"_id", t.Id}, {"s", tapplying}}, bson.D{{"$set", bson.D{{"s", tapplied}}}})847 return nil848}849func tokensToPull(dqueue []token, pull map[bson.ObjectId]*transaction, dontPull token) []token {850 var result []token851 for j := len(dqueue) - 1; j >= 0; j-- {852 dtt := dqueue[j]853 if dtt == dontPull {854 continue855 }856 if _, ok := pull[dtt.id()]; ok {857 // It was handled before and this is a leftover invalid858 // nonce in the queue. Cherry-pick it out.859 result = append(result, dtt)860 }861 }862 return result863}864func objToDoc(obj interface{}) (d bson.D, err error) {865 data, err := bson.Marshal(obj)866 if err != nil {867 return nil, err868 }869 err = bson.Unmarshal(data, &d)870 if err != nil {871 return nil, err872 }873 return d, err874}875func addToDoc(doc bson.D, key string, add bson.D) (bson.D, error) {876 for i := range doc {877 elem := &doc[i]878 if elem.Name != key {879 continue880 }881 if old, ok := elem.Value.(bson.D); ok {882 elem.Value = append(old, add...)883 return doc, nil884 } else {885 return nil, fmt.Errorf("invalid %q value in change document: %#v", key, elem.Value)886 }887 }888 return append(doc, bson.DocElem{key, add}), nil889}890func setInDoc(doc bson.D, set bson.D) bson.D {891 dlen := len(doc)892NextS:893 for s := range set {894 sname := set[s].Name895 for d := 0; d < dlen; d++ {896 if doc[d].Name == sname {897 doc[d].Value = set[s].Value898 continue NextS899 }900 }901 doc = append(doc, set[s])902 }903 return doc904}905func hasToken(tokens []token, tt token) bool {906 for _, ttt := range tokens {907 if ttt == tt {908 return true909 }910 }911 return false912}913func (f *flusher) debugf(format string, args ...interface{}) {914 if !debugEnabled {915 return916 }917 debugf(f.debugId+format, args...)918}...

Full Screen

Full Screen

regression.go

Source:regression.go Github

copy

Full Screen

...134 t.CID = cid135 var err error136 if r.EnableDeDup {137 // check if already exists138 dup, err := r.isDup(ctx, &t)139 if err != nil {140 r.log.Error("failed to run deduplication on the testcase", zap.String("cid", cid), zap.String("appID", t.AppID), zap.Error(err))141 return "", errors.New("internal failure")142 }143 if dup {144 r.log.Info("found duplicate testcase", zap.String("cid", cid), zap.String("appID", t.AppID), zap.String("uri", t.URI))145 return "", nil146 }147 }148 err = r.tdb.Upsert(ctx, t)149 if err != nil {150 r.log.Error("failed to insert testcase into DB", zap.String("cid", cid), zap.String("appID", t.AppID), zap.Error(err))151 return "", errors.New("internal failure")152 }153 return t.ID, nil154}155func (r *Regression) Put(ctx context.Context, cid string, tcs []models.TestCase) ([]string, error) {156 var ids []string157 if len(tcs) == 0 {158 return ids, errors.New("no testcase to update")159 }160 for _, t := range tcs {161 id, err := r.putTC(ctx, cid, t)162 if err != nil {163 msg := "failed saving testcase"164 r.log.Error(msg, zap.Error(err), zap.String("cid", cid), zap.String("id", t.ID), zap.String("app", t.AppID))165 return ids, errors.New(msg)166 }167 ids = append(ids, id)168 }169 return ids, nil170}171func (r *Regression) test(ctx context.Context, cid, id, app string, resp models.HttpResp) (bool, *run.Result, *models.TestCase, error) {172 tc, err := r.tdb.Get(ctx, cid, id)173 if err != nil {174 r.log.Error("failed to get testcase from DB", zap.String("id", id), zap.String("cid", cid), zap.String("appID", app), zap.Error(err))175 return false, nil, nil, err176 }177 bodyType := run.BodyTypePlain178 if json.Valid([]byte(resp.Body)) {179 bodyType = run.BodyTypeJSON180 }181 pass := true182 hRes := &[]run.HeaderResult{}183 res := &run.Result{184 StatusCode: run.IntResult{185 Normal: false,186 Expected: tc.HttpResp.StatusCode,187 Actual: resp.StatusCode,188 },189 BodyResult: run.BodyResult{190 Normal: false,191 Type: bodyType,192 Expected: tc.HttpResp.Body,193 Actual: resp.Body,194 },195 }196 var noise []string197 for _, n := range tc.Noise {198 a := strings.Split(n, ".")199 if len(a) > 1 && a[0] == "body" {200 x := strings.Join(a[1:], ".")201 noise = append(noise, x)202 }203 }204 if bodyType == run.BodyTypeJSON {205 pass, err = pkg.Match(tc.HttpResp.Body, resp.Body, noise, r.log)206 if err != nil {207 return false, res, &tc, err208 }209 } else {210 if !pkg.Contains(tc.Noise, "body") && tc.HttpResp.Body != resp.Body {211 pass = false212 }213 }214 res.BodyResult.Normal = pass215 if !pkg.CompareHeaders(tc.HttpResp.Header, resp.Header, hRes) {216 pass = false217 }218 res.HeadersResult = *hRes219 if tc.HttpResp.StatusCode == resp.StatusCode {220 res.StatusCode.Normal = true221 } else {222 pass = false223 }224 return pass, res, &tc, nil225}226func (r *Regression) Test(ctx context.Context, cid, app, runID, id string, resp models.HttpResp) (bool, error) {227 var t *run.Test228 started := time.Now().UTC()229 ok, res, tc, err := r.test(ctx, cid, id, app, resp)230 if tc != nil {231 t = &run.Test{232 ID: uuid.New().String(),233 Started: started.Unix(),234 RunID: runID,235 TestCaseID: id,236 URI: tc.URI,237 Req: tc.HttpReq,238 Dep: tc.Deps,239 Resp: resp,240 Result: *res,241 Noise: tc.Noise,242 }243 }244 t.Completed = time.Now().UTC().Unix()245 defer func() {246 err2 := r.saveResult(ctx, t)247 if err2 != nil {248 r.log.Error("failed test result to db", zap.Error(err2), zap.String("cid", cid), zap.String("app", app))249 }250 }()251 if err != nil {252 r.log.Error("failed to run the testcase", zap.Error(err), zap.String("cid", cid), zap.String("app", app))253 t.Status = run.TestStatusFailed254 }255 if ok {256 t.Status = run.TestStatusPassed257 return ok, nil258 }259 t.Status = run.TestStatusFailed260 return false, nil261}262func (r *Regression) saveResult(ctx context.Context, t *run.Test) error {263 err := r.rdb.PutTest(ctx, *t)264 if err != nil {265 return err266 }267 if t.Status == run.TestStatusFailed {268 err = r.rdb.Increment(ctx, false, true, t.RunID)269 } else {270 err = r.rdb.Increment(ctx, true, false, t.RunID)271 }272 if err != nil {273 return err274 }275 return nil276}277func (r *Regression) DeNoise(ctx context.Context, cid, id, app, body string, h http.Header) error {278 tc, err := r.tdb.Get(ctx, cid, id)279 if err != nil {280 r.log.Error("failed to get testcase from DB", zap.String("id", id), zap.String("cid", cid), zap.String("appID", app), zap.Error(err))281 return err282 }283 a, b := map[string][]string{}, map[string][]string{}284 // add headers285 for k, v := range tc.HttpResp.Header {286 a["header."+k] = []string{strings.Join(v, "")}287 }288 for k, v := range h {289 b["header."+k] = []string{strings.Join(v, "")}290 }291 err = addBody(tc.HttpResp.Body, a)292 if err != nil {293 r.log.Error("failed to parse response body", zap.String("id", id), zap.String("cid", cid), zap.String("appID", app), zap.Error(err))294 return err295 }296 err = addBody(body, b)297 if err != nil {298 r.log.Error("failed to parse response body", zap.String("id", id), zap.String("cid", cid), zap.String("appID", app), zap.Error(err))299 return err300 }301 // r.log.Debug("denoise between",zap.Any("stored object",a),zap.Any("coming object",b))302 var noise []string303 for k, v := range a {304 v2, ok := b[k]305 if !ok {306 noise = append(noise, k)307 continue308 }309 if !reflect.DeepEqual(v, v2) {310 noise = append(noise, k)311 }312 }313 // r.log.Debug("Noise Array : ",zap.Any("",noise))314 tc.Noise = noise315 err = r.tdb.Upsert(ctx, tc)316 if err != nil {317 r.log.Error("failed to update noise fields for testcase", zap.String("id", id), zap.String("cid", cid), zap.String("appID", app), zap.Error(err))318 return err319 }320 return nil321}322func addBody(body string, m map[string][]string) error {323 // add body324 if json.Valid([]byte(body)) {325 var result interface{}326 err := json.Unmarshal([]byte(body), &result)327 if err != nil {328 return err329 }330 j := flatten(result)331 for k, v := range j {332 nk := "body"333 if k != "" {334 nk = nk + "." + k335 }336 m[nk] = v337 }338 } else {339 // add it as raw text340 m["body"] = []string{body}341 }342 return nil343}344// Flatten takes a map and returns a new one where nested maps are replaced345// by dot-delimited keys.346// examples of valid jsons - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#examples347func flatten(j interface{}) map[string][]string {348 if j == nil {349 return map[string][]string{"": {""}}350 }351 o := make(map[string][]string)352 x := reflect.ValueOf(j)353 switch x.Kind() {354 case reflect.Map:355 m, ok := j.(map[string]interface{})356 if !ok {357 return map[string][]string{}358 }359 for k, v := range m {360 nm := flatten(v)361 for nk, nv := range nm {362 fk := k363 if nk != "" {364 fk = fk + "." + nk365 }366 o[fk] = nv367 }368 }369 case reflect.Bool:370 o[""] = []string{strconv.FormatBool(x.Bool())}371 case reflect.Float64:372 o[""] = []string{strconv.FormatFloat(x.Float(), 'E', -1, 64)}373 case reflect.String:374 o[""] = []string{x.String()}375 case reflect.Slice:376 child, ok := j.([]interface{})377 if !ok {378 return map[string][]string{}379 }380 for _, av := range child {381 nm := flatten(av)382 for nk, nv := range nm {383 if ov, exists := o[nk]; exists {384 o[nk] = append(ov, nv...)385 } else {386 o[nk] = nv387 }388 }389 }390 default:391 fmt.Println("found invalid value in json", j, x.Kind())392 }393 return o394}395func (r *Regression) fillCache(ctx context.Context, t *models.TestCase) (string, error) {396 index := fmt.Sprintf("%s-%s-%s", t.CID, t.AppID, t.URI)397 _, ok1 := r.noisyFields[index]398 _, ok2 := r.fieldCounts[index]399 if ok1 && ok2 {400 return index, nil401 }402 r.mu.Lock()403 defer r.mu.Unlock()404 // check again after the lock405 _, ok1 = r.noisyFields[index]406 _, ok2 = r.fieldCounts[index]407 if !ok1 || !ok2 {408 var anchors []map[string][]string409 fieldCounts, noisyFields := map[string]map[string]int{}, map[string]bool{}410 tcs, err := r.tdb.GetKeys(ctx, t.CID, t.AppID, t.URI)411 if err != nil {412 return "", err413 }414 for _, v := range tcs {415 //var appAnchors map[string][]string416 //for _, a := range v.Anchors {417 // appAnchors[a] = v.AllKeys[a]418 //}419 anchors = append(anchors, v.Anchors)420 for k, v1 := range v.AllKeys {421 if fieldCounts[k] == nil {422 fieldCounts[k] = map[string]int{}423 }424 for _, v2 := range v1 {425 fieldCounts[k][v2] = fieldCounts[k][v2] + 1426 }427 if !isAnchor(fieldCounts[k]) {428 noisyFields[k] = true429 }430 }431 }432 r.fieldCounts[index], r.noisyFields[index], r.anchors[index] = fieldCounts, noisyFields, anchors433 }434 return index, nil435}436func (r *Regression) isDup(ctx context.Context, t *models.TestCase) (bool, error) {437 reqKeys := map[string][]string{}438 filterKeys := map[string][]string{}439 index, err := r.fillCache(ctx, t)440 if err != nil {441 return false, err442 }443 // add headers444 for k, v := range t.HttpReq.Header {445 reqKeys["header."+k] = []string{strings.Join(v, "")}446 }447 // add url params448 for k, v := range t.HttpReq.URLParams {449 reqKeys["url_params."+k] = []string{v}450 }...

Full Screen

Full Screen

isDup

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Enter the number of data points")4 fmt.Scan(&n)5 fmt.Println("Enter the data points")6 scanner := bufio.NewScanner(os.Stdin)7 for scanner.Scan() {8 line := scanner.Text()9 if line == "" {10 }11 words := strings.Split(line, " ")12 a, _ := strconv.ParseFloat(words[0], 64)13 b, _ := strconv.ParseFloat(words[1], 64)14 x = append(x, a)15 y = append(y, b)16 }17 reg := regression.NewRegression(x, y)18 fmt.Println("Enter the value of x")19 fmt.Scan(&x1)20 y1 := reg.Predict(x1)21 fmt.Println("The predicted value of y is", y1)22}23import (24func main() {25 fmt.Println("Enter the number of data points")26 fmt.Scan(&n)27 fmt.Println("Enter the data points")28 scanner := bufio.NewScanner(os.Stdin)29 for scanner.Scan() {30 line := scanner.Text()31 if line == "" {32 }33 words := strings.Split(line, " ")34 a, _ := strconv.ParseFloat(words[0], 64)35 b, _ := strconv.ParseFloat(words[1], 64)36 x = append(x, a)37 y = append(y, b)38 }39 reg := regression.NewRegression(x, y)40 fmt.Println("Enter the value of x")41 fmt.Scan(&x1)42 y1 := reg.Predict(x1)43 fmt.Println("The predicted value of y is", y1)44}45import (46func main() {

Full Screen

Full Screen

isDup

Using AI Code Generation

copy

Full Screen

1import (2type regression struct {3}4func (r *regression) isDup(x float64, y float64) bool {5 for i := 0; i < len(r.x); i++ {6 if r.x[i] == x && r.y[i] == y {7 }8 }9}10func (r *regression) add(x float64, y float64) {11 if r.isDup(x, y) == false {12 r.x = append(r.x, x)13 r.y = append(r.y, y)14 }15}16func (r *regression) size() int {17 return len(r.x)18}19func (r *regression) sumX() float64 {20 for i := 0; i < len(r.x); i++ {21 }22}23func (r *regression) sumY() float64 {24 for i := 0; i < len(r.y); i++ {25 }26}27func (r *regression) sumXY() float64 {28 for i := 0; i < len(r.x); i++ {29 }30}31func (r *regression) sumXX() float64 {32 for i := 0; i < len(r.x); i++ {33 }34}35func (r *regression) meanX() float64 {36 return r.sumX() / float64(r.size())37}38func (r *regression) meanY() float64 {39 return r.sumY() / float64(r.size())40}41func (r *regression) slope() float64 {42 return (r.sumXY() - (r.sumX() * r.sumY() / float64(r.size()))) / (r.sumXX() - (r.sumX() * r.sumX() / float64(r.size())))43}44func (r *regression) intercept() float64 {

Full Screen

Full Screen

isDup

Using AI Code Generation

copy

Full Screen

1import java.io.*;2import java.util.*;3import java.lang.*;4import java.util.regex.*;5import java.util.stream.*;6import java.util.function.*;7import java.util.concurrent.*;8import java.util.concurrent.atomic.*;9import java.util.concurrent.locks.*;10import java.util.concurrent.atomic.AtomicInteger;11public class 1 {12 public static void main(String[] args) throws IOException {13 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));14 String line;15 int[] a = new int[3];16 int[] b = new int[3];17 int[] c = new int[3];18 int[] d = new int[3];19 int[] e = new int[3];20 int[] f = new int[3];21 int[] g = new int[3];22 int[] h = new int[3];23 int[] i = new int[3];24 int[] j = new int[3];25 int[] k = new int[3];26 int[] l = new int[3];27 int[] m = new int[3];28 int[] n = new int[3];29 int[] o = new int[3];30 int[] p = new int[3];31 int[] q = new int[3];32 int[] r = new int[3];33 int[] s = new int[3];34 int[] t = new int[3];35 int[] u = new int[3];36 int[] v = new int[3];37 int[] w = new int[3];38 int[] x = new int[3];39 int[] y = new int[3];40 int[] z = new int[3];41 int[] aa = new int[3];42 int[] bb = new int[3];43 int[] cc = new int[3];44 int[] dd = new int[3];45 int[] ee = new int[3];46 int[] ff = new int[3];47 int[] gg = new int[3];48 int[] hh = new int[3];49 int[] ii = new int[3];50 int[] jj = new int[3];51 int[] kk = new int[3];52 int[] ll = new int[3];53 int[] mm = new int[3];54 int[] nn = new int[3];

Full Screen

Full Screen

isDup

Using AI Code Generation

copy

Full Screen

1import java.io.*;2import java.util.*;3import java.util.regex.*;4public class DuplicateFinder {5 private static String path = "C:\\Users\\User\\Desktop\\1.csv";6 private static File file = new File(path);7 private static ArrayList<String> data = new ArrayList<String>();8 private static ArrayList<String> dataNoDup = new ArrayList<String>();9 private static ArrayList<String> dataDup = new ArrayList<String>();10 private static ArrayList<String> dataFinal = new ArrayList<String>();11 private static ArrayList<String> dataFinalNoDup = new ArrayList<String>();12 private static ArrayList<String> dataFinalDup = new ArrayList<String>();13 private static ArrayList<String> dataFinalDupNoDup = new ArrayList<String>();14 private static ArrayList<String> dataFinalDupDup = new ArrayList<String>();15 private static ArrayList<String> dataFinalNoDupNoDup = new ArrayList<String>();16 private static ArrayList<String> dataFinalNoDupDup = new ArrayList<String>();17 private static ArrayList<String> dataFinalDupNoDupNoDup = new ArrayList<String>();18 private static ArrayList<String> dataFinalDupNoDupDup = new ArrayList<String>();19 private static ArrayList<String> dataFinalDupDupNoDup = new ArrayList<String>();20 private static ArrayList<String> dataFinalDupDupDup = new ArrayList<String>();

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Keploy automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful