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