github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/collection.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/mgo.v2" 9 "gopkg.in/mgo.v2/bson" 10 11 "github.com/juju/juju/mongo" 12 ) 13 14 // getRawCollection returns the named mgo Collection. As no automatic 15 // model filtering is performed by the returned collection it 16 // should be rarely used. getCollection() should be used in almost all 17 // cases. 18 func (st *State) getRawCollection(name string) (*mgo.Collection, func()) { 19 collection, closer := st.database.GetCollection(name) 20 return collection.Writeable().Underlying(), closer 21 } 22 23 // modelStateCollection wraps a mongo.Collection, preserving the 24 // mongo.Collection interface and its Writeable behaviour. It will 25 // automatically modify query selectors and documents so that queries 26 // and inserts only interact with data for a single model (where 27 // possible). 28 type modelStateCollection struct { 29 mongo.WriteCollection 30 modelUUID string 31 } 32 33 // Writeable is part of the Collection interface. 34 func (c *modelStateCollection) Writeable() mongo.WriteCollection { 35 // Note that we can't delegate this to the embedded WriteCollection: 36 // that would return a writeable collection without any env-handling. 37 return c 38 } 39 40 // Count returns the number of documents in the collection that belong 41 // to the model that the modelStateCollection is filtering on. 42 func (c *modelStateCollection) Count() (int, error) { 43 return c.WriteCollection.Find(bson.D{{"model-uuid", c.modelUUID}}).Count() 44 } 45 46 // Find performs a query on the collection. The query must be given as 47 // either nil or a bson.D. 48 // 49 // An "model-uuid" condition will always be added to the query to ensure 50 // that only data for the model being filtered on is returned. 51 // 52 // If a simple "_id" field selector is included in the query 53 // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will 54 // be added on to the id. Note that more complex selectors using the 55 // "_id" field (e.g. using the $in operator) will not be modified. In 56 // these cases it is up to the caller to add model UUID 57 // prefixes when necessary. 58 func (c *modelStateCollection) Find(query interface{}) mongo.Query { 59 return c.WriteCollection.Find(c.mungeQuery(query)) 60 } 61 62 // FindId looks up a single document by _id. If the id is a string the 63 // relevant model UUID prefix will be added to it. Otherwise, the 64 // query will be handled as per Find(). 65 func (c *modelStateCollection) FindId(id interface{}) mongo.Query { 66 if sid, ok := id.(string); ok { 67 return c.WriteCollection.FindId(ensureModelUUID(c.modelUUID, sid)) 68 } 69 return c.Find(bson.D{{"_id", id}}) 70 } 71 72 // Insert adds one or more documents to a collection. If the document 73 // id is a string the model UUID prefix will be automatically 74 // added to it. The model-uuid field will also be automatically added if 75 // it is missing. An error will be returned if an model-uuid field is 76 // provided but is the wrong value. 77 func (c *modelStateCollection) Insert(docs ...interface{}) error { 78 var mungedDocs []interface{} 79 for _, doc := range docs { 80 mungedDoc, err := mungeDocForMultiEnv(doc, c.modelUUID, modelUUIDRequired) 81 if err != nil { 82 return errors.Trace(err) 83 } 84 mungedDocs = append(mungedDocs, mungedDoc) 85 } 86 return c.WriteCollection.Insert(mungedDocs...) 87 } 88 89 // Update finds a single document matching the provided query document and 90 // modifies it according to the update document. 91 // 92 // An "model-uuid" condition will always be added to the query to ensure 93 // that only data for the model being filtered on is returned. 94 // 95 // If a simple "_id" field selector is included in the query 96 // (e.g. "{{"_id", "foo"}}" the relevant model UUID prefix will 97 // be added on to the id. Note that more complex selectors using the 98 // "_id" field (e.g. using the $in operator) will not be modified. In 99 // these cases it is up to the caller to add model UUID 100 // prefixes when necessary. 101 func (c *modelStateCollection) Update(query interface{}, update interface{}) error { 102 return c.WriteCollection.Update(c.mungeQuery(query), update) 103 } 104 105 // UpdateId finds a single document by _id and modifies it according to the 106 // update document. The id must be a string or bson.ObjectId. The model 107 // UUID will be automatically prefixed on to the id if it's a string and the 108 // prefix isn't there already. 109 func (c *modelStateCollection) UpdateId(id interface{}, update interface{}) error { 110 if sid, ok := id.(string); ok { 111 return c.WriteCollection.UpdateId(ensureModelUUID(c.modelUUID, sid), update) 112 } 113 return c.WriteCollection.UpdateId(bson.D{{"_id", id}}, update) 114 } 115 116 // Remove deletes a single document using the query provided. The 117 // query will be handled as per Find(). 118 func (c *modelStateCollection) Remove(query interface{}) error { 119 return c.WriteCollection.Remove(c.mungeQuery(query)) 120 } 121 122 // RemoveId deletes a single document by id. If the id is a string the 123 // relevant model UUID prefix will be added on to it. Otherwise, the 124 // query will be handled as per Find(). 125 func (c *modelStateCollection) RemoveId(id interface{}) error { 126 if sid, ok := id.(string); ok { 127 return c.WriteCollection.RemoveId(ensureModelUUID(c.modelUUID, sid)) 128 } 129 return c.Remove(bson.D{{"_id", id}}) 130 } 131 132 // RemoveAll deletes all documents that match a query. The query will 133 // be handled as per Find(). 134 func (c *modelStateCollection) RemoveAll(query interface{}) (*mgo.ChangeInfo, error) { 135 return c.WriteCollection.RemoveAll(c.mungeQuery(query)) 136 } 137 138 func (c *modelStateCollection) mungeQuery(inq interface{}) bson.D { 139 outq, err := mungeDocForMultiEnv(inq, c.modelUUID, modelUUIDRequired|noModelUUIDInInput) 140 if err != nil { 141 panic(err) 142 } 143 return outq 144 }