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